文章目录
- 前言
- 1. 从尾到头打印链表
- 2. 链表中倒数最后k个结点
- 3. 反转链表
- 4. 合并两个排序的链表
- 5. 复杂链表的复制
- 6. 两个链表的第一个公共结点
- 7. 链表中环的入口结点
- 8. 删除链表中重复的结点
前言
本博客对剑指offer中的链表类型的题目进行总结和整理,并分析各类题目中容易出错的点和容易遗忘的边界条件等。
1. 从尾到头打印链表
题目描述:
输入一个链表的头节点,按链表从尾到头的顺序返回每个节点的值(用数组返回)。
示例:
输入:{1,2,3}
返回值:[3,2,1]
思路:新建一个空list,遍历链表,依次将链表的值插入到list头部位置。
class listNode:
def __init__(self, x):
self.val = x
self.next = None
class Solution:
# 返回从尾部到头部的列表值序列,例如[1,2,3]
def printListFromTailToHead(self, listNode):
list0 = []
while listNode:
list0.insert(0, listNode.val)
listNode = listNode.next
return list0
2. 链表中倒数最后k个结点
题目描述:
输入一个链表,输出一个链表,该输出链表包含原链表中从倒数第k个结点至尾节点的全部节点。
如果该链表长度小于k,请返回一个长度为 0 的链表。
示例:
输入:{1,2,3,4,5},1
返回值:{5}
思路:新建一个空list,将链表节点逐个存入。返回倒数第k个节点,即可返回该节点之后的子链表。
class Solution:
def FindKthToTail(self , pHead , k ):
list0 = []
count = 0
while pHead:
list0.append(pHead)
pHead = pHead.next
count += 1
if count < k or k == 0:
return
else:
return list0[count-k]
3. 反转链表
题目描述:
输入一个链表,反转链表后,输出新链表的表头。
示例:
输入:{1,2,3}
返回值:{3,2,1}
思路:三指针法(PreNode、pHead、nextNode)。
class Solution:
# 返回ListNode
def ReverseList(self, pHead):
preNode = None
while pHead:
nextNode = pHead.next
pHead.next = preNode
preNode, pHead = pHead, nextNode
return preNode
4. 合并两个排序的链表
题目描述:
输入两个单调递增的链表,输出两个链表合成后的链表,当然我们需要合成后的链表满足单调不减规则。
示例:
输入:{1,3,5},{2,4,6}
返回值:{1,2,3,4,5,6}
思路:新建一个val=0的虚头部节点,然后依次对比两个链表,最后返回虚节点后一个节点(return firstNode.next)。
class Solution:
# 返回合并后列表
def Merge(self, pHead1, pHead2):
firstNode = head0 = ListNode(0)
while pHead1 and pHead2:
if pHead1.val <= pHead2.val:
head0.next = pHead1
pHead1 = pHead1.next
else:
head0.next = pHead2
pHead2 = pHead2.next
head0 = head0.next
if pHead1 is None:
head0.next = pHead2
if pHead2 is None:
head0.next = pHead1
return firstNode.next
5. 复杂链表的复制
题目描述:
输入一个复杂链表(每个节点中有节点值,以及两个指针,一个指向下一个节点,另一个特殊指针random指向一个随机节点),请对此链表进行深拷贝,并返回拷贝后的头结点。(注意,输出结果中请不要返回参数中的节点引用,否则判题程序会直接返回空)。 下图是一个含有5个结点的复杂链表。图中实线箭头表示next指针,虚线箭头表示random指针。为简单起见,指向null的指针没有画出。
示例:
输入:{1,2,3,4,5,3,5,#,2,#}
输出:{1,2,3,4,5,3,5,#,2,#}
解析:我们将链表分为两段,前半部分{1,2,3,4,5}为ListNode,后半部分{3,5,#,2,#}是随机指针域表示。
以上示例前半部分可以表示链表为的ListNode:1->2->3->4->5 后半部分,3,5,#,2,#分别的表示为
1的位置指向3,2的位置指向5,3的位置指向null,4的位置指向2,5的位置指向null
如下图:
思路:哈希表法。新建一个dict,key:原始复杂链表阶段,value:新copy的链表节点。
class RandomListNode:
def __init__(self, x):
self.label = x
self.next = None
self.random = None
class Solution:
# 返回 RandomListNode
def Clone(self, pHead):
# write code here
if pHead is None:
return
dict = {}
head = pHead
# 哈希表的方法。构建字典,根据原链表节点逐个创建新链表节点作为value,
# 与原链表节点一一对应,原链表节点作为key
while head:
dict[head] = RandomListNode(head.label)
head = head.next
# 字典建立完成后,根据key的next和random指向来设置value的next, random
head = pHead
while head:
dict[head].next = dict.get(head.next)
dict[head].random = dict.get(head.random)
head = head.next
return dict[pHead]
6. 两个链表的第一个公共结点
题目描述:
输入两个无环的单链表,找出它们的第一个公共结点。(注意因为传入数据是链表,所以错误测试数据的提示是用其他方式显示的,保证传入数据是正确的)
示例:
输入:{1,2,3},{4,5},{6,7}
输出:{6,7}
说明:第一个参数{1,2,3}代表是第一个链表非公共部分,第二个参数{4,5}代表是第二个链表非公共部分,最后的{6,7}表示的是2个链表的公共部分
这3个参数最后在后台会组装成为2个两个无环的单链表,且是有公共节点的
思路:双指针法。
- 第一种情况:相同长度有交点
两个指针一起走,步长一致,碰到第一个相同的节点 p1 == p1,退出循环,return p1。 - 第二种情况:相同长度无交点
两个指针一起走,直到走到最后一个节点,p1.next 和 p2.next都为 None,满足 相等的条件,退出循环,return p1。 - 第三种情况:不同长度有交点
两个指针一起走,当一个指针p1走到终点时,说明p1所在的链表比较短,让p1指向另一个链表的头结点开始走,直到p2走到终点,让p2指向短的链表的头结点,那么,接下来两个指针要走的长度就一样了,变成第一种情况。 - 第四种情况:不同长度无交点
两个指针一起走,当一个指针p1走到终点时,说明p1所在的链表比较短,让p1指向另一个链表的头结点开始走,直到p2走到终点,让p2指向短的链表的头结点,那么,接下来两个指针要走的长度就一样了,变成第二种情况。
class Solution:
def FindFirstCommonNode(self, pHead1, pHead2):
# double point
p1 = pHead1
p2 = pHead2
# when p1==p2 or p1==p2==None,exit
while p1 != p2:
if not p1:
p1 = pHead2
else:
p1 = p1.next
if not p2:
p2 = pHead1
else:
p2 = p2.next
return p1
7. 链表中环的入口结点
题目描述:
给一个链表,若其中包含环,请找出该链表的环的入口结点,否则,返回null。
输入描述:
输入分为2段,第一段是入环前的链表部分,第二段是链表环的部分,后台将这2个会组装成一个有环或者无环单链表
返回值描述:
返回链表的环的入口结点即可。而我们后台程序会打印这个节点
示例1
输入:{1,2},{3,4,5}
返回值:3
说明:返回环形链表入口节点,我们后台会打印该环形链表入口节点,即3
思路:
哈希表法:借助字典,同复杂链表的复制
class Solution:
def EntryNodeOfLoop(self, pHead):
if pHead is None:
return
# 哈希表:字典,同复杂链表的复制
dict = {}
while pHead:
if dict.get(pHead):
return pHead
else:
dict[pHead] = pHead.val
pHead = pHead.next
return
8. 删除链表中重复的结点
题目描述
在一个排序的链表中,存在重复的结点,请删除该链表中重复的结点,重复的结点不保留,返回链表头指针。 例如,链表1->2->3->3->4->4->5 处理后为 1->2->5
示例
输入:{1,2,3,3,4,4,5}
返回值:{1,2,5}
思路:
三指针法,通过cur和nextNode来找出重复的节点范围
class Solution:
def deleteDuplication(self, pHead):
# write code here
if pHead is None:
return pHead
if pHead.next is None:
return pHead
preNode = head = ListNode(0)
# 三指针法,通过cur和nextNode来找出重复的节点范围
preNode.next = pHead
cur = pHead
nextNode = pHead.next
while nextNode:
if cur.val == nextNode.val:
while nextNode and cur.val == nextNode.val:
nextNode = nextNode.next
if nextNode is None:
preNode.next = nextNode
else:
preNode.next = nextNode
cur = nextNode
nextNode = nextNode.next
else:
preNode = preNode.next
cur = cur.next
nextNode = nextNode.next
return head.next