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)
共同学习,写下你的评论
评论加载中...
作者其他优质文章