栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > Python

python 数据结构一 之 线性表

Python 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

python 数据结构一 之 线性表

python数据结构教程第一课
从这里将会正式开始讲解python的一些实用的数据结构,原理加上实例源码。

一、简介
二、线性表的抽象数据类型
三、顺序表的实现
四、链接表的实现
1.单链表
2.带尾指针的单链表
3.循环单链表
4.双链表
5.循环双链表
五、线性表的应用—Josephus问题
1.顺序表解法
2.循环单链表解法

##一、简介 ##
在程序里经常需要将一组数据元素作为整体管理和使用,需要创建这种元素组,用变量记录它们,一组数据中包含的元素个数可能发生变化,也可能会用元素在序列里的位置和顺序,表示实际应用中的某种有意义信息。线性表就是这样一组元素的抽象,其具体实现方式有两种,顺序表和链接表

二、线性表的抽象数据类型(ADT)

线性表的基本操作应当有创建空表、返回长度信息、插入、删除等操作,其基本的ADT如下:

ADT List:
   List(self)    #创建一个新表
   is_empty(self)#判断self是否是一个空表
   len(self)     #返回表长度
   prepend(self,elem)   #在表头插入元素
   append(self,elem)    #在表尾加入元素
   insert(self,elem,i)  #在表的位置i处插入元素
   del_first(self)      #删除第一个元素
   def_last(self)#删除最后一个元素
   del(self,i)   #删除第I个元素
   search(self,elem)    #查找元素在表中第一次出现的位置
   forall(self,op)      #对表元素的遍历操作,op操作
三、顺序表的实现##

python内部的tuple与list采用的就是顺序表结构,其不同点在于tuple是固定结构,一旦创建就无法进行改动,而list则支持变动操作,具有上述ADT所描述的全部操作,这里不再具体重写类代码,其主要使用命令如下

list1 = list([1,2,3,4,5])    #创建新表
list1.append(6)#在尾部添加新元素 6
k = len(list1) #返回表长度
list1.insert(k,7)     #在位置k插入7
list1.pop()    #返回并删除尾部元素
print(list1)   #输出表的全部元素
list2 = list1[2:]     #表的切片操作

顺序表的优势在于O(1)时间的定位元素访问,很多简单的操作效率也比较高,比较麻烦的地方在于,表中间位置元素的插入删除操作,由于元素在顺序表的存储区里连续排列,插入/删除操作可能要移动很多元素,代价很高

四、链接表的实现##

基于链接技术实现的线性表称为链接表或者链表,用链接关系显式表示元素之间的顺序关系,链接表结构的基本思想如下:
1.把表中的元素分别存储在一批独立的存储块(结点)里
2.保证从组成表结构中的任一个结点可找到与其相关的下一个结点
3.在前一结点里用链接的方式显式地记录与下一结点之间的关联

在python里链表的实现有诸多方式和变形,接下来将选取主要的结构进行源码讲解

1.单链表
单链表是最基本也是最常用的链表结构,以下描述了链表的各种方法,包括,插入、排序、删除、融合等

import copy

#单链表结点类
class LNode:     
    def __init__(self, elem,next_=None):
 self.elem = elem
 self.next = next_

#链表位置定位错误
class linkedListUnderflow(ValueError): 
    pass
 
#单链表类的具体实现
class LList:    
    def __init__(self): #初始化操作
 self._head = None
 self.num = 0    #num记录结点数
 
    def is_empty(self): #空表判定
 return self._head is None
    
    def len(self):      #返回表长
 return self.num
   
    #定位到链表的第loc个元素 
    def located(self,loc):     
 if (loc > self.num or loc < 1):
     raise linkedListUnderflow('in located')
 temp = self._head
 i = 1
 if loc == 1:
     return temp
 else:
     while i < loc:
  temp = temp.next
  i += 1
     return temp
 
     
    #在链表的第loc个位置添加元素elem
    def located_add(self,loc,elem):
 temp = self.located(loc)     
 node = LNode(elem)
 if loc == 1:
     node.next = self._head
     self._head = node
 else:
     node.next = temp.next
     temp.next = node
 self.num += 1
    
    #在链表的第loc个位置删除元素    
    def located_del(self,loc):
 temp = self.located(loc)
 if loc == 1:
     self._head = self._head.next
 else:
     temp.next = temp.next.next
 self.num -= 1
    
    #表头插入元素
    def prepend(self,elem):
 self._head = LNode(elem,self._head)
 self.num += 1
    
    #返回并删除表头元素    
    def pop(self):
 if self._head is None:
     raise linkedListUnderflow('in pop')
 e = self._head.elem
 self._head = self._head.next
 self.num -= 1
 return e
    
    #在表尾添加元素
    def append(self,elem):
 if self._head is None:
     self._head = LNode(elem)
     self.num += 1
     return
 p = self._head
 while p.next is not None:
     p = p.next
 p.next = LNode(elem)
 self.num += 1
    
    #返回并删除表尾元素    
    def pop_last(self):
 if self._head is None:
     raise linkedListUnderflow('in pop_last')
 p = self._head
 if p.next is None:
     e = p.elem
     self._head  = None
     self.num -= 1
     return e
 while p.next.next is not None:
     p = p.next
 e = p.next.elem
 p.next = None
 self.num -= 1
 return e
    
    #返回表中所有满足pred()操作的元素
    def filter(self,pred):
 p = self._head
 while p is not None:
     if pred(p.elem):
  yield p.elem     
     p = p.next
    
    #输出表中的全部元素 
    def printall(self):
 p = self._head
 while p is not None:
     print(p.elem,end='')
     if p.next is not None:
  print(', ',end='')
     p = p.next
 print('')
    
    #对表中的所有元素执行proc操作
    def for_each(self,proc):
 p = self._head
 while p is not None:
     proc(p.elem)
     p = p.next
    
    #使链表支持iterator操作 
    def elements(self):
 p = self._head
 while p is not None:
     yield p.elem
     p = p.next
    
    #链表倒置
    def rev(self):
 p = None
 while self._head is not  None:
     q = self._head
     self._head = q.next
     q.next = p
     p = q
 self._head = p
     
    #链表从小到大排序    
    def sort(self):
 if self._head is None:
     return
 crt = self._head.next
 while crt is not None:
     x = crt.elem
     p = self._head
     while p is not crt and p.elem <= x:
  p = p.next
     while p is not crt:
  y = p.elem
  p.elem = x
  x = y
  p = p.next
     crt.elem = x
     crt = crt.next
    
    #第二种排序算法
    def sort1(self):
  p = self._head
  if p is None or p.next is None:
      return
  rem = p.next
  p.next = None
  while rem is not None:
      p = self._head
      q = None
      while rem is not None and p.elem <= rem.elem:
   q = p
   p = p.next
      if q is None:
   self._head = rem
      else:
   q.next = rem
      q = rem
      rem = rem.next
      q.next = p
    
    #第三种排序算法
    def sort2(self):
 list1 = copy.deepcopy(self)
 if list1._head.next is None:
     return
 list1._head.next.next = None
     
 if list1._head.next.elem < list1._head.elem:
     a = list1._head
     list1._head = list1._head.next
     list1._head.next = a
     list1._head.next.next = None
 
 temp = self._head.next.next
 
 while temp is not None:
     p = list1._head
     q = list1._head.next
     if temp.elem < list1._head.elem:
  a = temp.next
  temp.next = list1._head
  list1._head = temp
  temp = a
  if temp is not None:
      print(temp.elem)
      list1.printall()
     elif temp.elem >= list1._head.elem:   
  while q is not None:
      if q.elem >= temp.elem:
   a = temp.next
   temp.next = q
   p.next = temp
   temp = a
   break
      elif q.elem < temp.elem:
   q = q.next
   p = p.next
  if q is None:
      p.next = temp
      a = temp.next
      temp.next = None
      temp = a  
 self._head = list1._head

  
    #链表深拷贝操作 
    def deep_copy(self):
 Co = copy.deepcopy(self)
 return Co
      
    #链表相等判断   
    def __eq__(self,List1):
 Co1 = self.deep_copy()
 Co2 = List1.deep_copy()
 Co1.sort()
 Co2.sort()
 temp1 = Co1._head
 temp2 = Co2._head
 while Co1.len() == Co2.len() and temp1 is not None and temp2 is not None and temp1.elem == temp2.elem:
     temp1 = temp1.next
     temp2 = temp2.next
 return temp1 is None and temp2 is None 
    
    #链表按字典序,< 运算函数
    def __lt__(self,other):
 temp1 = self._head
 temp2 = other._head
 while temp1 is not None and temp2 is not None:
     if temp1.elem < temp2.elem:
  return True
     elif temp1.elem > temp2.elem:
  return False
     else:
  temp1 = temp1.next
  temp2 = temp2.next
 if temp1 is None and temp2 is not None:
     return True
 else:
     return False
    
    #链表按字典序,=< 运算函数 
    def __le__(self,other):
 temp1 = self._head
 temp2 = other._head
 while temp1 is not None and temp2 is not None:
     if temp1.elem < temp2.elem:
  return True
     elif temp1.elem > temp2.elem:
  return False
     else:
  temp1 = temp1.next
  temp2 = temp2.next
 if temp1 is None:
     return True
 else:
     return False
    
    #链表按字典序 >= 运算函数    
    def __ge__(self,other):
 temp1 = self._head
 temp2 = other._head
 while temp1 is not None and temp2 is not None:
     if temp1.elem > temp2.elem:
  return True
     elif temp1.elem < temp2.elem:
  return False
     else:
  temp1 = temp1.next
  temp2 = temp2.next
 if temp2 is None:
     return True
 else:
     return False
    
    #链表按字典序,> 运算函数    
    def __gt__(self,other):
 temp1 = self._head
 temp2 = other._head
 while temp1 is not None and temp2 is not None:
     if temp1.elem > temp2.elem:
  return True
     elif temp1.elem < temp2.elem:
  return False
     else:
  temp1 = temp1.next
  temp2 = temp2.next
 if temp2 is None and temp1 is not None:
     return True
 else:
     return False
    
    #链表反向遍历,执行对每个元素执行op操作
    def rev_visit(self,op):
 temp = copy.deepcopy(self)
 temp.rev()
 head = temp._head
 while head is not None:
     op(head.elem)
     head = head.next
   
    #删除表中的elem
    def del_elem(self,elem):
 a = self._head
 b = self._head.next
 if a is None:
     return 
 if a.elem == elem:
     self._head = b
 while b is not None:
     if b.elem == elem:
  a.next = b.next
     a = a.next
     b = b.next
    
    #删除表中最小元素
    def del_minimal(self):
 temp = copy.deepcopy(self)
 temp.sort()
 elem = temp._head.elem
 self.del_elem(elem)
    
    #删除表中所有满足pred操作的元素    
    def del_if(self,pred):
 temp = self._head
 while temp is not None:
     if pred(temp.elem):
  self.del_elem(temp.elem)
     temp = temp.next
    
    #返回一个字典,字典记录了表中每个元素出现的次数
    def elem_num(self):
 temp = self._head
 adict = dict()
 while temp is not None:
     if temp.elem not in adict:
  adict[temp.elem] = 1
     else:
  adict[temp.elem] += 1
     temp = temp.next
 return adict
    
    #删除链表中出现的重复项,第一次不变
    def del_duplicate(self):
 temp1 = self._head
 temp2 = self._head.next
 adict = self.elem_num()
 
 if adict[temp1.elem] > 1:
     adict[temp1.elem] *= -1
 
 while temp2 is not None:
     if adict[temp2.elem] > 1:
  adict[temp2.elem] *= -1
  temp1 = temp1.next
     elif adict[temp2.elem] < 0:
  temp1.next = temp2.next
     else:
  temp1 = temp1.next
     temp2 = temp2.next
     print(adict)
    
    #两个链表的交叉融合为一个链表
    def interleaving(self,another):
 temp1 = self._head
 temp2 = another._head
 while temp1 is not None and temp2 is not None:
     p = temp1.next
     temp1.next = temp2
     q = temp2.next
     temp2.next = p
     temp1 = p
     temp2 = q
 if temp1 is None:
     p = self._head
     while p.next is not None:
  p = p.next
     p.next = temp1

以上描述了单链表的众多方法,单链表还存在很多别的形态,可以让很多操作变的简洁有效率

2.带尾结点的单链表
单链表对尾部结点的访问效率是十分低下的,需要遍历表中之前的全部结点,当单链表带上尾部指针时,这种操作就会变的有效率很多

#带尾结点的单链表,继承自单链表,支持其的全部属性和方法
class LList1(LList): 
    def __init__(self): #初始化,新添了—rear作为尾结点
 LList.__init__(self)
 self._rear = None
 
    #首部结点插入方法
    def prepend(self,elem):
 self._head = LNode(elem,self._head)
 if self._rear is None:
     self._rear = self._head
     
    #尾部结点方法重写
    def append(self,elem):
 if self._head is None:
     self._head = LNode(elem,self._head)
     self._rear = self._head
 else:
     self._rear.next = LNode(elem)
     self._rear = self._rear.next
    
    #返回并删除最后一个结点
    def pop_last(self):
 if self._head is None:
     raise linkedListUnderflow('in pop_last')
 p = self._head
 if p.next is None:
     e = p.elem
     self._head = None
     return e
 while p.next.next is not None:
     p = p.next
 e = p.next.elem
 p.next = None
 self._rear = p
 return e

3.循环单链表
使单链表的尾指针指向首结点,就构成了循环单链表,其与单链表的不同在于,其扫描循环结束的控制判断

class LCList:#循环单链表
    def __init__(self):
 self._rear = None
    
    #空链表判断
    def is_empty(self):
 return self._rear is None
    
    #前端插入
    def prepend(self,elem):
 p = LNode(elem)
 if self._rear is None:
     p.next = p
     self._rear = p
 else:
     p.next = self._rear.next
     self._rear.next = p
    
    #尾端插入   
    def append(self,elem):
 self.prepend(elem)
 self._rear = self._rear.next
    #尾端返回并删除    
    def pop(self):
 if self._rear is None:
     raise linkedListUnderflow('in pop of CLList')
 p = self._rear.next
 if self._rear is p:
     self._rear = None
 else: 
     self._rear.next = p.next
 return p.elem
    
    #输出所有结点内容
    def printall(self):
 if self.is_empty():
     return
 p = self._rear.next
 while True:
     print(p.elem,end = " ")
     if p is self._rear:
  break
     p = p.next
    
    #两个链表交叉融合为一个链表
    def interleaving(self,another):
 temp1 = self._rear.next
 temp2 = another._rear.next
 
 while temp1 is not self._rear and temp2 is not another._rear:
     a = temp2.next
     temp2.next = temp1.next
     temp1.next = temp2
     temp2 = a
     temp1 = temp1.next.next
 if temp1 is self._rear:
     while temp2 is not another._rear:
  self.append(temp2.elem)
  temp2 = temp2.next

4.双链表
在单链表中,除了首结点和尾结点外,每个元素不但指向它的下一个结点,还会指向它的上一个结点,双链表支持更简单的反向遍历操作,双链表需要双结点类支持

#双结点类
class DLNode(LNode):
    def __init__(self,elem,prev = None,next_ = None):
 LNode.__init__(self,elem,next_)
 self.prev = prev

#双链表继承自带首尾指针的单链表,不过需要重写添加和删除方法
class DLList(LList1):
    def __init__(self):#初始化
 LList1.__init__(self)
     
    #使用双向结点前端插入   
    def prepend(self,elem):
 p = DLNode(elem,None,self._head)
 if self._head is None:
     self._rear = p
 else:
     p.next.prev = p
 self._head = p
    
    #首端返回并删除
    def pop(self):
 if self._head is None:
     raise linkedListUnderflow('in pop of DLList')
 e = self._head.elem
 self._head = self._head.next
 if self._head is not None:
     self._head.prev = None
 return e
    
    #尾端返回并删除
    def pop_last(self):
 if self._head is None:
     raise linkedListUnderflow('in pop_last of DLList')
 e = self._rear.elem
 self._rear = self._rear.prev
 if self._rear is None:
     self._head = None
 else:
     self._rear.next = None
 return e

5.循环双链表
双链表的尾部首部互指,构成循环双链表

class DCLList():
    def __init__(self):   #双链表类
 self._head = None
 self.__num = 0
    
    #尾端插入   
    def append(self,elem):
 p = DLNode(elem,None,None)
 if self._head is None:
     p.next = p 
     p.prev = p
     self._head = p
 else:
     p.prev = self._head.prev
     p.next = self._head
     self._head.prev.next = p
     self._head.prev = p
 self.__num += 1
    
    #尾部返回并删除
    def pop(self):
 if self._head is None:
     raise linkedListUnderflow('in pop_last of DCLList')
 elem = self._head.prev.elem
 self._head.prev.prev.next = self._head
 self._head.prev = self._head.prev.prev
 self.__num -= 1
 return elem

    #返回长度
    def len(self):
 return self.__num
    
    #链表倒置
    def reverse(self):
 q = self._head
 p = self._head.prev
 n = 1
 while p is not q and n <= self.len()/2:
     t = p.elem
     p.elem = q.elem
     q.elem = t
     q = q.next
     p = p.prev
     n += 1
     
     #链表元素排序
    def sort(self):
 i = 0
 while i < self.len():
     j = 0
     p = self._head
     while j < self.len()-i-1:
  if p.elem > p.next.elem:
      t = p.elem
      p.elem = p.next.elem
      p.next.elem = t
  j += 1
  p = p.next
     self.printall()
     i += 1
    
    #链表倒置算法2
    def reverse1(self):
 li = DCLList()
 p = self._head.prev
 for i in  range(self.len()):
     li.append(p.elem)
     p = p.prev
     i += 1
 self._head = li._head
    
    #链表排序算法2    
    def sort1(self):
 
 i = 0
 while i < self.len()-1:
     j = 0
     p = self._head.next
     while j < self.len()-i-2:
  if p.elem > p.next.elem:
      a = p.prev
      b = p.next.next
      c = p.next
      a.next = c
      c.prev = a
      c.next = p
      p.prev = c
      p.next = b
      b.prev = p
  else:
      p = p.next
  j += 1
     i += 1
 i = 0 
 p = self._head.next
 elem = self._head.elem
 while i < self.len()-1:
     if p.elem <= elem and p.next.elem > elem:
  a = self._head
  b = self._head.prev
  c = self._head.next
  b.next = c
  c.prev = b
  a.next = p.next
  p.next.prev = a
  p.next = a
  a.prev = p
  self._head = c
  break
     i += 1
     p = p.next
 if i == self.len()-1:
     self._head = self._head.next
     
    #输出链表元素     
    def printall(self):
 p = self._head
 for i in range(self.len()):
     print(p.elem,end = ' ')
     p = p.next
 print()

以上介绍了链表的众多基本与高级操作,以及链表的各种形态变形,链表的优势在于,表元素之间的顺序由它们所在的结点之间的链接显式表示,因此表结点可以任意安排位置,灵活的调整结构。
同时,为了实现链接表,每个结点都增加了一个链接域,付出了额外的空间代价,链表的位置访问代价很高,需要一个个结点的遍历,使用链表最合理的方式是前端操作和顺序访问

五、线性表的应用—Josephus问题

这里举出一个经典的问题来描述链表的用法
Josephus问题:
假设有n个人围坐一圈,现要求从第k个人开始报数,报到第m个数的人退出。然后从下一个人开始继续报数并按同样的规则退出,直至所有人退出,要求按顺序输出各出列人的编号

方法1.我们可以用list实现算法:

def josephus_A(n,k,m):
    people = list(range(1,n+1))
    
    i = k - 1
    for num in range(n):
 count = 0
 while count < m:
     if people[i] > 0:
  count += 1
     if count == m:
  print(people[i],end = ' ')
  people[i] = 0
     i = (i+1) % n
 if num < n - 1:
     print(',',end = '')
 else:
     print('')
    return

方法2.如果我们该用循环单链表,会发现问题简单了很多:

class Josephus(LCList):   
    def turn(self,m):  
 for i in range(m):
     self._rear = self._rear.next

    def __init__(self,n,k,m):
 LCList.__init__(self)
 for i in range(n):
     self.append(i+1)
 self.turn(k-1)
 while not self.is_empty():
     self.turn(m-1)
     print(self.pop(),end = ('n' if self.is_empty() else ','))

假设有13个人,从第5个人开始报数,数为6,则两种算法的使用和结果为:
算法1:

josephus_A(13,5,6) 


算法2:

Josephus(13,5,6)

转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/220135.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号