线性表:同一种类型的有顺序的一组数据元素
线性表的形式:顺序表、链表
顺序表:表中元素按顺序放在一大块连续的内存中,元素中的顺序由存储顺序来表示
链表:表中元素放在一系列的结点中,通过连接构造。结点可以连续也可以不连续。
结点分为「数据域」和「指针域」。
数据域:保存着作为表元素的数据项;
指针域:保存同一个表里的下一个结点的标识。
头结点和头指针的区分
头结点 的设立是为了操作的统一和方便,是放在第一个元素的节点之前,它的数据域一般没有意义,并且它本身也不是链表必须要带的。那设立头节点的目的是什么呢?其实就是为了在某些时候可以更方便的对链表进行操作,有了头结点,我们在对第一个元素前插入或者删除结点的时候,它的操作与其它结点的操作就统一了。
头指针 顾名思义,是指向链表第一个结点的指针,如果有头结点的话,那么就是指向头结点的指针。它是链表的必备元素且无论链表是否为空,头指针都不能为空,因为在访问链表的时候你总得知道它在什么位置,这样才能通过它的指针域找到下一个结点的位置,也就是说知道了头指针,整个链表的元素我们都是可以访问的,所以它必须要存在,这也就是我们常说的标识,这也就是为什么我们一般用头指针来表示链表。
单链表
n 个结点链接成一个链表,因为这个链表中的每个结点中只包含一个指针域,所以又叫单链表。单链表的第一个结点的存储位置叫做「头指针」,最后一个结点的指针为「空」,一般用 “^” 表示。
不带头结点的单链表:
带头结点的单链表:
空链表:
为了方便后续的操作,我们一般会先定义一个简单的结点类:
class Node(object):
def __init__(self,data):
self.data = data
self.next = None
单链表的基本操作
首先我们先来创建一个链表类:
class LinkList(object):
def _init_(self):
self.head = Node(None)
#判断链表是否为空
def IsEmpty(self):
p=self.head #头指针
if p.next == None:
print("List is empty")
return True
return False
#打印链表
def PrintList(self):
if self.IsEmpty():
return False
p = self.head
while p:
print(p.data,end='')
p=p.next
1.创建单链表
创建单链表的过程其实就是一个动态生成链表的过程,说简单点就是从一个「空链表」开始,依次建立各个元素的结点,并把它们逐个插入链表,时间复杂度为 O(n):
def InitList(self,data)
self.head = Node(data[0])#头结点
p = self.head #头指针
for i in data[1:]:
node = Node(i)
p.next = node
p = p.next
2.计算单链表的长度
在使用链表的时候,经常需要求表的长度,为此我们可以创建一个球表长的函数,这个函数就是从左到右扫描,遍历表中的所有结点并完成计数,时间复杂度为 O(n):
def LengthList(self):
if self.IsEmpty():
return 0
p = self.head
cnt = 0
while p:
cnt += 1
p = p.next
return cnt
3.单链表的插入
假设我们要将结点 s 插入到 结点 p 的后面,只需要将结点 s 插入到结点 p 和 结点 p.next 之间即可.
单链表结点的插入根本不需要惊动其它结点,只需要让 s.next 和 p.next 的指针稍作改变即可。这里一定要切记,插入操作的顺序不能改变,至于为什么,你可以拿起纸笔手动的画一下,结果一下子就会出来(对于单链表的表头和表尾的特殊情况,操作是相同的)。
#单链表的插入(在第 s 个结点后面插入 data)
def InsertList(self,s,data):
if self.IsEmpty() or s < 0 or s > self.LengthList():
print("Insert failed!")
return
p = self.head
index = 1
while index < s:
p = p.next
index += 1
node = Node(data)
node.next = p.next
p.next = node
4.单链表删除
看完插入,我们现在再来看看单链表的删除。假设我们想要删除一个结点 q,其实就是将它的前继结点 p 的指针绕过 q,直接指向 q 的后继结点即可,具体操作如下图所示:
#单链表的删除(删除第 s 个结点)
def DeleteList(self, s):
if self.IsEmpty() or s < 0 or s > self.LengthList():
print("Delete failed! ")
return
p = self.head
index = 1
while index < s:
pre = p
index += 1
p = p.next
pre.next = p.next
p = None
由 p = None 可以看出,在 Python 中,只需要简单的将指针赋值为 None,就抛弃了链表原有的结点,Python 解释器的存储管理系统会自动回收不用的存储。
5.单链表的读取
在顺序结构中,我们想要获取任意一个元素的存储位置是很容易的,但是在单链表中,第 i 个元素到底在哪我们一开始没办法知道,只能傻傻的从头开始找,所以在对于单链表获取第 i 个元素的操作,算法上相对麻烦一些。
#单链表的读取(获取第 s 个结点的值)
def GetList(self, s):
if self.IsEmpty() or s < 0 or s > self.LengthList():
print("Read failed! ")
return
p = self.head
index = 1
while index < s:
index += 1
p = p.next
print("第 {} 个值为 {}".format(s, p.data))
从上面的代码我们可以很清楚的看出,单链表获取第 i 个元素就是从头开始找,知道第 i 个元素为止,所以我们可以很容易的估算出它的时间复杂度是 O(n)。任何事物都不是完美的,有好的地方就有坏的地方,元素的读取就是单链表美中不足的地方之一。