前言
本文主要总结链表中排序相关的问题与解法。
这一类问题的基本都是根据题目给定的条件,对链表进行各种组合,如:
- 基于归并排序思想,根据节点的数值,合并两个链表(合并两个排序的链表、合并k个已排序的链表)
- 根据节点的位置,对链表重新排序(链表的奇偶重排)
- 对两个链表节点的数值相加(链表相加(二))
- 穿针引线(删除有序链表中重复的元素-I、删除有序链表中重复的元素-II)
一般套路是:
- 新建一个虚拟头节点 dummpyNode = ListNode(-1) 和一个游走节点 pHead = dummpyNode
- dummpyNode负责指返回链表,游走节点负责生成链表
- 如果满足xx要求,则有 pHead.next = head; pHead = pHead.next;再开始下一轮循环
- 由于在最前面,pHead.next = head,所以dummpyNode.next也就指向了整个链表的头部,后面dummpyNode一直没有动,只有pHead在游走。因此,返回 dummpyNode.next,也就是返回了整个链表。
基于归并排序
合并两个排序的链表
输入两个递增的链表,单个链表的长度为n,合并这两个链表并使新链表中的节点仍然是递增排序的。
要求:空间复杂度 O(1)O(1),时间复杂度 O(n)O(n)输入:
{1,3,5},{2,4,6}返回值:
{1,2,3,4,5,6}
# -*- coding:utf-8 -*-
class ListNode:
def __init__(self, x):
self.val = x
self.next = None
class Solution:
# 返回合并后列表
def Merge(self, pHead1, pHead2):
# write code here
dumpyNode = ListNode(-1)
pHead = dumpyNode
while pHead1 or pHead2:
if pHead1 and pHead2:
if pHead1.val<pHead2.val:
pHead.next = pHead1
pHead1 = pHead1.next
else:
pHead.next = pHead2
pHead2 = pHead2.next
elif pHead1:
pHead.next = pHead1
pHead1 = pHead1.next
elif pHead2:
pHead.next = pHead2
pHead2 = pHead2.next
pHead = pHead.next
return dumpyNode.next
合并k个已排序的链表
前面合并两个排序的链表的对象,是两个单独的链表。对于有k个链表的数组,可以使用分治+递归的思想,把k个元素逐渐拆分k/2、k/4、...、2,就可以套用前面的merge函数。
class ListNode:
def __init__(self, x):
self.val = x
self.next = None
class Solution:
def mergeKLists(self , lists ):
# write code here
start = 0
end = len(lists)
return self.subMergeKLists(lists, start, end-1)
def subMergeKLists(self , lists, start, end):
if start>end:
return None
elif start==end:
return lists[start]
# mid = int((end-start)/2)+start
mid = (end-start)/2+start
pHead1 = self.subMergeKLists(lists, start, mid)
pHead2 = self.subMergeKLists(lists, mid+1, end)
return self.merge(pHead1, pHead2)
def merge(self, pHead1, pHead2):
# write code here
dumpyNode = ListNode(-1)
pHead = dumpyNode
while pHead1 or pHead2:
if pHead1 and pHead2:
if pHead1.val<pHead2.val:
pHead.next = pHead1
pHead1 = pHead1.next
else:
pHead.next = pHead2
pHead2 = pHead2.next
elif pHead1:
pHead.next = pHead1
pHead1 = pHead1.next
elif pHead2:
pHead.next = pHead2
pHead2 = pHead2.next
pHead = pHead.next
return dumpyNode.next
细节提示
需要注意判断递归停止的条件是:
- 当start>end时,返回None
- 当start==end时,返回一个链表
- 否则,继续分治
链表相加
链表相加(二)
假设链表中每一个节点的值都在 0 - 9 之间,那么链表整体就可以代表一个整数。
给定两个这种链表,请生成代表两个整数相加值的结果链表。
要求:空间复杂度 O(n),时间复杂度 O(n)
例如:链表 1 为 9->3->7,链表 2 为 6->3,最后生成新的结果链表为 1->0->0->0。
输入:
[9,3,7],[6,3]返回值:
{1,0,0,0}
整体思路
如题目,链表的顺序与加法的顺序是相反的,自然的想到两种思路:
- 把链表的元素压入栈中,借助栈实现对反转链表的元素进行操作;
- 直接反转链表
由于两种方式都需要新建链表,存储两个整数的相加值,因此空间复杂度都是o(n)。方法1比2多一个栈的空间,但是总的空间复杂度也是o(n)。
细节提示
- 加法的10进制的进位。设置进位标志incre,每次循环判断 val1 = list1.pop(-1)+list2.pop(-1)+incre。并且,在循环结束后,需要判断incre是否>0,如果>0,需要在链表中增加
class ListNode:
def __init__(self, x):
self.val = x
self.next = None
class Solution:
def addInList(self , head1 , head2 ):
# write code here
list1 = []
while head1:
list1.append(head1.val)
head1 = head1.next
list2 = []
while head2:
list2.append(head2.val)
head2 = head2.next
list3 = []
incre = 0
while len(list1) and len(list2):
val1 = list1.pop(-1)+list2.pop(-1)+incre
incre = val1/10
val1 = val1%10
list3.append(val1)
while len(list1):
val1 = list1.pop(-1)+incre
incre = val1/10
val1 = val1%10
list3.append(val1)
while len(list2):
val1 = list2.pop(-1)+incre
incre = val1/10
val1 = val1%10
list3.append(val1)
if incre>0:
list3.append(incre)
dumpyNode = ListNode(-1)
pHead = dumpyNode
while len(list3):
pHead.next = ListNode(list3.pop(-1))
pHead = pHead.next
return dumpyNode.next
def addInList2(self , head1 , head2 ):
cur1 = head1
pre = None
while cur1:
next1 = cur1.next
cur1.next = pre
pre = cur1
cur1 = next1
head1 = pre
cur2 = head2
pre2 = None
while cur2:
next2 = cur2.next
cur2.next = pre2
pre2 = cur2
cur2 = next2
head2 = pre2
dumpyNode3 = ListNode(-1)
pHead = dumpyNode3
incre = 0
while head1 and head2:
val = head1.val+head2.val+incre
incre = val/10
val = val%10
head = ListNode(val)
pHead.next = head
pHead = pHead.next
head1 = head1.next
head2 = head2.next
while head1:
val = head1.val+incre
incre = val/10
val = val%10
head = ListNode(val)
pHead.next = head
pHead = pHead.next
head1 = head1.next
while head2:
val = head2.val+incre
incre = val/10
val = val%10
head = ListNode(val)
pHead.next = head
pHead = pHead.next
head2 = head2.next
if incre>0:
head = ListNode(incre)
pHead.next = head
pHead = pHead.next
pHead = dumpyNode3.next
cur1 = pHead
pre = None
while cur1:
next1 = cur1.next
cur1.next = pre
pre = cur1
cur1 = next1
return pre
排序
单链表的排序
给定一个节点数为n的无序单链表,对其按升序排序。
数据范围:0 < n \le 1000000<n≤100000
输入:
[1,3,2,4,5]复制返回值:
{1,2,3,4,5}
class ListNode:
def __init__(self, x):
self.val = x
self.next = None
class Solution:
def sortInList(self , head ):
# write code here
list1 = []
while head:
list1.append(head.val)
head = head.next
list1.sort()
dummpyHead = ListNode(-1)
pHead = dummpyHead
while list1:
pHead.next = ListNode(list1.pop(0))
pHead = pHead.next
return dummpyHead.next
链表的奇偶重排
给定一个单链表,请设定一个函数,将链表的奇数位节点和偶数位节点分别放在一起,重排后输出。
注意是节点的编号而非节点的数值。
要求:空间复杂度 O(n)O(n),时间复杂度 O(n)O(n)
输入:
{1,2,3,4,5,6}返回值:
{1,3,5,2,4,6}
class ListNode:
def __init__(self, x):
self.val = x
self.next = None
class Solution:
def oddEvenList(self , head ):
# write code here
dumpyHead = ListNode(-1)
pHead = dumpyHead
dumpyHead2 = ListNode(-1)
pHead2 = dumpyHead2
i = 1
while head:
val = head.val
if i%2 == 0:
pHead.next = ListNode(val)
pHead = pHead.next
else:
pHead2.next = ListNode(val)
pHead2 = pHead2.next
head = head.next
i = i+1
pHead2.next = dumpyHead.next
return dumpyHead2.next
元素删除
删除有序链表中重复的元素-I
删除给出链表中的重复元素(链表中元素从小到大有序),使链表中的所有元素都只出现一次
进阶:空间复杂度 O(1)O(1),时间复杂度 O(n)O(n)
输入:
{1,1,2}返回值:
{1,2}
class ListNode:
def __init__(self, x):
self.val = x
self.next = None
class Solution:
def deleteDuplicates(self , head ):
# write code here
dumpyNode = ListNode(-1)
dumpyNode.next = head
cur = head
while cur:
while cur.next and cur.val == cur.next.val:
cur.next = cur.next.next
cur = cur.next
return dumpyNode.next
删除有序链表中重复的元素-II
给出一个升序排序的链表,删除链表中的所有重复出现的元素,只保留原链表中只出现一次的元素。
要求:空间复杂度 O(n),时间复杂度 O(n)
进阶:空间复杂度 O(1),时间复杂度 O(n)
输入:
{1,2,2}复制返回值:
{1}
class ListNode:
def __init__(self, x):
self.val = x
self.next = None
class Solution:
def deleteDuplicates(self , head ):
# write code here
dumpyNode = ListNode(-1)
dumpyNode.next = head
cur = head
pre = dumpyNode
is_re = 0
while cur:
while cur.next and cur.val == cur.next.val:
cur.next = cur.next.next
is_re = 1
if is_re==0:
pre = cur
cur = cur.next
else:
pre.next = cur.next
cur = cur.next
is_re = 0
return dumpyNode.next
细节提示
注意删除有序链表中重复的元素-I和删除有序链表中重复的元素-II 两道题的区别。
1中的重复元素可以保留,所以可以直接用cur来遍历链表;
2中的重复元素不可以保留,所以需要使用pre来跳过,且分两种情况:
- cur是重复元素,跳过cur,pre.next = cur.next, cur = cur.next
- cur不是重复元素,逐个遍历,pre = cur,cur = cur.next
总结
其实上面的题目的思路都很简单,相当于把简单的排序从数组迁移到了链表中。个人认为技巧在于链表节点的生成与穿针引线,一般可以使用两个辅助节点,定义虚拟节点和游走节点,虚拟节点负责返回整个链表,游走节点负责穿针引线。
- 新建一个虚拟头节点 dummpyNode = ListNode(-1) 和一个游走节点 pHead = dummpyNode
- dummpyNode负责指返回链表,游走节点负责生成链表
- 如果满足xx要求,则有 pHead.next = head; pHead = pHead.next;再开始下一轮循环
- 由于在最前面,pHead.next = head,所以dummpyNode.next也就指向了整个链表的头部,后面dummpyNode一直没有动,只有pHead在游走。因此,返回 dummpyNode.next,也就是返回了整个链表。