目录
2 线性表
2.1 线性表的定义和基本操作
2.2 顺序表
2.2.1 顺序表的定义
- 注意区分顺序表与数组的下标
- 动态分配和静态分配都是分配连续空间
2.2.2 顺序表上基本操作的实现
a=[1,2,3,6,6,7]
# 插入-前插法
a.insert(0,6)
# 插入-后插法
a.append(5)
# 删除
a.pop(2)
# 查找
a.index(2)
-
插入
- 一般使用 前插法
- 将插入位置之后的元素向后平移一位
- 时间复杂度 O(n)
-
删除
- 将删除位置之后的元素依次往前平移一位
- 时间复杂度 O(n)
-
查找
2.3 链表
2.3.1 单链表的定义
- 每个元素:data 和 指针域
- 引入头节点,有两个优点:
- 第一个位置和其他位置操作的天涯
- 空表和非空表操作的统一(例如判断是否为空表)
2.3.2 单链表上基本操作的实现
class SingleNode():
def __init__(self, item):
self.item = item
self.next = None
class SingleLinkList():
def __init__(self):
# using headNode
self._headNode = SingleNode(None)
def is_empty(self):
return self._headNode.next == None
def length(self):
cur = self._headNode.next
i=0
while cur!=None:
i += 1
cur=cur.next
return i
def travel(self):
if self.is_empty():
return None
cur=self._headNode.next
while cur!=None:
print(cur.item)
cur=cur.next
def tail_insert(self,i,item):
# insert a node right after location(i)
new_node = SingleNode(item)
pre = self.GetElm(i)
new_node.next=pre.next
pre.next=new_node
def head_insert(self,i,item):
# insert a node right before location(i)
new_node=SingleNode(item)
pre=self.GetElm(i-1)
new_node.next=pre.next
pre.next=new_node
def GetElm(self,i):
# get element by index
if self.is_empty():
return None
if i<1:
return self._headNode
cur=self._headNode.next
while (cur!=None) & (i>1):
cur=cur.next
i-=1
if i>1:
# exceed the length of LinkList
return None
else:
return cur
def LocateElm(self,Elm):
# locate the index of given element
cur = self._headNode.next
i=1
while cur!=None:
if cur.item==Elm:
return i
cur=cur.next
i+=1
return None
def construct_by_headInsert(self,node_list):
# O(n)
length=len(node_list)
self._headNode.next=SingleNode(node_list[length-1])
for i in range(1,length):
new_node=SingleNode(node_list[length-1-i])
new_node.next=self._headNode.next
self._headNode.next=new_node
def construct_by_tailInsert(self,node_list):
# O(n)
cur=self._headNode
for item in node_list:
cur.next=SingleNode(item)
cur=cur.next
def remove_by_index(self,i):
pre=self.GetElm(i-1)
tmp=pre.next
pre.next=tmp.next
del tmp
def remove_by_item(self,item):
i=self.LocateElm(item)
self.remove_by_index(i)
def remove_by_address(self,node):
# O(1)
# step1 swap
afr=node.next
node.item=afr.item
node.next=afr.next
# step2 delete
del afr
L=SingleLinkList()
L.construct_by_headInsert([5,3,2,1,7])
L2=SingleLinkList()
L2.construct_by_tailInsert([5,3,2,1,7])
- 建立
- 头插法 O(n)
- 尾插法
- 头插法 O(n)
- 查找
- 按序号查找 & 按值查找
- 插入
- 前插法 O(n)
- 先查找
- 再修改(修改的顺序不能反)
- 后插法 O(1)
- 直接在给定位置插入元素即可(不需要查找)
- 可以使用 后插法 实现 前插法
- 先使用后插法插入元素,然后交换两元素位置
- 前插法 O(n)
- 删除
- 给定下标
- 给定节点地址
- 普通:先查找,再删除 O(n)
- 加速:先交换,后删除 O(1)
- 给定下标
- 求表长
- 加入头节点的另一个好处:空表和非空表操作的统一(例如判断是否为空表)
2.3.3 双链表
class DNode():
def __init__(self,item):
self.item=item
self.next=None
self.pre=None
class DoubleLineList():
def __init__(self):
self._headNode=DNode(None)
def is_empty(self):
return self._headNode.next==None
def length(self):
cur=self._headNode.next
i=0
while cur!=None:
i+=1
cur=cur.next
return i
def travel_forward(self):
if self.is_empty():
return None
cur = self._headNode.next
while cur != None:
print(cur.item)
cur = cur.next
def travel_backward(self):
if self.is_empty():
return None
cur = self._headNode.next
while cur.next != None:
cur = cur.next
while cur.pre!=None:
print(cur.item)
cur=cur.pre
def GetElm(self, i):
# get element by index
if self.is_empty():
return None
if i < 1:
return self._headNode
cur = self._headNode.next
while (cur != None) & (i > 1):
cur = cur.next
i -= 1
if i > 1:
# exceed the length of LinkList
return None
else:
return cur
def tail_insert(self,i,item):
# insert a node right after location(i)
# O(1)
pre=self.GetElm(i)
new_node=DNode(item)
if pre.next==None:
pre.next=new_node
new_node.pre=pre
else:
new_node.next=pre.next
new_node.pre=pre
pre.next.pre = new_node
pre.next=new_node
def remove_by_adress(self,node):
pre=node.pre
pre.next=node.next
node.next.pre=pre
def construct_by_tailInsert(self,node_list):
length=len(node_list)
cur = self._headNode
pre = self._headNode
for item in node_list:
new_node=DNode(item)
cur.next=new_node
new_node.pre=cur
cur=cur.next
L=DoubleLineList()
L.construct_by_tailInsert([1,5,2,6,9])
L.travel_backward()
L.travel_forward()
L.tail_insert(5,1)
- 定义
- 特点:引入了前驱指针(prior *)
- 注意:双链表没有尾节点,因此操作不是统一的
- 插入 O(1)
- 删除 O(1)
2.3.4 循环链表
循环单链表
class SingleNode():
def __init__(self, item):
self.item = item
self.next = None
class SinCycLinkList():
def __init__(self):
# using head pointer
self._phead=None
def is_empty(self):
return self._phead == None
def length(self):
cur = self._phead
i=1
while cur.next!=self._phead:
i += 1
cur=cur.next
return i
def travel(self):
if self.is_empty():
return None
cur=self._phead
while cur.next!=self._phead:
print(cur.item)
cur=cur.next
print(cur.item)
def tail_insert(self,i,item):
# insert a node right after location(i)
new_node = SingleNode(item)
if i==0:
last_node=self.GetElm(self.length())
new_node.next=self._phead
self._phead=new_node
last_node.next=self._phead
else:
pre = self.GetElm(i)
new_node.next=pre.next
pre.next=new_node
def head_insert(self,i,item):
# insert a node right before location(i)
new_node=SingleNode(item)
if i==1:
last_node = self.GetElm(self.length())
new_node.next = self._phead
self._phead = new_node
last_node.next = self._phead
else:
pre=self.GetElm(i-1)
new_node.next=pre.next
pre.next=new_node
def GetElm(self,i):
# get element by index
if self.is_empty():
return None
if i<1:
return None
cur=self._phead
while (cur.next!=self._phead) & (i>1):
cur=cur.next
i-=1
if i>1:
# exceed the length of LinkList
return None
else:
return cur
def LocateElm(self,Elm):
# locate the index of given element
cur = self._phead
i=1
while cur.next!=self._phead:
if cur.item==Elm:
return i
cur=cur.next
i+=1
if cur.item==Elm:
return i
return None
def construct_by_headInsert(self,node_list):
# O(n)
length=len(node_list)
last_node=SingleNode(node_list[length-1])
self._phead=last_node
for i in range(1,length):
new_node=SingleNode(node_list[length-1-i])
new_node.next=self._phead
self._phead=new_node
last_node.next=self._phead
def construct_by_tailInsert(self,node_list):
# O(n)
self._phead=SingleNode(node_list[0])
cur=self._phead
for i in range(1,len(node_list)):
cur.next=SingleNode(node_list[i])
cur=cur.next
cur.next=self._phead
def remove_by_address(self,node):
if node.next==self._phead:
# O(n)
pre=self.GetElm(self.length()-1)
pre.next=self._phead
del node
else:
# O(1)
# step1 swap
afr=node.next
node.item=afr.item
node.next=afr.next
# step2 delete
del afr
L=SinCycLinkList()
L.construct_by_tailInsert([5,3,2,1,7])
L2=SinCycLinkList()
L2.construct_by_headInsert([5,3,2,1,7])
循环双链表
2.3.5 静态链表
- 定义:
- 用数组实现链表结构
- next存的是下一个节点的下标
- -1表示是最后一个节点
- 静态链表不能动态扩展
- 适用于没有指针的语言
2.3.6 顺序表与链表的比较
- 存取方式:
- 逻辑结构和物理结构
- 基本操作 -- 时间复杂度
- 内存空间
- 怎么选择
- 三个常用操作,分别选择什么数据结构
- 最值 - 遍历 【时间 O(n)】
- 逆序 - 对换位置
顺序表【时间O(n)空间O(1)】 | 链表【时间O(n) 空间 O(1)】 |
![]() | ![]() |
- 归并
顺序表【时间O(n+m) 空间O(n+m)】 | 链表【时间O(n) 空间 O(1) 或 O(0)(不创建新链表)】 |
![]() | ![]() |