1.1.1 顺序表和链表都是线性表的一种。那么什么是线性表?
线性表:同类型数据元素有序的线性(一维) 结构。
它是一种数据结构:
①名字:线性表
②对象:n个元素构成的有序序列
③操作:查找,删除,插入,添加。
1.1.2 线性表的存储实现方式1----顺序表
1.1.2.1
在用python实现顺序表的各种操作之前,我们需要学习python已有的顺序表list,有助于我们写出自己的顺序表!
list的实现技术(有一体式与分离式两种)
- python中的标准list顺序结构已十分成熟,它的元素个数是可变的,没有指定的上限。(在加入元素的时候,通过每次扩容1.125倍,来实现末端加入元素的复杂度为 O(1)
- 基于引索的高效访问(O(1))
- 允许加入任意多的元素,并且保持表对象的id不变(就是始终能找到这个表的位置,后面我们会知道,这是得益于表对象标识与元素内容分离式技术)
基于以上,list采用顺序+分离式技术实现。
list的操作使用
- len() 得到表的长度,复杂度是O(1),因为它是由表对象标识记录
- pop(i)删除操作只有末端为O(1),其余为O(n)
- b = a.clear() 表示清除表a 结果为b。O(1)
- a = [1,2,3,4,5] 引索是0--4 末尾引索也可以是-1 用[ 起始 : 结束 : 步长 ]来切片
- 复合项 b = [1,2,a,4] b[2][1]可得到a里面的元素 2
- 允许 list1+list2
1.1.2.2 自己动手实现一个顺序表
class Ordlist():
def __init__(self,capacity,num):
self._cap = capacity # 表容量
self.num = num # 元素数量
self.list = [None]*self._cap # 初始化顺序表
def print_list(self):
if self.is_empty():
mylist=[]
else:
mylist = self.list[0:self.num]
print(mylist)
def is_empty(self):
return self.num == 0
def is_full(self):
return self._cap == self.num
def append_elem(self,elem):
if not self.is_full():
self.list[self.num] = elem
self.num += 1
else:
return "list is full"
def prepend_elem(self,elem):
if not self.is_full():
self.list[1:] = self.list[0:-1]
self.list[0] = elem
self.num += 1
else:
return "list is full"
def insert(self,index,elem):
if not self.is_full():
list_end = self.list[index:]
self.list[index] =elem
self.list = self.list[0:index+1]+list_end
self.num += 1
else:
return "list is full"
def del_end(self):
if not self.is_empty():
self.list[self.num-1] = None
self.num -= 1
else:
print("list is empty")
def del_index(self,i):
if not self.is_empty():
self.list=self.list[0:i]+self.list[i+1:]+[None]
self.num -= 1
else:
print("list is empty")
# https://blog.csdn.net/Tommy1295/article/details/82315768
'''一个表的操作:
1.是否空
2.是否满
3.后端添加
4.前端添加
5.中间添加
6.后端删除
7.前端删除
8.任意位置删除
9.遍历
10.查找某元素第一次出现的位置'''
1.1.3 线性表的存储实现方式2----链表
1.1.3.1
线性表的另外一种实现方式:表现在物理层面的散乱性,每个元素带有下一个元素的地址,对内存见缝插针。
单链表实现技术
- 最普通的链表形式:每个元素都是一个带有元素值和下一个节点位置信息的节点。找到里首节点,就能找到链表中的任何元素。所以,我们需要一个变量保存首节点的位置,这个变量称之为表头变量。
单链表的操作使用
- 创建空链表:把表头变量设置为空链接(None) O(1)
- 删除链表:将表头变量设置为空(None),python自动管理内存O(1)
- 判断表是否为空:表头变量是否为None, 另外注意:链表不会满O(1)
- 首端插入:新增节点> 把原来首节点的位置给新增节点的 next位置存放 > 修改表头变量指向新增节点
- 一般插入:找到pre的节点 >把pre节点的next位置的值给新节点的next的位置 >把新增节点的位置信息给pre的next的位置
- 删除首元素:修改表头变量使其指向第二个节点位置
- 删除一般元素:找到pre节点 > 修改pre节点的next值使其指向下下个节点
- 定位,遍历元素:p = head
按下标:
while p is not None and i>0:
i -= 1
p = p.next(p变量找到了指定的第i个元素的位置)
按元素值查找:
while p is not None and p != value:
p = p.next
当然,如果存在值相同的多个元素,那么就只能找到第一个。
1.1.3.2 自己动手实现一个链表
#实现的关键是 节点(元素、引索)的构建
# 内部逻辑和属性的严谨关联
# 引用类型可修改原类型
class Note:
def __init__(self,elem,next_=None): # 节点单元
self.next = next_ # 引索
self.elem = elem # 元素
class Linklist:
def __init__(self): # 创建空链表
self.head = None
self.length = 0
def delete(self): # 删除链表
self.head = None
def traversal(self):
p = self.head
elems=[]
while p != None:
elems.append(p.elem)
p=p.next
print(elems)
def prepend(self,elem): # 首部添加元素
note = Note(elem)
note.next=self.head
self.head= note
self.length += 1
def insert(self,elem,i): # 插入元素
note = Note(elem)
if 0<i<=self.length:
p=self.head
p0=p
while i>0:
i -= 1
p0=p
p=p.next
p0.next=note
note.next=p
self.length += 1
elif i==0:
self.prepend(elem)
else:
print("超出索引")
def append(self,elem): # 末端加入元素
self.insert(elem,self.lenght)
def delete(self,i): # 删除第i个元素
if 0<i<self.length:
p=self.head
p0=p
while i>0:
i -= 1
p0=p # i 前端元素
p=p.next # i 本身
p0.next=p.next
self.length -= 1
elif i == 0 and self.length>0:
self.head=self.head.next
self.length -= 1
else:
print("索引出错")
我认为,实现链表的关键是要清楚:
- 链表的最小单元是节点,所以要先定义一个节点类Lnote。
- 然后再是理解head 指代的是一个表头节点,并且,next的指向也是一个节点类型,这个类型里面有元素和下一个元素的地址(其实就是下个元素)
- 任何遍历都要从 p =head 开始,用这个节点是否为空作为循环条件 ,更值得一提的是配合i >0 的循环条件,i -= 1操作,来定位第i个元素
几点改进:
def list_elements(self): # 就是一个生成器了,生成器会自动提供遍历机制(相当一个列表了)比较规范 作用:可以给其他操作使用
p = self._head
while p is not None:
yield p.elem
p = p.next_
def print_me(self):
for i in self.list_elements():
print(i)
def find_it(self,n): # 找到第一个值,第二个为n的元素?利用生成器阿
for i ,j in enumerate(self.list_elements()): # 生成器可以看成 既定列表 牛逼
if n == j :
print(i)
1.1.4 练习题
1.1.4.1 单链表的简单变形一(表头加一个尾部节点的地址,闭合这个单链表)
实现代码:
from Llist import *
""" 因为增加了尾节点信息,只是对部分操作的改变"""
# 这个程序改进了尾部操作的效率
class endlist(Linklist):
def __init__(self):
super().__init__()
self._rear = None # 注意尾部信息和头部信息一样是全局变量
def prepend(self,elem): # 前端加入操作
note = Note(elem)
if self.head is None:
self.head = note
self._rear = self.head
else:
self.head=Note(elem,self.head)
def append(self,elem):
note=Note(elem)
if self.head is not None:
self._rear.next = note
self._rear = note
else:
self.head = note
self._rear = self.head
self.length += 1
1.1.4.2 单链表的简单变形二(循环单链表)
实现代码:
#循环单链表(记录尾部的)
class Note:
def __init__(self,elem,next_=None):
self.next =next_
self.elem=elem
class looplist:
def __init__(self):
self._rear = None # 这是尾部循环单链表唯一的全局变量
def prepend(self,elem): # 参照图来写逻辑
if self._rear ==None:
self._rear = Note(elem)
self._rear.next=self._rear
else:
note=Note(elem,self._rear.next)
self._rear.next=note
def traversal(self):
p=self._rear
if p is None:
print([])
else:
p0=p
p=p.next
while p is not self._rear:
print(p.elem)
p=p.next
print(p0.elem)
1.1.4.3 单链表的变形三(双链表)
实现代码:
class Note:
def __init__(self,elem,prev=None,next_=None):
self.elem=elem
self.next=next_
self.prev=prev
class Dlist:
def __init__(self):
self.head=None
self._rear=None
def prepend(self,elem):#首端加入
if self.head==None:
self.head=Note(elem)
self._rear=self.head
else:
note=Note(elem,None,self.head)
self.head.prev=note
self.head=note
def traversal(self):
p=self.head
while p is not None:
print(p.elem)
p=p.next
1.1.4.4 单链表的简单变形四(循环双链表)
代码实现:
class Note:
def __init__(self,elem,prev=None,next_=None):
self.elem=elem
self.next=next_
self.prev=prev
class Dlist:
def __init__(self):
self.head=None
self._rear=None
self.length=0
def prepend(self,elem):#首端加入
if self.head==None:
self.head=Note(elem)
self._rear=self.head
self.head.prev=self.head
self.head.next = self.head
else:
note=Note(elem,None,self.head)
self.head.prev=note
self.head=note
self.head.prev=self._rear
self._rear.next=self.head
self.length +=1
def append(self,elem):# 测试后端加入
if self.head==None:
self.prepend(elem)
else:
note=Note(elem,self._rear,self.head)
self._rear.next=note
self._rear=note
self.length +=1
def pop(self,index): #测试弹出功能
if self.length ==1:
ans=self.head.elem
self.head=None
self._rear=None
elif self.length==2:
if index==0:
ans=self.head.elem
self.head=self._rear
self._rear.prev=self._rear
self._rear.next=self._rear
elif index==1:
ans=self._rear.elem
self._rear=self.head
self.head.prev=self.head
self.head.next=self.head
else:
print("超出引索1")
self.length +=1
elif 0<=index<self.length:
if index==0:
ans=self.head.elem
self.head.next.prev=self._rear
self.head=self.head.next
self._rear.next=self.head
elif index==self.length-1:
ans=self._rear.elem
self._rear=self._rear.prev
self._rear.next=self.head
self.head.prev=self._rear
else:
p=self.head
while index:
index -=1
p=p.next
ans=p.elem
p.prev.next=p.next
p.next.prev=p.prev
else:
self.length +=1
return print("超出引索2")
self.length -=1
#print("长度",self.length)
return ans
def traversal(self):
p=self.head
while p is not self._rear:
print(p.elem)
p=p.next
print(p.elem)