博主最近在复习算法与数据结构,由于平时主力语言是Python,所以找了个用Python讲解数据结构的视频看了下,链接为:https://www.bilibili.com/video/av20982396?p=1。
关于链表,视频里讲的很清楚,但是代码有几处小错误,现将其代码纠正,并添加视频里没有讲到的双循环链表代码。
那么我们直接上代码吧。
class Node(object):
"""单链表结点"""
def __init__(self, elem):
self.elem = elem
self.next = None
class SinglyLinkedList(object):
def __init__(self, node=None):
self.__head = node
def is_empty(self):
"""判断链表是否为空"""
return self.__head is None
def length(self):
"""返回链表长度"""
cur = self.__head
count = 0
while cur is not None:
count += 1
cur = cur.next
return count
def travel(self):
"""遍历链表"""
cur = self.__head
while cur is not None:
print(cur.elem, end=' ')
cur = cur.next
print()
def add(self, elem):
"""向链表头部添加元素"""
node = Node(elem)
node.next = self.__head
self.__head = node
def append(self, elem):
"""向链表尾部添加元素"""
node = Node(elem)
if self.is_empty():
self.__head = node
else:
cur = self.__head
while cur.next is not None:
cur = cur.next
cur.next = node
def insert(self, pos, elem):
"""向链表位置pos处插入元素
Args:
pos: 插入位置,从0开始计数
elem: 需要插入的元素
"""
if pos <= 0:
self.add(elem)
elif pos > (self.length() - 1):
self.append(elem)
else:
pre = self.__head
count = 0
while count < (pos-1):
count += 1
pre = pre.next
# 当循环退出后,pre指向pos-1位置
node = Node(elem)
node.next = pre.next
pre.next = node
def remove(self, elem):
"""从链表中删除第一个值为elem的元素"""
cur = self.__head
pre = None
while cur is not None:
if cur.elem == elem:
if cur == self.__head:
self.__head = cur.next
else:
pre.next = cur.next
break
else:
pre = cur
cur = cur.next
def search(self, elem):
"""查找链表中是否存在元素elem"""
cur = self.__head
while cur is not None:
if cur.elem == elem:
return True
else:
cur = cur.next
return False
if __name__ == '__main__':
singly_linked_list = SinglyLinkedList()
print(singly_linked_list.is_empty())
print(singly_linked_list.length())
print('===================')
singly_linked_list.append(1)
print(singly_linked_list.is_empty())
print(singly_linked_list.length())
print('===================')
singly_linked_list.append(2)
singly_linked_list.append(3)
singly_linked_list.add(7)
singly_linked_list.append(4)
singly_linked_list.append(5)
singly_linked_list.insert(0, 13) # 13, 7, 1, 2, 3, 4, 5
singly_linked_list.travel()
singly_linked_list.insert(2, 99) # 13, 7, 99, 1, 2, 3, 4, 5
singly_linked_list.travel()
singly_linked_list.insert(11, 22) # 13, 7, 99, 1, 2, 3, 4, 5, 22
singly_linked_list.travel()
singly_linked_list.remove(13)
singly_linked_list.travel() # 7 99 1 2 3 4 5 22
singly_linked_list.remove(22)
singly_linked_list.travel() # 7 99 1 2 3 4 5
singly_linked_list.remove(3)
singly_linked_list.travel() # 7 99 1 2 4 5
print(singly_linked_list.search(1000)) # False
print(singly_linked_list.search(7)) # True
print(singly_linked_list.search(5)) # True
print(singly_linked_list.search(2)) # True
如上所示,测试代码已经在main函数中给出了。
class Node(object):
def __init__(self, elem):
"""单循环链表结点"""
self.elem = elem
self.next = None
class SinglyLinkedCircularList(object):
"""单向循环链表"""
def __init__(self, node=None):
self.__head = node
if node:
node.next = node
def is_empty(self):
"""判断链表是否为空"""
return self.__head is None
def length(self):
"""求链表长度"""
if self.is_empty():
return 0
cur = self.__head
count = 1 # 下方循环退出时,链表至少有一个结点
while cur.next != self.__head:
count += 1
cur = cur.next
return count
def travel(self):
"""遍历链表"""
if self.is_empty():
return
cur = self.__head
while cur.next != self.__head:
print(cur.elem, end=' ')
cur = cur.next
# 退出循环时,cur指向最后一个结点
print(cur.elem)
def add(self, elem):
"""向链表头部添加元素"""
node = Node(elem)
if self.is_empty():
node.next = node
self.__head = node
else:
cur = self.__head
while cur.next != self.__head:
cur = cur.next
node.next = self.__head
self.__head = node
cur.next = node
def append(self, elem):
"""向链表尾部添加元素"""
node = Node(elem)
if self.is_empty():
node.next = node
self.__head = node
else:
cur = self.__head
while cur.next != self.__head:
cur = cur.next
node.next = self.__head
cur.next = node
def insert(self, pos, elem):
"""向链表位置pos处插入元素
Args:
pos: 插入的位置,从0开始计数
elem: 插入的元素
"""
if pos <= 0:
self.add(elem)
elif pos > (self.length() - 1):
self.append(elem)
else:
pre = self.__head
count = 0
while count < (pos - 1):
count += 1
pre = pre.next
# 退出循环,pre指向插入位置的前一个结点
node = Node(elem)
node.next = pre.next
pre.next = node
def remove(self, elem):
"""删除链表中第一个为elem的元素"""
if self.is_empty():
return
cur = self.__head
pre = None
while cur.next != self.__head:
if cur.elem == elem:
# 头结点
if cur == self.__head:
rear = self.__head
while rear.next != self.__head:
rear = rear.next
rear.next = cur.next
self.__head = cur.next
else: # 中间结点
pre.next = cur.next
return
else:
pre = cur
cur = cur.next
# 退出循环, cur指向尾结点
if cur.elem == elem:
# 链表中只有一个结点
if cur == self.__head:
self.__head = None
else:
pre.next = self.__head
def search(self, elem):
"""查找链表中是否存在元素elem"""
if self.is_empty():
return False
cur = self.__head
while cur.next != self.__head:
if cur.elem == elem:
return True
else:
cur = cur.next
# 退出循环,cur指向尾结点
if cur.elem == elem:
return True
return False
if __name__ == '__main__':
singly_linked_circular_list = SinglyLinkedCircularList()
print(singly_linked_circular_list.is_empty())
print(singly_linked_circular_list.length())
print('===================')
singly_linked_circular_list.append(1)
print(singly_linked_circular_list.is_empty())
print(singly_linked_circular_list.length())
print('===================')
singly_linked_circular_list.append(2)
singly_linked_circular_list.append(3)
singly_linked_circular_list.add(7)
singly_linked_circular_list.append(4)
singly_linked_circular_list.append(5)
singly_linked_circular_list.insert(0, 13) # 13, 7, 1, 2, 3, 4, 5
singly_linked_circular_list.travel()
singly_linked_circular_list.insert(2, 99) # 13, 7, 99, 1, 2, 3, 4, 5
singly_linked_circular_list.travel()
singly_linked_circular_list.insert(11, 22) # 13, 7, 99, 1, 2, 3, 4, 5, 22
singly_linked_circular_list.travel()
singly_linked_circular_list.remove(13)
singly_linked_circular_list.travel() # 7 99 1 2 3 4 5 22
singly_linked_circular_list.remove(22)
singly_linked_circular_list.travel() # 7 99 1 2 3 4 5
singly_linked_circular_list.remove(3)
singly_linked_circular_list.travel() # 7 99 1 2 4 5
print(singly_linked_circular_list.search(666)) # False
print(singly_linked_circular_list.search(7)) # True
print(singly_linked_circular_list.search(5)) # True
print(singly_linked_circular_list.search(1)) # True
class Node(object):
def __init__(self, elem):
"""双链表结点"""
self.elem = elem
self.pre = None
self.next = None
class DoubleLinkedList(object):
def __init__(self, node=None):
self.__head = node
def is_empty(self):
"""判断链表是否为空"""
return self.__head is None
def length(self):
"""获取链表长度"""
cur = self.__head
count = 0
while cur is not None:
count += 1
cur = cur.next
return count
def travel(self):
"""遍历链表"""
cur = self.__head
while cur is not None:
print(cur.elem, end=' ')
cur = cur.next
print()
def add(self, elem):
"""向双链表头部添加元素"""
node = Node(elem)
if self.is_empty(): # 链表为空
self.__head = node
else:
node.next = self.__head
node.next.pre = node
self.__head = node
def append(self, elem):
"""向链表尾部添加结点"""
node = Node(elem)
if self.is_empty():
self.__head = node
else:
cur = self.__head
while cur.next is not None:
cur = cur.next
cur.next = node
node.pre = cur
def insert(self, pos, elem):
"""向链表位置pos处插入元素elem"""
if pos <= 0:
self.add(elem)
elif pos > (self.length() - 1):
self.append(elem)
else:
cur = self.__head
count = 0
while count < pos:
count += 1
cur = cur.next
# 退出循环时,cur即为pos位置
node = Node(elem)
node.next = cur
node.pre = cur.pre
cur.pre.next = node
cur.pre = node
def remove(self, elem):
"""删除链表中第一个值为elem的结点"""
if self.is_empty():
return
cur = self.__head
while cur is not None:
if cur.elem == elem:
if cur == self.__head: # 若是头结点
self.__head = cur.next # 链表中只有一个结点
if cur.next:
cur.next.pre = None
else:
cur.pre.next = cur.next
if cur.next: # 如果不是尾结点
cur.next.pre = cur.pre
break
else:
cur = cur.next
def search(self, elem):
"""查找链表中是否存在元素elem"""
cur = self.__head
while cur is not None:
if cur.elem == elem:
return True
else:
cur = cur.next
return False
if __name__ == '__main__':
double_linked_list = DoubleLinkedList()
print(double_linked_list.is_empty())
print(double_linked_list.length())
print('===================')
double_linked_list.append(1)
print(double_linked_list.is_empty())
print(double_linked_list.length())
print('===================')
double_linked_list.append(2)
double_linked_list.append(3)
double_linked_list.add(7)
double_linked_list.append(4)
double_linked_list.append(5)
double_linked_list.insert(0, 13) # 13, 7, 1, 2, 3, 4, 5
double_linked_list.travel()
double_linked_list.insert(2, 99) # 13, 7, 99, 1, 2, 3, 4, 5
double_linked_list.travel()
double_linked_list.insert(11, 22) # 13, 7, 99, 1, 2, 3, 4, 5, 22
double_linked_list.travel()
double_linked_list.remove(13)
double_linked_list.travel() # 7 99 1 2 3 4 5 22
double_linked_list.remove(22)
double_linked_list.travel() # 7 99 1 2 3 4 5
double_linked_list.remove(3)
double_linked_list.travel() # 7 99 1 2 4 5
print(double_linked_list.search(100)) # False
print(double_linked_list.search(7)) # True
print(double_linked_list.search(2)) # True
print(double_linked_list.search(5)) # True
class Node(object):
"""双循环链表结点"""
def __init__(self, elem):
self.elem = elem
self.pre = None
self.next = None
class DoubleLinkedCircularList(object):
def __init__(self, node=None):
self.__head = node
if node:
node.next = node
def is_empty(self):
"""判断链表是否为空"""
return self.__head is None
def length(self):
"""求取链表长度"""
if self.is_empty():
return 0
cur = self.__head
count = 1
while cur.next != self.__head:
count += 1
cur = cur.next
return count
def travel(self):
"""遍历链表"""
if self.is_empty():
return
cur = self.__head
while cur.next != self.__head:
print(cur.elem, end=' ')
cur = cur.next
# 退出循环,cur指向尾结点
print(cur.elem)
def add(self, elem):
"""向链表头部添加元素"""
node = Node(elem)
if self.is_empty():
self.__head = node
node.next = node
else:
cur = self.__head
while cur.next != self.__head:
cur = cur.next
# 退出循环,cur指向尾结点
cur.next = node
node.next = self.__head
self.__head.pre = node
self.__head = node
def append(self, elem):
"""向链表尾部添加元素"""
node = Node(elem)
if self.is_empty():
self.__head = node
node.next = node
else:
cur = self.__head
while cur.next != self.__head:
cur = cur.next
# 退出循环,cur指向尾结点
cur.next = node
node.pre = cur
node.next = self.__head
def insert(self, pos, elem):
"""往双向循环链表位置pos处插入元素
Args:
pos: 插入位置
elem: 插入的元素
"""
if pos <= 0:
self.add(elem)
elif pos > (self.length() - 1):
self.append(elem)
else:
cur = self.__head
count = 0
while count < pos:
count += 1
cur = cur.next
# 退出循环,cur指向需要插入的位置pos
node = Node(elem)
node.next = cur
node.pre = cur.pre
cur.pre.next = node
cur.pre = node
def remove(self, elem):
"""删除链表中第一个值为elem的结点"""
if self.is_empty():
return
cur = self.__head
while cur.next != self.__head:
if cur.elem == elem:
if cur == self.__head: # 删除的是头结点
rear = self.__head
while rear.next != self.__head:
rear = rear.next
self.__head = cur.next
self.__head.pre = None
rear.next = self.__head
else: # 删除的是中间结点
cur.next.pre = cur.pre
cur.pre.next = cur.next
return
else:
cur = cur.next
# 退出循环,cur指向尾结点
if cur.elem == elem:
if cur == self.__head: # 链表中只有一个结点
self.__head = None
else:
cur.pre.next = self.__head
def search(self, elem):
"""查找链表中是否存在值为elem的结点"""
if self.is_empty():
return False
cur = self.__head
while cur.next != self.__head:
if cur.elem == elem:
return True
else:
cur = cur.next
# 退出循环,cur指向尾结点
if cur.elem == elem:
return True
return False
if __name__ == '__main__':
double_linked_circular_list = DoubleLinkedCircularList()
print(double_linked_circular_list.is_empty())
print(double_linked_circular_list.length())
print('===================')
double_linked_circular_list.append(1)
print(double_linked_circular_list.is_empty())
print(double_linked_circular_list.length())
print('===================')
double_linked_circular_list.append(2)
double_linked_circular_list.append(3)
double_linked_circular_list.add(7)
double_linked_circular_list.append(4)
double_linked_circular_list.append(5)
double_linked_circular_list.travel() # 7, 1, 2, 3, 4, 5
double_linked_circular_list.insert(0, 13) # 13, 7, 1, 2, 3, 4, 5
double_linked_circular_list.travel()
double_linked_circular_list.insert(2, 99) # 13, 7, 99, 1, 2, 3, 4, 5
double_linked_circular_list.travel()
double_linked_circular_list.insert(11, 22) # 13, 7, 99, 1, 2, 3, 4, 5, 22
double_linked_circular_list.travel()
double_linked_circular_list.remove(13)
double_linked_circular_list.travel() # 7 99 1 2 3 4 5 22
double_linked_circular_list.remove(22)
double_linked_circular_list.travel() # 7 99 1 2 3 4 5
double_linked_circular_list.remove(3)
double_linked_circular_list.travel() # 7 99 1 2 4 5
print(double_linked_circular_list.search(100)) # False
print(double_linked_circular_list.search(7)) # True
print(double_linked_circular_list.search(2)) # True
print(double_linked_circular_list.search(5)) # True
对于单链表、单循环链表、双链表代码纠正了视频里两处错误:
def insert(self, pos, elem):
if pos < 0:
self.add(elem)
纠正为现在的:
def insert(self, pos, elem):
if pos <= 0:
self.add(elem)
双链表add(self, elem)方法由原来的:
def add(self, elem):
"""向双链表头部添加元素"""
node = Node(elem)
node.next = self.__head
self.__head = node
node.next.pre = node
纠正为现在的:
def add(self, elem):
"""向双链表头部添加元素"""
node = Node(elem)
if self.is_empty(): # 链表为空
self.__head = node
else:
node.next = self.__head
node.next.pre = node
self.__head = node
如果代码中还有其他错误,或者我改错了欢迎读者朋友们评论区指证,我会及时纠正。
操作 | 单链表 | 单循环链表 | 双链表 | 双向循环链表 |
---|---|---|---|---|
初始化 | O ( 1 ) O(1) O(1) | O ( 1 ) O(1) O(1) | O ( 1 ) O(1) O(1) | O ( 1 ) O(1) O(1) |
判空 | O ( 1 ) O(1) O(1) | O ( 1 ) O(1) O(1) | O ( 1 ) O(1) O(1) | O ( 1 ) O(1) O(1) |
求长度 | O ( n ) O(n) O(n) | O ( n ) O(n) O(n) | O ( n ) O(n) O(n) | O ( n ) O(n) O(n) |
遍历 | O ( n ) O(n) O(n) | O ( n ) O(n) O(n) | O ( n ) O(n) O(n) | O ( n ) O(n) O(n) |
头部插入/删除元素 | O ( 1 ) O(1) O(1) | O ( n ) O(n) O(n) | O ( 1 ) O(1) O(1) | O ( n ) O(n) O(n) |
尾部插入/删除元素 | O ( n ) O(n) O(n) | O ( n ) O(n) O(n) | O ( n ) O(n) O(n) | O ( n ) O(n) O(n) |
中间插入/删除元素 | O ( n ) O(n) O(n) | O ( n ) O(n) O(n) | O ( n ) O(n) O(n) | O ( n ) O(n) O(n) |
查找元素 | O ( n ) O(n) O(n) | O ( n ) O(n) O(n) | O ( n ) O(n) O(n) | O ( n ) O(n) O(n) |
可以看到,除了在头部插入/删除元素外,各种链表的其他操作的时间复杂度都是一样的。循环链表因为尾结点的next域(类似C++中的指针)要指向头结点,所以每当在头部插入/删除元素时,都需要遍历链表找到尾结点从而修改其next域,这样就提高了时间复杂度。
需要说明的是,文章上述部分以及代码中所提到的头结点应该叫做首元结点(链表中第一个元素所在的结点)会更为准确。
通常我们说的头结点是首元结点之前的一个结点,它的数据域可以不去定义,也可以存储链表长度等信息。而这个时候头指针就指向头结点而不是首元结点了。也就是说链表的头指针始终保存的是链表的地址,无论这个链表有没有头结点。
设置头结点的主要作用为(参考链接):
如果一个链表的最常用的操作是在末尾插入结点和删除结点,则选用()最节省时间。
A. 带头结点的双循环链表
B. 单循环链表
C. 带尾指针的单循环链表
D. 单链表
这道题的答案应该是A。A中可以利用头结点的数据域存储尾结点的地址,那么每次插入和删除就可以直接访问尾结点,时间复杂度为 O ( 1 ) O(1) O(1)。而其他三种链表中,单链表和单循环链表插入时都需要找到尾结点,时间复杂度为 O ( n ) O(n) O(n),而带尾指针的单循环链表插入元素时间复杂度为 O ( 1 ) O(1) O(1)。在末尾删除元素时,剩下的三种链表都需要找到倒数第二个结点,时间复杂度为 O ( n ) O(n) O(n)。综上,选择A最节省时间。
该题在牛客网上的链接:题目讨论