本篇准备学习单链表的找查、删除和反转操作,所有操作会用到以下前文《触“类”旁通2|数据结构入门之单链表的创建和遍历》中的代码,除__init__是必须的外,其它方法和属性基本只是为了方便赋值和检验结果正确与否而设,在解决问题时尽可能地不使用;更不能使用上篇中特别指出的“万能偷懒大法”来解决问题。节点类的基本属性和方法,如下:
class Node():
def __init__(self, value=None, Next=None):
self.val = value
self.next = Next
if type(self.next)==Node and self.next.val==None:
self.next=None
def __repr__(self):
return f'Node({self.val}->{self.next})'
def __str__(self):
return f'{self.val}->{self.next}'
def __len__(self):
if self.val is None:
return 0
length,ptr = 0,self
while ptr is not None:
length += 1
ptr = ptr.next
return length
def __eq__(self, other):
ptr1,ptr2 = self,other
if len(ptr1)!=len(ptr2):
return False
while ptr1 is not None:
if ptr1.val!=ptr2.val:
return False
ptr1 = ptr1.next
ptr2 = ptr2.next
else:
return True
def size(self):
return self.__len__()
@property
def length(self):
return self.size()
@property
def value(self):
return self.val
@property
def values(self):
ret,ptr = [],self
while ptr is not None:
ret.append(ptr.val)
ptr = ptr.next
return ret
def pprint(self):
items = [str(i) for i in self.values]
if items==[]:
print('None->None')
elif len(items)==1:
print(f'{items[0]}->None')
else:
print('->'.join(items)+'->None')
def isNode(node):
return isinstance(node,Node) and isinstance(node.next,(Node,type(None)))
def build(*data, split=True):
'''把数据转换成节点链表'''
lst,ret = [],Node()
for val in data:
if type(val) is str:
if not split:
lst.append(val)
continue
if str=='':
continue
try:
num = int(val)
lst.extend([int(_) for _ in val])
except:
lst.extend([_ for _ in val])
elif hasattr(val,'__iter__'):
lst.extend([_ for _ in val])
elif type(val) is Node:
if val is not None:
lst.extend(val.values)
else:
lst.append(val)
ret = Node()
for i in lst[::-1]:
ret = Node(i, ret)
return ret
def copy(node):
ret = Node()
ptr1,ptr2 = node,ret
while ptr1 is not None:
ptr2.next = Node(ptr1.val)
ptr1,ptr2 = ptr1.next,ptr2.next
return ret.next
1. 查找操作
1. 查找首个元素
返回数据域等于目标值num的第一个节点的索引号,头节点为0;找不到则返回 -1。
def find(self, num):
ptr,ret = self,-1
while ptr is not None:
ret += 1
if ptr.val == num: break
ptr = ptr.next
else:
ret = -1
return ret
2. 查找所有元素
返回数据域等于目标值num的所有节点的索引号列表;找不到则返回 [-1]。
def findall(self, num):
ptr,idx,ret = self,-1,[]
while ptr is not None:
idx += 1
if ptr.val == num:
ret.append(idx)
ptr = ptr.next
if ret == []: ret = [-1]
return ret
3. 返回指定索引号的元素
def index(self, n):
if type(n) is not int or n<0:
raise BaseException('N = 0, 1, 2, ..., Node.size()-1')
ptr,count = self,-1
while ptr is not None:
count += 1
if count==n: return ptr.val
ptr = ptr.next
if ptr is None: self.index(-1)
'''
>>> node.index(0)
1
>>> node.index(2)
3
>>> node.index(4)
5
>>> node.index(5)
Traceback (most recent call last):
File "<pyshell#27>", line 1, in <module>
node.index(5)
File "D:\Node0806.py", line 362, in index
if ptr is None: self.index(-1)
File "D:\Node0806.py", line 356, in index
raise BaseException('N = 0, 1, 2, ..., Node.size()-1')
BaseException: N = 0, 1, 2, ..., Node.size()-1
>>>
'''
4. 找出链表最大(小)值
def nlargest(self):
ptr,ret = self,self.val
while ptr is not None:
ret = max(ret, ptr.val)
ptr = ptr.next
return ret
def nsmallest(self):
ptr,ret = self,self.val
while ptr is not None:
ret = min(ret, ptr.val)
ptr = ptr.next
return ret
'''
>>> node = Node.build(11,3,12,5,6,7,23,8)
>>> node.nsmallest()
3
>>> node.nlargest()
23
>>>
'''
5. 返回子链表
返回包括第m到第n个元素的子链表,其中m,n 是个数非索引值,范围为 1 ≤ m ≤ n;为方便使用设置了一个负值,仅当n=-1时表示子链表从m开始截止到尾节点结束。即 m,n 可以用(1,n) 表示返回链表的前 n 个节点,而用(m,-1)表示返回链接从第m个节点开始到尾节点结束。
def listMtoN(self, m, n):
'''返回链表的从第m到n的节点,设置n=-1到尾节点'''
if n!=-1 and n<m or m<1:
raise BaseException('range: 1 <= M <= N')
ret = Node()
ptr,ptr1 = self,ret
for _ in range(m-1):
ptr = ptr.next
while ptr is not None:
m += 1
if n!=-1 and m-n>1: break
ptr1.next = Node(ptr.val)
ptr,ptr1 = ptr.next,ptr1.next
return ret.next
'''
>>> node = Node.build(range(1,11))
>>> node.listMtoN(1,4)
Node(1->2->3->4->None)
>>> node.listMtoN(3,8)
Node(3->4->5->6->7->8->None)
>>> node.listMtoN(5,-1)
Node(5->6->7->8->9->10->None)
>>> node.listMtoN(5,-1).reverse()
Node(10->9->8->7->6->5->None)
>>> node
Node(1->2->3->4->5->6->7->8->9->10->None)
>>>
'''
2. 删除操作
1. 清空
>>> def clear(self):
self.val = self.next = None
return self
>>> n = Node.build(1,2,3)
>>> n
Node(1->2->3->None)
>>> clear(n)
Node(None->None)
>>> n
Node(None->None)
>>>
2. 销毁
设置一个类的内置__del__() 方法:当节点不再被需要调用时,引用计数为0时就会被回收,并且调用__del__()方法显示其回收过程。
【知识点】Python 采用自动引用计数(简称 ARC)的方式实现垃圾回收机制。该方法的核心思想是:每个 Python 对象都会配置一个计数器,初始 Python 实例对象的计数器值都为 0,如果有变量引用该实例对象,其计数器的值会加 1,依次类推;反之,每当一个变量取消对该实例对象的引用,计数器会减 1。如果一个 Python 对象的的计数器值为 0,则表明没有变量引用该 Python 对象,即证明程序不再需要它,此时 Python 就会自动调用 __del__() 方法将其回收。
del obj 并不主动调用__del__方法,只有引用计数为0时,__del__()才会被执行,并且定义了__del_()的实例无法被Python的循环垃圾收集器收集,所以尽量不要自定义__del__()。一般情况下,__del__() 不会破坏垃圾处理器。
节点类中添加以下代码用以测试:
def __del__(self):
print(id(self),self)
def init1():
a=Node(1)
b=Node(2)
return a
def init2():
a=Node(1)
b=Node(2)
a.next = b
return a
测试结果大致反馈了被回收对象的id和内容:
>>> n = Node.init1()
55624680 2->None # init1()中的b没被调用,引用计数为0,即被回收
>>> n = Node.init2()
50818736 1->None # init1()中的a也不需要了,才被回收
>>> n = Node()
55624680 1->2->None # 这是init2()中的a,此时才被回收
55624752 2->None # 这是init2()中的b被回收
>>> n = Node.build(3,4,5)
55624680 None->None # 前2个是build方法中用过的两个临时空节点 ret
55624872 None->None
50818736 None->None # 这个是上一步 n=Node() 设置的空节点
>>> del n
55624704 3->4->5->None # del操作即可销毁,引用计数为0从头节点开始回收
55624872 4->5->None
55624920 5->None
>>>
3. 删除节点
3-1.删除首节点
def delhead(self):
'''删除第一个节点'''
if self.next is None:
self.val = None
else:
self.val,self.next = self.next.val,self.next.next
return self
注:首节点即前面的第一个节点,而头节点是专用名词
3-2. 删除前n个节点
def delfront(self,n):
'''删除前n个节点'''
for _ in range(n):
if self.next is None:
self.val = None
else:
self.val,self.next = self.next.val,self.next.next
return self
3-3. 删除中间单个节点

def delKthNode(self,k):
'''删除第k个节点,首节点时k=1'''
if type(k) is not int or k<1:
raise BaseException('K = 1, 2, 3, ...')
ret,tmp = Node(),Node()
ptr1,ptr2 = self,tmp
for _ in range(1,k):
ptr2.next = Node(ptr1.val)
ptr1,ptr2 = ptr1.next,ptr2.next
if ptr1 is None:
raise BaseException('length of Node less than K')
if ptr1.next is not None:
ret.val,ret.next = ptr1.next.val,ptr1.next.next
if ret.val is not None:
ptr2.next = ret
self.val,self.next = tmp.next.val,tmp.next.next
return self
注:如果要删除中间 n 个节点,上面代码中的ptr1往后多移n-1个节点即可;或者就用循环执行n次本函数。
3-4. 删除首个指定值的节点
即查找出指定值的索引号,然后删除。先idx=find(num)再delKthNode(idx+1);或者把两个函数综合到一个函数,比如 delAnum(self,num):
>>> node = Node.build(1,2,3,4,2,5)
>>> n = 2
>>> node
Node(1->2->3->4->2->5->None)
>>> node.delKthNode(node.find(n)+1)
Node(1->3->4->2->5->None)
>>> node
Node(1->3->4->2->5->None)
>>>
3-5. 删除指定值的所有节点
>>> def delall(node,n):
while node.find(n)!=-1:
node.delKthNode(node.find(n)+1)
return node
>>> node = Node.build(1,2,3,4,2,2,5,2)
>>> node
Node(1->2->3->4->2->2->5->2->None)
>>> delall(node,2)
Node(1->3->4->5->None)
>>> node
Node(1->3->4->5->None)
>>>
>>> # 从副本中删除:
>>> node = Node.build(1,2,3,4,2,2,5,2)
>>> delall(node.copy(),2)
Node(1->3->4->5->None)
>>> node
Node(1->2->3->4->2->2->5->2->None)
>>>
3-6. 弹出首、尾节点
3.3中的函数,当k等于节点链长度时,即删除尾节点。但在此我们要模仿列表的pop()方法,删除尾部元素的同时,返回它的值:
>>> lst = [1,2,3]
>>> lst.pop()
3
>>> lst
[1, 2]
>>> lst.pop()
2
>>> lst
[1]
>>> lst.pop()
1
>>> lst
[]
>>> lst.pop()
Traceback (most recent call last):
File "<pyshell#330>", line 1, in <module>
lst.pop()
IndexError: pop from empty list
>>>
代码及测试效果如下:
def pop(self):
'''弹出尾节点,并返回它的值'''
if self.val==None==self.next:
raise IndexError('pop from empty Node')
if self.next is None:
ret,self.val = self.val,None
return ret
temp = Node()
ptr1,ptr2 = self,temp
while ptr1.next is not None:
ptr2.next = Node(ptr1.val)
ptr1,ptr2 = ptr1.next,ptr2.next
ret = ptr1.val
self.val,self.next = temp.next.val,temp.next.next
return ret
'''
>>> a=Node.build(1,2)
>>> a.pop()
2
>>> a
Node(1->None)
>>> a.pop()
1
>>> a
Node(None->None)
>>> a.pop()
Traceback (most recent call last):
File "<pyshell#341>", line 1, in <module>
a.pop()
File "D:\Node0806.py", line 103, in pop
raise IndexError('pop from empty Node')
IndexError: pop from empty Node
>>>
'''
同理,如要删除首节点的同时返回它的值;只要把delfront()中的返回return语句修改一下:
def frontpop(self):
'''删除第一个节点,并返回它的值'''
if self.val==None==self.next:
raise IndexError('frontpop from empty Node')
ret = self.val
if self.next is None:
self.val = None
else:
self.val,self.next = self.next.val,self.next.next
return ret
Node.pop()的应用
把链表各节点的值依次从尾部弹出,存入新链表刚好形成反序的效果:
def reverseList(self):
ptr = ret = Node()
while True:
try:
num = self.pop()
except:
break
ptr.next = Node(num)
ptr = ptr.next
return ret.next
'''
>>> node = Node.build(1,2,3,4,5)
>>> node
Node(1->2->3->4->5->None)
>>> node.reverseList()
Node(5->4->3->2->1->None)
>>> node
Node(None->None) # node 已被弹空
>>>
>>> # 想要节点本身倒序,直接赋值
>>> node = Node.build(1,2,3,4,5)
>>> node = node.reverseList()
>>> node
Node(5->4->3->2->1->None)
>>>
'''
注:以上只为测试而已没有实用性,因为对有N个节点的链表来说: 单次弹出 fontpop()的效率是 pop() 的N倍;而全部弹出的效率是(N+1)/2倍。所以采用前弹加前插法的效率更高一些。
3. 反转操作
反转即把链表各节点的指向倒转,头尾反过来;类的内置方法__reversed__() 重载了python的内建函数 reversed();而.reverse()直接对节点操作:
1. 重载reversed()函数
def __reversed__(self):
ret,ptr = Node(),self
while ptr is not None:
ret = Node(ptr.val, ret)
ptr = ptr.next
return ret
另一种写法:
def reversed(self):
ret,ptr = None,self.copy()
while ptr is not None:
tmp,ptr.next = ptr.next,ret
ret,ptr = ptr,tmp
return ret
注: 第一行中ret = None,不是Node();另外还用了self的副本.copy()进行操作,否则self本身会被改动。不要.copy()的话,多做一次赋值也是可以的: node = node.reversed() 。
2. 直接反转节点.reverse()
def reverse(self):
ptr,ret = self,Node()
while ptr is not None:
ret = Node(ptr.val,ret)
ptr = ptr.next
self.val,self.next = ret.val,ret.next
return self
3. 两者的区别
区别在于reversed(node)不改变被反转节点node,只是返加一个反序的链表;node.reverse()直接改变了node的节点顺序。差不多与列表中的sorted()和.sort()的区别一致。
>>> node = Node.build(1,2,3,4,5)
>>> reversed(node)
Node(5->4->3->2->1->None)
>>> node
Node(1->2->3->4->5->None)
>>> node.reverse()
Node(5->4->3->2->1->None)
>>> node
Node(5->4->3->2->1->None)
>>>
4. 力扣实战题
实战一:删除倒数第N个节点
Remove Nth Node From End of List (#19)
Given a linked list, remove the n-th node from the end of list and return its head.
示例
Given linked list: 1->2->3->4->5, and n = 2.
After removing the second node from the end, the linked list becomes 1->2->3->5.输入: 1->2->3->4->5, and n = 2.
输出: 1->2->3->5.
解法一:本文3.3-4. 中的 delKthNode() 是删除前面数起第k个,那么倒数第N个只要先遍历一个总个数,然后两数一减后+1再调用即可:
>>> node = Node.build(range(1,6))
>>> node
Node(1->2->3->4->5->None)
>>> n = 2
>>> node.delKthNode(node.length - n + 1)
Node(1->2->3->5->None)
>>>
综合到一起的代码:
def delNthEnd(self,n):
'''删除倒数第n个节点,尾节点时n=1'''
if type(n) is not int or n<1:
raise BaseException('N = 1, 2, 3, ...')
ptr,size = self,0
while ptr is not None:
size += 1
ptr = ptr.next
k = size - n + 1
if k<1:
raise BaseException('length of Node less than N')
ret,tmp = Node(),Node()
ptr1,ptr2 = self,tmp
for _ in range(1,k):
ptr2.next = Node(ptr1.val)
ptr1,ptr2 = ptr1.next,ptr2.next
if ptr1.next is not None:
ret.val,ret.next = ptr1.next.val,ptr1.next.next
if ret.val is not None:
ptr2.next = ret
self.val,self.next = tmp.next.val,tmp.next.next
return self
测试效果:
>>> node = Node.build(range(1,6))
>>> node.delNthEnd(2)
Node(1->2->3->5->None)
>>> node = Node.build(range(1,6))
>>> node.delKthNode(4) # 倒数2和前数4删的效果一致
Node(1->2->3->5->None)
>>>>>> node = Node.build(range(1,6))
>>> node.delNthEnd(1)
Node(1->2->3->4->None)
>>> node = Node.build(range(1,6))
>>> node.delNthEnd(3)
Node(1->2->4->5->None)
>>> node = Node.build(range(1,6))
>>> node.delNthEnd(5)
Node(2->3->4->5->None)
>>> node = Node.build(range(1,6))
>>> node.delNthEnd(6)
Traceback (most recent call last):
File "<pyshell#115>", line 1, in <module>
node.delNthEnd(66)
File "D:\Node0806.py", line 259, in delNthEnd
raise BaseException('length of Node less than N')
BaseException: length of Node less than N
解法二:这个方法相对简单一些,设置快慢两个指针不用先遍历出链表长度。原理:找倒数第n个节点,先用“快指针”移动n个节点,然后“快慢”两个指针同时步进;当快指针到达尾节点时,慢指针刚好到达倒数第n个节点。
def delEndTwoPtr(self,n):
'''快慢双指针删除倒数第n个节点,尾节点时n=1'''
if type(n) is not int or n<1:
raise BaseException('N = 1, 2, 3, ...')
fast,slow = self,self
ret = Node()
ptr = ret
while fast is not None:
fast = fast.next
n -= 1
if n==0: break
if n>0:
raise BaseException('length of Node less than N')
while fast is not None:
ptr.next = Node(slow.val)
ptr = ptr.next
fast,slow = fast.next,slow.next
ptr.next = slow.next
self.val,self.next = ret.next.val,ret.next.next
return self
实战二:成对反转节点
Swap Nodes in Pairs (#24)
Given a linked list, swap every two adjacent nodes and return its head.
You may not modify the values in the list's nodes, only nodes itself may be changed.
示例
Given 1->2->3->4, you should return the list as 2->1->4->3.
输入: 1->2->3->4.
输出: 2->1->4->3.要求不能修改节点的数据域,假设或有成单的尾节点不反转。
解法:前后相邻的双指针双步进遍历链表即可。
def swapPairs(self):
if self.next is None:
return self
ptr1,ptr2 = self,self
ret = Node()
ptr,ptr1 = ret,ptr1.next
while ptr1 is not None:
ptr.next = Node(ptr1.val)
ptr = ptr.next
ptr.next = Node(ptr2.val)
ptr = ptr.next
ptr1,ptr2 = ptr1.next,ptr2.next
if ptr1 is None: break
if ptr1.next is None:
ptr.next = Node(ptr1.val)
ptr1,ptr2 = ptr1.next,ptr2.next
self.val,self.next = ret.next.val,ret.next.next
return self
'''
>>> node = Node.build(range(1,5)); node.swapPairs()
Node(2->1->4->3->None)
>>> node = Node.build(range(1,6)); node.swapPairs()
Node(2->1->4->3->5->None)
>>> node = Node.build(range(1,7)); node.swapPairs()
Node(2->1->4->3->6->5->None)
>>> node = Node.build(range(1,8)); node.swapPairs()
Node(2->1->4->3->6->5->7->None)
>>>
'''
实战三:成组反转节点
Reverse Nodes in k-Group(#25)
Given a linked list, reverse the nodes of a linked list k at a time and return its modified list.
k is a positive integer and is less than or equal to the length of the linked list. If the number of nodes is not a multiple of k then left-out nodes in the end should remain as it is.
示例
Given this linked list: 1->2->3->4->5
For k = 2, you should return: 2->1->4->3->5
For k = 3, you should return: 3->2->1->4->5输入: 1->2->3->4->5.
输出: k=2时,2->1->4->3->5;
k=3时,3->2->1->4->5.如节点的数量不是k的倍数,那么最后剩下的个数小于k的则保持原样不反转。
本题就是上一题的加强版,当k=2时两题的效果相同。
解法:因为k值可以很大,不能像上题一样交换位置;应该多次读出k个节点,分别反转后相接;若最后剩余的少于k个节点就原样追加。反转部分就调用上面重载好的reversed()函数即可。
def reverseKGroup(self, k):
if type(k) is not int or k<1:
raise BaseException('K = 1, 2, 3, ...')
if k==1: return self
ret = Node()
ptr,ptr1 = self,ret
size = 0
while True:
kgroup = Node()
ptr2 = kgroup
count = 0
for _ in range(k):
size += 1
if ptr is None:
if k>=size:
raise BaseException('length of Node less than K')
break
ptr2.next = Node(ptr.val)
count += 1
ptr,ptr2 = ptr.next,ptr2.next
kgroup = kgroup.next
if count==k:
kgroup.reverse()
ptr2 = kgroup
for _ in range(k):
ptr1.next = Node(ptr2.val)
ptr1,ptr2 = ptr1.next,ptr2.next
else:
ptr1.next = kgroup
break
self.val,self.next = ret.next.val,ret.next.next
return self
'''
>>> node = Node.build(range(1,6)); node.reverseKGroup(2)
Node(2->1->4->3->5->None)
>>> node = Node.build(range(1,6)); node.reverseKGroup(3)
Node(3->2->1->4->5->None)
>>>
>>> node = Node.build(range(1,11)); node.reverseKGroup(2)
Node(2->1->4->3->6->5->8->7->10->9->None)
>>> node = Node.build(range(1,11)); node.reverseKGroup(3)
Node(3->2->1->6->5->4->9->8->7->10->None)
>>> node = Node.build(range(1,11)); node.reverseKGroup(4)
Node(4->3->2->1->8->7->6->5->9->10->None)
>>> node = Node.build(range(1,11)); node.reverseKGroup(5)
Node(5->4->3->2->1->10->9->8->7->6->None)
>>> node = Node.build(range(1,11)); node.reverseKGroup(10)
Node(10->9->8->7->6->5->4->3->2->1->None)
>>>
'''
实战四:反转链表中一段节点
Reverse Linked List (a part of NodeList) (#92)
Reverse a linked list from position m to n. Do it in one-pass.
Note: 1 ≤ m ≤ n ≤ length of list.
示例
输入: 1->2->3->4->5->None, m = 2, n = 4
输出: 1->4->3->2->5->None
解法一:直接调用本文1-4.中的 listMtoN()以及3-1.的__reversed__()非常方便,按m,n的值取出链表的三段,最后拼接一下即可:
>>> node = Node.build(1,2,3,4,5)
>>> m,n = 2,4
>>> left = node.listMtoN(1,m-1)
>>> mid = node.listMtoN(m,n)
>>> right = node.listMtoN(n+1,-1)
>>>
>>> left,reversed(mid),right
(Node(1->None), Node(4->3->2->None), Node(5->None))
>>>
综合后的代码:
def reverseMtoN(self, m, n):
ret = Node()
ptr,ptr1 = self,ret
for _ in range(m-1):
ptr1.next = Node(ptr.val)
ptr,ptr1 = ptr.next,ptr1.next
ptr1.next = reversed(self.listMtoN(m, n))
ptr,ptr1 = self,ret
for _ in range(n):
ptr,ptr1 = ptr.next,ptr1.next
ptr1.next = ptr
return ret.next
# 未对m,n的取值范围做判断
'''
>>> node = Node.build(1,2,3,4,5)
>>> node.reverseMtoN(2,4)
Node(1->4->3->2->5->None)
>>> node = Node.build(1,2,3,4,5,6,7,8)
>>> node.reverseMtoN(3,6)
Node(1->2->6->5->4->3->7->8->None)
>>>
'''
解法二:不调用之前的函数,直接操作:遍历时左右两段用追加,中间一段(m到n)用前插法刚好完成倒转。
def reverseMtoN(self, m, n):
ret = Node()
ptr,ptr1 = self,ret
for _ in range(m-1):
ptr1.next = Node(ptr.val)
ptr,ptr1 = ptr.next,ptr1.next
mid = Node(ptr.val)
for _ in range(m,n):
if ptr.next is None: break
mid = Node(ptr.next.val,mid)
ptr = ptr.next
ptr1.next = mid
while mid is not None:
ptr1,mid = ptr1.next,mid.next
while ptr.next is not None:
ptr1.next = Node(ptr.next.val)
ptr,ptr1 = ptr.next,ptr1.next
return ret.next
实战五:链表分组
Partition List (#86)
Given a linked list and a value x, partition it such that all nodes less than x come before nodes greater than or equal to x.
You should preserve the original relative order of the nodes in each of the two partitions.
示例
输入: 1->4->3->2->5->2->None, x = 3
输出: 1->2->2->4->3->5->None给定一个链表和一个整数,把链表分成“小于指定数”和“不小于指定数”的二组连接在一起,并且各组元素在本组中的先后位置保持与原链表相同。
解法一:只新建一个链表,原链表遍历两遍分别找出“小于的”和“不小于的”依次追加到新链表中。
解法二:新建两个链表,遍历一次就分出两组,然后把“不小于组”接到“小于组”的后面。
def partitionI(self, x):
'''把链表分成小于和不小于x的两组并相接'''
ret = Node()
ptr1,ptr2,ptr = self,self,ret
while ptr1 is not None:
if ptr1.val<x:
ptr.next = Node(ptr1.val)
ptr = ptr.next
ptr1 = ptr1.next
while ptr2 is not None:
if ptr2.val>=x:
ptr.next = Node(ptr2.val)
ptr = ptr.next
ptr2 = ptr2.next
return ret.next
def partitionII(self, x):
'''把链表分成小于和不小于x的两组并相接'''
ret,gex = Node(),Node()
ptr1,ptr2,ptr = ret,gex,self
while ptr is not None:
if ptr.val<x:
ptr1.next = Node(ptr.val)
ptr1 = ptr1.next
else:
ptr2.next = Node(ptr.val)
ptr2 = ptr2.next
ptr = ptr.next
ptr1.next = gex.next
return ret.next
'''
>>> node = Node.build(1,4,3,2,5,2)
>>> node.partitionI(3)
Node(1->2->2->4->3->5->None)
>>> node
Node(1->4->3->2->5->2->None)
>>> node.partitionII(3)
Node(1->2->2->4->3->5->None)
>>> node
Node(1->4->3->2->5->2->None)
>>>
>>> node.partitionI(2)
Node(1->4->3->2->5->2->None)
>>> node.partitionII(2)
Node(1->4->3->2->5->2->None)
>>>
>>> node.partitionI(4)
Node(1->3->2->2->4->5->None)
>>> node.partitionII(4)
Node(1->3->2->2->4->5->None)
>>>
'''
类似的还有leetcode第328题是把链表分成索引号是奇、偶数的两组;当然还可以按节点的数据域是奇、偶数或者是质、合数......等等各种不同性质来分组。
实战六:链表旋转
Rotate List (#61)
Given a linked list, rotate the list to the right by k places, where k is non-negative.
示例1
输入: 1->2->3->4->5->NULL, k = 2
输出: 4->5->1->2->3->NULL
解释:
rotate 1 steps to the right: 5->1->2->3->4->NULL
rotate 2 steps to the right: 4->5->1->2->3->NULL
示例2
输入: 0->1->2->NULL, k = 4
输出: 2->0->1->NULL
解释:
rotate 1 steps to the right: 2->0->1->NULL
rotate 2 steps to the right: 1->2->0->NULL
rotate 3 steps to the right: 0->1->2->NULL
rotate 4 steps to the right: 2->0->1->NULL
解法一:链表向右移动,并把原尾节点放到原头节点之前,这是右旋转;顺带把反向的左旋转一并解决:即链表向左移动,并把原头节点放到原尾节点之后。
def rotateR(self, k):
'''链表向右旋转,步进数为k'''
k %= self.length
if k==0:
return self
elif k==1:
ret = Node()
ptr,ptr1 = self,ret
while ptr.next is not None:
ptr1.next = Node(ptr.val)
ptr,ptr1 = ptr.next,ptr1.next
tail = Node(ptr.val,ret.next)
return tail
else:
for _ in range(k):
self = self.rotateR(1)
return self
def rotateL(self, k):
'''链表向左旋转,步进数为k'''
k %= self.length
if k==0:
return self
elif k==1:
ret,head = Node(),Node(self.val)
ptr,ptr1 = self,ret
while ptr.next is not None:
ptr1.next = Node(ptr.next.val)
ptr,ptr1 = ptr.next,ptr1.next
ptr1.next = head
return ret.next
else:
for _ in range(k):
self = self.rotateL(1)
return self
'''
>>> a = Node.build(1,2,3,4,5);k=2
>>> a.rotateR(k)
Node(4->5->1->2->3->None)
>>> a.rotateL(k)
Node(3->4->5->1->2->None)
>>>
>>> b = Node.build(0,1,2);k=4
>>> b.rotateR(k)
Node(2->0->1->None)
>>> b.rotateL(k)
Node(1->2->0->None)
>>>
>>>
>>> a = Node.build(1,2,3,4,5);k=7
>>> for i in range(1,k+1):
print(f'rotate {i} steps to the right: ',a.rotateR(i))
rotate 1 steps to the right: 5->1->2->3->4->None
rotate 2 steps to the right: 4->5->1->2->3->None
rotate 3 steps to the right: 3->4->5->1->2->None
rotate 4 steps to the right: 2->3->4->5->1->None
rotate 5 steps to the right: 1->2->3->4->5->None
rotate 6 steps to the right: 5->1->2->3->4->None
rotate 7 steps to the right: 4->5->1->2->3->None
>>>
>>> for i in range(1,k+1):
print(f'rotate {i} steps to the left: ',a.rotateL(i))
rotate 1 steps to the left: 2->3->4->5->1->None
rotate 2 steps to the left: 3->4->5->1->2->None
rotate 3 steps to the left: 4->5->1->2->3->None
rotate 4 steps to the left: 5->1->2->3->4->None
rotate 5 steps to the left: 1->2->3->4->5->None
rotate 6 steps to the left: 2->3->4->5->1->None
rotate 7 steps to the left: 3->4->5->1->2->None
>>>
'''
解法二:链表复制两份头尾相连,新链表的原链表长度 size 的每一段都是旋转到某一步的结果。并且 k和size 能整除即不移动,有余数就是移到的步数;同时k>0表示向左移,k<0表示向右移。
def rotate(self, k):
'''链表旋转|k|步,正数向左负数向右'''
if k==0: return self
dbouble,size = Node(self.val),0
ptr,ptr1 = self,dbouble
while ptr.next is not None:
ptr1.next = Node(ptr.next.val)
ptr,ptr1 = ptr.next,ptr1.next
ptr = self
while ptr is not None:
size += 1
ptr1.next = Node(ptr.val)
ptr,ptr1 = ptr.next,ptr1.next
k %= size
if k==0: return self
ret = Node()
ptr,ptr1 = dbouble,ret
for i in range(k+size):
if i>=k:
ptr1.next = Node(ptr.val)
ptr1 = ptr1.next
ptr = ptr.next
return ret.next
'''
>>> node = Node.build(1,2,3,4,5)
>>> for i in range(7):
node.rotate(i)
Node(1->2->3->4->5->None)
Node(2->3->4->5->1->None)
Node(3->4->5->1->2->None)
Node(4->5->1->2->3->None)
Node(5->1->2->3->4->None)
Node(1->2->3->4->5->None)
Node(2->3->4->5->1->None)
>>> for i in range(7):
node.rotate(-i)
Node(1->2->3->4->5->None)
Node(5->1->2->3->4->None)
Node(4->5->1->2->3->None)
Node(3->4->5->1->2->None)
Node(2->3->4->5->1->None)
Node(1->2->3->4->5->None)
Node(5->1->2->3->4->None)
>>>
'''
——*** 本篇完 ***——
相关文章链接:
本文详细介绍了单链表的各种操作,包括查找元素(首个、所有、指定索引)、删除元素(首节点、指定数量、指定值、倒数指定位置)、反转链表(整体、子链表)以及实战应用(删除倒数节点、成对反转、分组、旋转)。通过这些操作展示了链表数据结构的灵活运用。
772

被折叠的 条评论
为什么被折叠?



