python 数据结构一 之 线性表
2017-08-18 15:39
78 查看
python数据结构教程第一课
从这里将会正式开始讲解python的一些实用的数据结构,原理加上实例源码。
一、简介
二、线性表的抽象数据类型
三、顺序表的实现
四、链接表的实现
1.单链表
2.带尾指针的单链表
3.循环单链表
4.双链表
5.循环双链表
五、线性表的应用—Josephus问题
1.顺序表解法
2.循环单链表解法
顺序表的优势在于O(1)时间的定位元素访问,很多简单的操作效率也比较高,比较麻烦的地方在于,表中间位置元素的插入删除操作,由于元素在顺序表的存储区里连续排列,插入/删除操作可能要移动很多元素,代价很高
1.把表中的元素分别存储在一批独立的存储块(结点)里
2.保证从组成表结构中的任一个结点可找到与其相关的下一个结点
3.在前一结点里用链接的方式显式地记录与下一结点之间的关联
在python里链表的实现有诸多方式和变形,接下来将选取主要的结构进行源码讲解
1.单链表
单链表是最基本也是最常用的链表结构,以下描述了链表的各种方法,包括,插入、排序、删除、融合等
以上描述了单链表的众多方法,单链表还存在很多别的形态,可以让很多操作变的简洁有效率
2.带尾结点的单链表
单链表对尾部结点的访问效率是十分低下的,需要遍历表中之前的全部结点,当单链表带上尾部指针时,这种操作就会变的有效率很多
3.循环单链表
使单链表的尾指针指向首结点,就构成了循环单链表,其与单链表的不同在于,其扫描循环结束的控制判断
4.双链表
在单链表中,除了首结点和尾结点外,每个元素不但指向它的下一个结点,还会指向它的上一个结点,双链表支持更简单的反向遍历操作,双链表需要双结点类支持
5.循环双链表
双链表的尾部首部互指,构成循环双链表
以上介绍了链表的众多基本与高级操作,以及链表的各种形态变形,链表的优势在于,表元素之间的顺序由它们所在的结点之间的链接显式表示,因此表结点可以任意安排位置,灵活的调整结构。
同时,为了实现链接表,每个结点都增加了一个链接域,付出了额外的空间代价,链表的位置访问代价很高,需要一个个结点的遍历,使用链表最合理的方式是前端操作和顺序访问
Josephus问题:
假设有n个人围坐一圈,现要求从第k个人开始报数,报到第m个数的人退出。然后从下一个人开始继续报数并按同样的规则退出,直至所有人退出,要求按顺序输出各出列人的编号
方法1.我们可以用list实现算法:
方法2.如果我们该用循环单链表,会发现问题简单了很多:
假设有13个人,从第5个人开始报数,数为6,则两种算法的使用和结果为:
算法1:
算法2:
从这里将会正式开始讲解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 #返 16fab 回并删除表头元素 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)
相关文章推荐
- Python进阶强化训练之数据结构与算法进阶
- 数据结构(一)-----线性表
- Python 数据结构与算法 —— 从分治的角度看快速排序、归并排序
- 数据结构笔记---线性表
- 数据结构——线性表——知识点总结
- Python基础(四) — 数据结构
- Python 之数据结构
- 【Python】10、python内置数据结构之集合
- Python基础数据结构之大循环(for list,set,dict,tuple)
- Python-数据结构
- Python——基本数据结构
- 基础数据结构——顺序线性表
- 【数据结构与算法】刷题汇总 Python 版
- 数据结构基础--线性表操作
- 笨办法学 Python · 续 第三部分:数据结构
- 数据结构(2线性表)
- Python-----数据结构
- 2、数据结构——线性表
- Python实现常用的逻辑数据结构
- 数据结构之 线性表---单链表的应用(重复元素删除)