目录
talk is cheap,show me your code:
双向链表
介绍:
百度百科:双向链表也叫双链表,是链表的一种,它的每个数据结点中都有两个指针,分别指向直接后继和直接前驱。所以,从双向链表中的任意一个结点开始,都可以很方便地访问它的前驱结点和后继结点。一般我们都构造双向循环链表。
链表功能:
尾插、尾删、指定位置插入指定值、删除指定位置节点、遍历、长度、反转、清空、删除倒数第n个节点
说明:
双向链表维护一个头节点、尾节点、链表长度,代码效率高
talk is cheap,show me your code:
bidirect.py:
class Node:
'''
链表节点
previous <- node(value) -> next
'''
def __init__(self, val = None):
self.value = val
self.previous = None
self.next = None
def __del__(self):
# print('delete the node')
pass
class LinkList:
'''
双向链表,采用哨兵结构,头尾节点值为空
head <-> node1 <-> node2 ...<-> node(n) <-> tail
'''
def __init__(self):
self.head = Node()
self.tail = Node()
self.head.next = self.tail
self.tail.previous = self.head
self.length = 0
def __len__(self) -> int:
'''
求链表长度
:return: 返回链表长度
'''
return self.length
def insert(self, pos = 0, val = 0):
'''
在链表指定位置插入指定值
位置参数:param pos: 整型且大于等于零
插入值参数:param val:
:return: 布尔型
'''
if not (isinstance(pos, int) and pos >= 0 and pos <= self.length):
raise Exception('输入的位置参数不合法')
node = self.head
i=0
while node.next != self.tail:
node = node.next
if pos == i:
new = Node(val)
nodePre = node.previous
nodePre.next = new
new.previous = nodePre
new.next = node
node.previous = new
self.length += 1
return True
i += 1
else:
if i == self.length:
self.append(val)
return True
return False
def pop(self):
'''
尾删
:return: 返回被删节点的值
'''
if self.length == 0:
raise Exception('链表已为空')
tailPre = self.tail.previous
val = tailPre.value
tailPrePre = self.tail.previous.previous
tailPrePre.next = self.tail
self.tail.previous = tailPrePre
del tailPre
self.length -= 1
return val
def append(self, val):
'''
尾插
'''
tailPre = self.tail.previous
new = Node(val)
tailPre.next = new
new.previous = tailPre
new.next = self.tail
self.tail.previous = new
self.length += 1
def remove(self, pos):
'''
删除链表指定位置节点
:return: 返回被删除的值
'''
if self.length == 0:
raise Exception('链表已为空')
if not(isinstance(pos,int) and pos >= 0 and pos < self.length):
raise Exception('输入的位置参数不合法')
node = self.head
i = 0
while node.next != self.tail:
node = node.next
if pos == i:
nodeNext, nodePre = node.next, node.previous
val = node.value
nodePre.next = nodeNext
nodeNext.previous = nodePre
del node
self.length -= 1
return val
i += 1
def traverse(self):
'''
遍历链表
'''
if self.length == 0:
raise Exception('链表为空')
node = self.head
while node.next != self.tail:
node = node.next
print(node.value, end = ' ')
print()
def reverse(self):
'''
链表反转
:return: 返回反转后的链表
'''
if self.length == 0:
raise Exception('链表为空')
node = self.tail
newll = LinkList()
while node.previous != self.head:
node = node.previous
newll.append(node.value)
return newll
def clear(self):
'''
清空链表(保留头节点和尾节点)
'''
self.head.next=self.tail
self.tail.previous=self.head
def remove_lastn(self,n):
'''
删除倒数第n个节点
'''
if not(isinstance(n,int) and n<=self.length and n>=1):
raise Exception('输入参数n不合法')
node=self.tail
i=0
while node.previous != self.head:
node=node.previous
i+=1
if i==n:
nodeNext=node.next
nodePre=node.previous
nodePre.next=nodeNext
nodeNext.previous=nodePre
if __name__ == '__main__':
ll = LinkList()
print(ll.__doc__)
ll.append(1)
ll.append(2)
ll.append(3)
ll.append(4)
ll.traverse()
print(ll.head.next.value, ll.tail.previous.value)
print(ll.pop())
ll.traverse()
ll.insert(0, 5)
ll.insert(2, 4)
print(ll.insert(5, 6))
print('len=', len(ll))
ll.traverse()
print(ll.head.next.value, ll.tail.previous.value)
ll = ll.reverse()
ll.traverse()
print(ll.head.next.value, ll.tail.previous.value)
ll = ll.reverse()
ll.traverse()
print(ll.head.next.value, ll.tail.previous.value)
测试结果:
衍生双向循环链表
实现:
将尾节点指向头节点即可,此带有头尾节点的循环链表使用过程中需额外多加判断,但问题不大
应用:
约瑟夫环游戏(已知 n 个人(以编号1,2,3…n分别表示)围坐在一张圆桌周围。从编号为 k 的人开始报数,数到 m 的那个人出圈;他的下一个人又从 1 开始报数,数到 m 的那个人又出圈;依此规律重复下去,直到剩余最后一个胜利者。)
代码:
from bidirect import *
class CircleLinkList(LinkList):
'''
循环链表
'''
def __init__(self):
super().__init__()
self.tail.next=self.head
self.head.previous=self.tail
def generate_withN(self,n):
'''
根据n生成节点为1-n的循环链表
:param n:
:return:
'''
self.length=0
node=self.head
for i in range(1,n+1):
new=Node(i)
node.next=new
new.previous=node
self.length+=1
node=node.next
new.next=self.tail
self.tail.previous=new
def Joseph(n,k,m):
'''
约瑟夫环问题
:param n: 人数
:param k: 第k个人开始
:param m: 报数到m
:return: 最后一个人,胜利者
'''
if not(isinstance(n,int) and n>0 and isinstance(k,int) and 0<k<=n and isinstance(m,int) and m>0):
raise Exception('input illegal')
cll=CircleLinkList()
cll.generate_withN(n)
head=cll.head
i=0
while head.next!=cll.tail:
head=head.next
i+=1
if i==k:
break
print('head:',head.value)
node=head
i=0
while cll.length!=1:
if node!=cll.tail and node!=cll.head:
i+=1
if i==m:
i=0
nodePre,nodeNext=node.previous,node.next
nodePre.next=nodeNext
nodeNext.previous=nodePre
cll.length-=1
cll.traverse()
node=node.next
return node.value
if __name__ == '__main__':
ll = CircleLinkList()
print(ll.__doc__)
winner=Joseph(14,2,5)
print('winner:',winner)
测试结果: