题目
-
从尾到头打印链表
-
反转链表
-
链表中倒数第K个结点
-
合并两个/K个有序链表
-
复杂链表的复制
-
删除链表中的重复元素
-
两个链表的第一个公共结点
-
链表中环的入口结点
-
二叉搜索树与双向链表
代码实现
从尾到头打印链表
题目:输入一个链表,按链表从尾到头的顺序返回一个ArrayList。
思路:
-
方式1:先加载链表中的所有值到新建的列表中,然后将该列表反转,返回一个ArrayList。
-
方式2:借助辅助栈,一个栈存储链表中由前至后结点值,另一个栈存储该栈依次弹出结点值。
python
# -*- coding:utf-8 -*-
# class ListNode:
# def __init__(self, x):
# self.val = x
# self.next = None
class Solution:
# 返回从尾部到头部的列表值序列,例如[1,2,3]
def printListFromTailToHead(self, listNode):
# write code here
"""
#解法1:先加载链表中的所有值到列表中,然后将该列表反转,返回一个ArrayList
ret=[]
head=listNode
while(head):
ret.append(head.val)
head=head.next
ret.reverse()
return ret
"""
#解法2:利用辅助栈来解决
stack = []
result_array = []
node_p = listNode
while node_p:
stack.append(node_p.val)
node_p = node_p.next
while stack:
result_array.append(stack.pop(-1))
return result_array
反转链表
题目描述:输入一个链表,反转链表后,输出新链表的表头。
思路:注意和第1题的区别,此题返回的是一个链表。设置None,从头指针开始依次改变指针的指向。注意:需用pHead.next作为判断条件,而非pHead,否则会导致头节点为 None,出现错误。反转链表很典型,一些链表的复杂题目是在其基础上产生的。
python
# -*- coding:utf-8 -*-
# class ListNode:
# def __init__(self, x):
# self.val = x
# self.next = None
class Solution:
# 返回ListNode
def ReverseList(self, pHead):
# write code here
#判断特殊情况
#对于pHead的长度为1的情况依然是适用的
if not pHead:
return None
pre = None
#反转指针的指向
#这里一定要是判断pHead.next,用pHead的话pHead的头节点是None,会出现问题
while pHead.next:
nex = pHead.next
pHead.next = pre
pre = pHead
pHead = nex
pHead.next = pre
return pHead
链表中倒数第K个结点
题目描述:输入一个链表,输出该链表中倒数第k个结点。
思路:采用快慢指针的方式。让一个指针先走k步,当该指针到达None时,另一个指针指向该链表中的倒数第k个结点,返回该指针所对应的结点值。快慢指针的方式的题目比较重要,很多链表题目都是使用这种方法求解的。
python
# -*- coding:utf-8 -*-
# class ListNode:
# def __init__(self, x):
# self.val = x
# self.next = None
class Solution:
def FindKthToTail(self, head, k):
# write code here
#考虑特殊条件
if not head or k<=0: return None
#快慢指针的初始位置
fast_p = head
slow_p = head
#确保可以得到与最初的fast_p位置相隔k个结点的新的新的fast_p位置
#该位置也是和slow_p相距k个结点的位置
for _ in range(k):
if fast_p:
fast_p = fast_p.next
else:
return None
#由于快慢指针相距为k,设原链表的长度为n,则最终慢指针的位置为n-k+1
#即倒数第k个结点的位置
while fast_p:
fast_p = fast_p.next
slow_p = slow_p.next
return slow_p #使用slow_p返回该节点的数值
合并两个/K个有序链表
题目一:合并两个有序链表
题目描述:输入两个单调递增的链表,输出两个链表合成后的链表,当然我们需要合成后的链表满足单调不减规则。
思路:构建新的列表存储两个单调递增的链表的数值,对列表进行排序生成新的列表后,再由其新的生成链表。
python
# -*- 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
p1 = pHead1
p2 = pHead2
#新建一个链表
l = list() #替换为[]会导致执行的速度下降
#加载链表的值到列表
while p1 or p2:
if p1:
l.append(p1.val)
p1 = p1.next
if p2:
l.append(p2.val)
p2 = p2.next
l.sort()
#构建新的链表
for i in range(len(l)):
if i ==0:
l3 = ListNode(l[i])
p3 = l3
else:
p3.next = ListNode(l[i])
p3 = p3.next
#注意返回的是链表,而不是最后一个结点,因而用了l3
return l3 if l else None
题目二:合并K个有序链表
题目:leetcode链接(https://leetcode-cn.com/problems/merge-k-sorted-lists/)
思路:分治递归,详见:合并K个有序链表
N是所有链表中元素的总数,K是链表数量,时间复杂度为NlogK,空间复杂度为logK
python
# Definition for singly-linked list.
# class ListNode:
# def __init__(self, val=0, next=None):
# self.val = val
# self.next = next
class Solution:
def mergeKLists(self, lists: List[ListNode]) -> ListNode:
if len(lists) <= 2:
return self.mergeTwoLists(lists)
def splitLists(lists):
idx = len(lists) // 2
return lists[:idx], lists[idx:]
a, b = splitLists(lists)
a_merge = self.mergeKLists(a)
b_merge = self.mergeKLists(b)
return self.mergeTwoLists([a_merge, b_merge])
def mergeTwoLists(self, lists):
if not lists: return None
if len(lists)==1: return lists[0]
head1, head2 = lists
head = dump = ListNode(0)
while head1 and head2:
if head1.val < head2.val:
head.next = head1
head1 = head1.next
else:
head.next = head2
head2 = head2.next
head = head.next
head.next = head1 if head1 else head2
return dump.next
复杂链表的复制
题目描述:输入一个复杂链表(每个节点中有节点值,以及两个指针,一个指向下一个节点,另一个特殊指针指向任意一个节点),返回结果为复制后复杂链表的head。(注意,输出结果中请不要返回参数中的节点引用,否则判题程序会直接返回空)
思路:先复制按照指向正常结点的结点所构成的链表,再在基础上复制指向任意结点的结点所构成的链表,最终输出复杂链表。
python
# -*- coding:utf-8 -*-
# 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 not pHead: return None
p = pHead
new_h = RandomListNode(p.label)
pre_p = new_h
random_map = {pHead: new_h}
p = p.next
#先完成对于链表的复制
while p:
new_p = RandomListNode(p.label)
random_map[p] = new_p
pre_p.next = new_p
pre_p = pre_p.next
p = p.next
p = pHead
new_p = new_h
#然后完成对于自由结点的复制
while p:
random_p = p.random
if random_p:
new_p.random = random_map[random_p]
p = p.next
new_p = new_p.next
#返回复杂链表
return new_h
删除链表中的重复元素
题目描述:在一个排序的链表中,存在重复的结点,请删除该链表中重复的结点,重复的结点不保留,返回链表头指针。 例如,链表1->2->3->3->4->4->5 处理后为 1->2->5
思路:依次遍历链表中的数值,将不重复结点的指针指向下一个不重复的结点,最后记得输出整个链表而非得到的链表中的最后一个结点的数值。题目很典型,类似的题目是:对于链表中的重复结点,只保留一次数值。
python
# -*- coding:utf-8 -*-
# class ListNode:
# def __init__(self, x):
# self.val = x
# self.next = None
class Solution:
def deleteDuplication(self, pHead):
# write code here
dummy_head = ListNode(None)
dummy_head.next = pHead
pre = dummy_head
cur = pHead
while cur:
if cur.next and cur.val == cur.next.val:
while cur.next and cur.val == cur.next.val:
cur = cur.next
# cur.val != cur.next.val
pre.next = cur.next
cur = cur.next
continue
pre = pre.next
cur = cur.next
#要用.next
return dummy_head.next
扩展题目:删除 链表 中的重复元素(只保留重复元素中的一个)
题目描述:给定一个排序链表,删除所有重复的元素,使得每个元素只出现一次。
思路:和第6题类似,只是指针指向的不同。
python
# Definition for singly-linked list.
# class ListNode:
# def __init__(self, x):
# self.val = x
# self.next = None
class Solution:
def deleteDuplicates(self, head: ListNode) -> ListNode:
"""
:type head: ListNode
:rtype: ListNode
"""
cur = head
while cur:
while cur.next and cur.val == cur.next.val:
cur.next = cur.next.next
cur = cur.next
#注意返回head
return head
两个链表的第一个公共结点
题目描述:输入两个链表,找出它们的第一个公共结点。
思路:
方法1:两个指针循环遍历链表,指针指向的数值相同时,说明是第一个公共结点。
方法2:找到两个链表的长度差k。让一个指针指向较长的链表的第k+1个结点,一个指针指向较短的链表的第一个结点,当指针指向的数值相同时,说明是第一个公共结点。
注意:题目描述中的两个链表A和B自公共结点L之后的结点都是一样的。
python
# -*- coding:utf-8 -*-
# class ListNode:
# def __init__(self, x):
# self.val = x
# self.next = None
class Solution:
def FindFirstCommonNode(self, pHead1, pHead2):
# write code here
"""
解决方案1
#判断条件
if pHead1 is None or pHead2 is None:
return None
pa = pHead1
pb = pHead2
#对于两个链表,相交的第一个节点之后的部分也是相同的
while pa != pb:
if pa:
pa = pa.next
else:
pa = pHead2
if pb:
pb = pb.next
else:
pb = pHead1
#pa==pb对应的是相交的节点。注意这里是返回节点,因而也可以是返回pa。
return pb
"""
#解决方法2:采用对其最后的结点对齐的方式,使得较长的链表的
#头结点先领先两者的长度差大小的步数,之后指针指向相同的结点值时就找到了第一个公共结点
p1 = pHead1
p2 = pHead2
n_p1 = 0
n_p2 = 0
while p1:
p1 = p1.next
n_p1 += 1
while p2:
p2 = p2.next
n_p2 += 1
if n_p1 < n_p2:
pHead1, pHead2 = pHead2, pHead1
for _ in range(n_p1 - n_p2):
pHead1 = pHead1.next
while pHead1:
if pHead1 == pHead2:
return pHead1
else:
pHead1 = pHead1.next
pHead2 = pHead2.next
return None
链表中环的入口结点
题目描述:给一个链表,若其中包含环,请找出该链表的环的入口结点,否则,输出null。
思路:利用快慢指针。刚开始指针都指向链表的第一个结点,先找到两个指针相遇的结点值。之后,根据链表的第一个结点到环入口的步数s等于相遇点到环入口的步数(r-m)加上环的长度r的整数倍进行while判断,当满足等式约束时返回该结点的数值。具体讲解见https://leetcode-cn.com/problems/linked-list-cycle-ii/solution/kuai-man-zhi-zhen-by-powcai-4/。
python
代码:
# -*- coding:utf-8 -*-
# class ListNode:
# def __init__(self, x):
# self.val = x
# self.next = None
class Solution:
def EntryNodeOfLoop(self, pHead):
# write code here
if not pHead or not pHead.next :
return None
# 快慢指针
slow = pHead
fast = pHead
# 重新开始
start = pHead
while fast and fast.next:
slow = slow.next
fast = fast.next.next
#找到相遇点
#根据初始点到环入口的步数s等于相遇点到环入口的步数(r-m)加上环的长度r的整数倍进行while判断
#即s=(n-1)r+(r-m)
if slow == fast:
while slow != start:
slow = slow.next
start = start.next
return slow #返回的是该节点的数值
扩展题目:判断 链表 是否是环形链表
题目来自leetcode(https://leetcode-cn.com/problems/linked-list-cycle/)
题目描述:给定一个链表,判断链表中是否有环。为了表示给定链表中的环,我们使用整数 pos 来表示链表尾连接到链表中的位置(索引从 0 开始)。 如果 pos 是 -1,则在该链表中没有环。
思路:和第8题类似,只是返回的是True或False。这里不需要满足第8题的约束条件,因为对于快慢指针而言,不是"环形链表"的话,就不会相遇。
python
# Definition for singly-linked list.
# class ListNode(object):
# def __init__(self, x):
# self.val = x
# self.next = None
class Solution(object):
def hasCycle(self, head):
"""
:type head: ListNode
:rtype: bool
"""
if not head:
return False
walker = head
runner = head.next
try:
while walker!=runner:
walker = walker.next
runner = runner.next.next
return True
except:
return False
二叉搜索树与双向链表
题目描述:输入一棵二叉搜索树,将该二叉搜索树转换成一个排序的双向链表。要求不能创建任何新的结点,只能调整树中结点指针的指向。
思路:先进行中序遍历,然后改变链表的指针指向。
python
# -*- coding:utf-8 -*-
# class TreeNode:
# def __init__(self, x):
# self.val = x
# self.left = None
# self.right = None
class Solution:
def treeToList(self,root):
if not root:
return []
return self.treeToList(root.left)+[root]+self.treeToList(root.right)
def Convert(self, pRootOfTree):
# write code here
list1=self.treeToList(pRootOfTree)
if len(list1)==0:
return None
if len(list1)==1:
return pRootOfTree
for i in range(len(list1)-1):
list1[i].right=list1[i+1]
list1[i+1].left=list1[i]
return list1[0]
结尾
亲爱的读者朋友:感谢您在繁忙中驻足阅读本期内容!您的到来是对我们最大的支持❤️
正如古语所言:"当局者迷,旁观者清"。您独到的见解与客观评价,恰似一盏明灯💡,能帮助我们照亮内容盲区,让未来的创作更加贴近您的需求。
若此文给您带来启发或收获,不妨通过以下方式为彼此搭建一座桥梁: ✨ 点击右上角【点赞】图标,让好内容被更多人看见 ✨ 滑动屏幕【收藏】本篇,便于随时查阅回味 ✨ 在评论区留下您的真知灼见,让我们共同碰撞思维的火花
我始终秉持匠心精神,以键盘为犁铧深耕知识沃土💻,用每一次敲击传递专业价值,不断优化内容呈现形式,力求为您打造沉浸式的阅读盛宴📚。
有任何疑问或建议?评论区就是我们的连心桥!您的每一条留言我都将认真研读,并在24小时内回复解答📝。
愿我们携手同行,在知识的雨林中茁壮成长🌳,共享思想绽放的甘甜果实。下期相遇时,期待看到您智慧的评论与闪亮的点赞身影✨!
万分感谢🙏🙏您的点赞👍👍、收藏⭐🌟、评论💬🗯️、关注❤️💚~
自我介绍:一线互联网大厂资深算法研发(工作6年+),4年以上招聘面试官经验(一二面面试官,面试候选人400+),深谙岗位专业知识、技能雷达图,已累计辅导15+求职者顺利入职大中型互联网公司。熟练掌握大模型、NLP、搜索、推荐、数据挖掘算法和优化,提供面试辅导、专业知识入门到进阶辅导等定制化需求等服务,助力您顺利完成学习和求职之旅(有需要者可私信联系)
友友们,自己的知乎账号为**"快乐星球"**,定期更新技术文章,敬请关注!