单链表
Posted 在这里, 意淫和实干都值得尊重
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了单链表相关的知识,希望对你有一定的参考价值。
写给自己看的笔记, 很多坑
1. 创建一个节点类
class Node(object):
'''节点类'''
def __init__(self, elem):
self.elem = elem
self.next = None
类内部的 __init__函数,是初始化函数,只要是类的实例对象,就会自动执行__init__函数内部的代码块,也就是说,类的实例对象都会有__init__函数的属性
ex:
__node = Node()__
__node__就是Node类的__实例__对象
该node具有__elem__和__next__属性
[========]
2. 创建一个链表类
class SingleLinkList():
def __init__(self, node=None):
self.__head = node
1. 定义实例的首节点属性,该属性为实例的私有属性
self__head = node
表示self的head属性是self私有的
2. 链表可以定义为空, 但是空链表内必须存在首节点,node=None,默认的首节点指向None,但以传递进来的首节点node为准
def __init__(self, node=None):
slef.__head=node
node在函数的参数元祖内, 如果不传递node,默认的node的值为None
ex1:
linklist = SingleLinkList()
- 创建一个SingleLinkList类的实例对象
- 该对象没有给SingleLinkList类传递node节点
- 所以该实例的首节点self.__head = None
ex2:
linklist = SingleLinkList(110)
- 创建一个SingleLinkList类的实例对象
- 该实例对象的首节点self.__head = 110
[========]
3.给SingleLinkList类,添加内置方法
1. 所有的内置方法
def __init__(self, node=None):
"""SingleLinkList类初始化,私有的,首节点方法"""
self.__head = node
def is_empty(self):
"""链表是否为空"""
def length(self):
"""链表的长度"""
def tarvel(self):
"""遍历整个链表"""
def add(self, item):
"""链表头部添加元素, 首插法"""
def append(self, item):
"""链表尾部添加元素, 尾插法"""
def insert(self, pos, item):
"""指定位置插入元素"""
def remove(self, item):
"""删除节点"""
def search(self, item):
"""查找节点是否存在"""
[========]
4. 创建实例对象,及调用内部方法
ex:
linklist= SingleLinkList()
创建一个实例对象
linklist.is_empty()
调用该实例的is_empty方法
[========]
5. 详解该链表的每一个方法
1. is_empty()
def is_empty(self):
return self.__head == None
__作用:__判断该链表是否为空
步骤详解:
self.__head
是该链表的首节点,- 如果
self._head==None
,就表示该链表首节点为None,即该链表没有节点 - return返回的是布尔值,如果为空返回True,否则返回False
[========]
2. length()
def length(self):
cur = self.__head
count = 0
while cur != None:
count += 1
cur = cur.next
return count
__作用:__查看该链表的长度
步骤详解:
cur = self.__head
创建游标,起始是指向链表的首节点
count = 0
创建count,起始值为0,进行数数
while cur != None:
遍历所有元素
1.判断条件:如果游标(cur)指向的不是None,就一直循环.
2.最后一个节点的next域指向的就是None
3.当cur == None的时候,就是已经遍历完所有的节点
count += 1
如果游标cur所指向的节点不是None,则执行count加一操作
cur = cur.next
将游标cur进行后移一位
return count
返回count的数值
注意点:
1. 如果该链表为空怎么办?
- 当链表为空的时候,游标cur的指向None;
cur = self.__head
count=0
已经设置好了;- 链表为空,跳过循环体;
while cur != None:
return count
返回count的值0
2. cur == None 与 cur.next == None的判断条件的问题
cur == None
表示当前游标指向的节点为Nonecur.next == None
表示当前游标指向的节点的后继节点为None- __后继结点:__表示当前节点的后一个节点
[========]
3. tarvel()
def tarvel(self):
cur = self.__head
while cur != None:
print(cur.elem, end=" ")
cur = cur.next
print("")
__作用:__遍历所有节点[遍历整个链表]
步骤详解:
cur = self.__head
创建游标,游标的起始指向是链表的首节点while cur != None:
遍历所有节点,如果游标cur指向的不是None,执行此循环print(cur.elem, end=" ")
在循环体内,如果cur所指向节点不是None,则打印当前节点的elem的值‘‘‘cur = cur.next
将游标进行后移一位print("")
1.当连续调用此方法的时候,终端输出的内容为连续
2.如果设置print(""),每次打印完毕都会换行
注意点:
1. 如果该链表为空怎么办?
- 当链表为空的时候,游标cur的指向None;
cur = self.__head
- 链表为空,跳过循环体;
while cur != None:
- 打印
print("")
2. cur == None 与 cur.next == None的判断条件的问题
cur == None
表示当前游标指向的节点为Nonecur.next == None
表示当前游标指向的节点的后继节点为None- 如果判断条件为
cur.next == None
,则会丢掉最后一个元素 - __后继结点:__表示当前节点的后一个节点
[========]
4. add():
def add(self, item):
node = Node(item)
node.next = self.__head
self.__head = node
__作用:__链表头部添加元素, 首插
步骤详解:
node = Node(item)
创建一个Node(item)的新节点,要插入到链表的首部, 也就是作为当前链表的首节点- 假设链表本身是有很多节点的
如果直接将链表的self.__head
首节点指向新节点,那么就会丢失原有的所有节点 - 先将新节点的next域指向原先的首节点
node.next = self.__head
- 然后再将
self.__head
指向现在的新节点self.__head = node
注意点:
- 当链表为空的时候, 默认的首节点是指向None的
slef.__head = None
- 以上三行代码完全可以应对链表为空的情况
[========]
5. append()
def append(self, item):
node = Node(item)
if self.is_empty():
self.__head = node
else:
cur = self.__head
while cur.next != None:
cur = cur.next
cur.next = node
__作用:__链接尾部添加节点,尾追加
步骤详解:
node = Node(item)
创建一个新节点,该节点的elem为itemif self.is_empty():
判断该链表是否为空
1.self.__head = node
将节点node作为链表的首节点cur = self.__head
利用首节点来创建游标while cur.next != None:
cur = cur.next
如果游标指向的节点的next域不指向None,则进行后移操作cur.next = node
1.循环结束
2.游标指向最后一个节点(最后一个节点的next域指向None)
3.将最后一个节点的next域指向node(最后一个节点的next域指向新创建的node节点)
[========]
6. insert()
def insert(self, pos, item):
if pos <= 0:
self.add(item)
elif pos > (self.length()-1):
self.append(item)
else:
node = Node(item)
pro = self.__head
count = 0
while count < (pos-1):
count += 1
pro = pro.next
node.next = pro.next
pro.next = node
__作用:__指定位置插入节点
步骤详解:
if pos <= 0:
self.add(item)
如果给定的下标小于等于0,直接在链表首部添加元素elif pos > (self.length()-1):
self.append(item)
如果给定的下标大于链表的尾部下标,直接进行尾部添加元素
3.
else:
进入插入节点的代码体
node = Node(item)
创建一个新节点
pro = self.__head
创建游标
count = 0
我们计数的方式来控制游标
while count < (pos-1):
当下标到pos前一位元素的时候,结束循环
count += 1
pro = pro.next
游标向后移动一位
node.next = pro.next
新节点的next域指向pos上一个位置处的next域的指向
pro.next = node
将pos上一个位置处的next域指向新节点
注意: 如果给定的下标等于尾部下标,就等于是在尾部下标处添加元素,原有的尾部元素要向后移动一位, 我们把他归类到插入节点的代码中
[========]
7. search()
def search(self, item):
cur = self.__head
while cur != None:
if cur.elem == item:
return True
else:
cur = cur.next
return False
__作用:__查找节点是否存在
cur = self.__head
创建游标,起始指向首节点while cur != None:
遍历所有节点,也就是说只要游标指向的不是None,此循环一直执行
1.if cur.elem == item:
如果节点的elem等于item,返回True
return True
2.else:
cur = cur.next
游标向后移动一位
return False
循环结束后,也就是遍历了所有节点之后没有找到item元素,返回False
[========]
8. remove()
def remove(self, item):
cur = self.__head
pro = None
while cur != None:
if cur.elem == item:
if cur == self.__head:
self.__head = cur.next
else:
pro.next = cur.next
break
else:
pro = cur
cur = cur.next
作用:_删除节点
- 创建两个游标,__cur__和pro
cur = self.__head
游标cur,起始指向链表的首节点.
作用:指向当前节点
pro = None
游标Pro,起始指向None
作用:指向cur游标的上一个节点 - 遍历所有元素,条件为cur不等于None
while cur != None:
2.1.if cur.elem == item:
如果cur的elem等于item(如果游标所指向的节点的元素等于要查找的元素), 执行删除操作
2.2.if cur == self.__head:
先判断item的节点是否为首节点
2.3self.__head = cur.next
让首节点指向cur的后继节点
3.
else:
pro.next = cur.next
item如果不是首节点的elem,
直接将当前节点的__前一个节点的next域__指向当前节点的__后继节点的next域__
4.break
pro = cur
pro为cur的上一个元素的游标
cur = cur.next
注意: pro与cur的位置不能颠倒
注意点:
- 先判断该链表是否为空
如果是空链表不执行任何操作 - 如果只有一个节点
以上代码完全可以完成任务(即:self.__head == cur.next) - 首节点
如果是首节点,将链表的首节点self.__head指向cur当前节点的后继节点 - 尾节点
以上代码完全可以完成任务(即:pro.next = cur.next)
顺序表与单链表的对比
时间复杂度的对比:
操作 链表 顺序表
访问元素 O(n) O(1)
从头部删除元素 O(1) O(n)
从尾部删除元素 O(n) O(1)
在中间插入元素 O(n) O(n)
总结:
链表失去了顺序表随机读取的优点,同时链表由于增加了节点的指针域,空间开销比较大,但对存储空间使用要相对灵活
链表和顺序表在插入和删除是进行的是完全不同的操作
链表:
1.主要耗时的操作是遍历查找
2.删除和插入操作本身的复杂度是O(1)
顺序表:
1.主要耗时的操作是拷贝覆盖
2.抛除元素在尾部的情况
顺序表进行插入和删除时,需要对操作点之后的所有元素进行前后移动操作
只能通过拷贝和覆盖的方法进行
以上是关于单链表的主要内容,如果未能解决你的问题,请参考以下文章