链表中倒数第k个节点
输入一个链表,输出该链表中倒数第k个结点。
借助列表存储法
class Solution:
def FindKthToTail(self, head, k):
if not head:
return None
a=[]
while head:
a.append(head)
head=head.next
if k>len(a) or k<1:
return None
return a[-k]
- 快慢指针法
单链表没有办法从后往前遍历节点,当我们从头开始遍历的时候,倒数第k个节点即正数第n-k+1个节点,但这样需要知道链表的长度,所以总共需要两次遍历。
快慢指针的方法即用两个指针同时指向头结点,快指针先走k-1步,然后快慢指针同时开始走,一直相隔k-1个指针,当快指针走到结尾的时候,慢指针刚好为倒数第k个节点。
这里有一个需要注意的点即k值不要超过链表长度。
- 算法注意点:
算法我们将快指针往前走了k步,这样可以判断k是否大于链表的长度,即遍历过程中看p1是否存在,不存在即大于返回空。
在慢指针走的过程中,由于我们的while 循环是以p1为判断条件,所以当p1.next不存在了即p1走到了最后一个节点,还是会再循环一次,这个和上边的p1先走k-1步就对应上了。即我们的算法p1多走了1步,但是下边的循环也使得p2多走了一步,所以是对应的。
class Solution:
def FindKthToTail(self, head, k):
p1=p2=head
for i in range(k):
if not p1:
return None
p1=p1.next
while p1:
p1=p1.next
p2=p2.next
return p2
反转链表
输入一个链表,反转链表后,输出新链表的表头。
- 解法思路
相当于加了一个指针,last=None,我们一直让last做phead的下一个节点,即phead.next=last,然后再把phead变为last,phead=phead.next这样循环
class Solution:
# 返回ListNode
def ReverseList(self, phead):
if not phead or not phead.next:
return phead
last=None
while phead:
temp=phead.next
phead.next=last
last=phead
phead=temp
return last
链表中环的入口节点
给一个链表,若其中包含环,请找出该链表的环的入口结点,否则,输出null。
- 解题思路:
第一是确定链表是否包含环。运用快慢指针,快指针走两步,慢指针走一步,如果两个的值会相等,即说明链表包含环。
第二步如何找到环的入口。如果链表的环有n个节点,快指针先走n步,然后快慢指针同时走,直到相遇的那个节点就是环的入口节点。
第三步如何得到环中节点的数目。在判断环的基础上,在遇到相等的节点后,快慢指针继续走,直到再回到最初相等的节点,便可得到环中节点数了。
- 算法思路:
//先说个定理:两个指针一个fast、一个slow同时从一个链表的头部出发 //fast一次走2步,slow一次走一步,如果该链表有环,两个指针必然在环内相遇 //此时只需要把其中的一个指针重新指向链表头部,另一个不变(还在环内), //这次两个指针一次走一步,相遇的地方就是入口节点。
class Solution:
def EntryNodeOfLoop(self, pHead):
if not pHead or not pHead.next:
return None
p1=p2=pHead
p2=pHead.next
p1=pHead.next.next
while p1.val!=p2.val:
p1=p1.next.next
p2=p2.next
p2=pHead
while p1.val!=p2.val:
p1=p1.next
p2=p2.next
return p2
删除链表中重复的节点
class Solution:
def deleteDuplication(self, pHead):
if not pHead or not pHead.next:
return pHead
new_head=ListNode(-1)
new_head.next=pHead
pre=new_head
p = pHead # cur node
next = None
while p and p.next:
next = p.next # later node
if p.val == next.val:
while next and next.val == p.val:
next = next.next
p = next
pre.next = p
else:
pre = p
p = next
return new_head.next
合并两个排序的链表
输入两个单调递增的链表,输出两个链表合成后的链表,当然我们需要合成后的链表满足单调不减规则。
- 递归解法: 主要思想就是每次的下一个节点都是比较后值小的那个节点,所以每次建立一个mergeHead=None,然后赋给它小的值。如果两个中有一个不存在了,那么就把剩下的合并。
class Solution:
#返回合并后列表
def Merge(self, pHead1, pHead2):
# write code here
if pHead1 == None:
return pHead2
elif pHead2 == None:
return pHead1
mergepHead = None
if pHead1.val <= pHead2.val:
mergepHead = pHead1
mergepHead.next = self.Merge(pHead1.next, pHead2)
elif pHead1.val > pHead2.val:
mergepHead = pHead2
mergepHead.next = self.Merge(pHead1, pHead2.next)
return mergepHead
- 非递归解法: 通过类建立一个新的头结点,每当这时候就要建立两个实例,一个用于循环,一个用于输出。
class Solution:
#返回合并后列表
def Merge(self, pHead1, pHead2):
# write code here
dummy = ListNode(0)
pHead = dummy
while pHead1 and pHead2:
if pHead1.val >= pHead2.val:
dummy.next = pHead2
pHead2 = pHead2.next
else:
dummy.next = pHead1
pHead1 = pHead1.next
dummy = dummy.next
if pHead1:
dummy.next = pHead1
elif pHead2:
dummy.next = pHead2
return pHead.next
两个链表的第一个公共结点
输入两个链表,找出它们的第一个公共结点。
- 解题思路:
两个单向链表在从第一个公共节点之后所有的节点都是重合的,不可能再出现分叉。拓扑形状看起来像一个Y。
所以说公共节点出现在链表的尾部,那么我们从后往前比较,遇到的第一个相同的节点就是要找的节点。符合“后进先出”原则,所以我们可以借助栈做辅助空间来比较,不过这样需要空间复杂度O(m+n)
之所以需要用到栈,是因为我们相同时遍历到链表的尾节点,当两个链表的长度不相同时,如果我们从头开始遍历,到达尾节点的时间就不一样。解决办法即:首先遍历两个链表得到他们的长度,在第二次遍历的时候,在较长的链表上先走若干步,接着同时在两个链表上遍历,找到的第一个相同的节点就是他们的第一个公共节点。
- 算法注意点
求长度时因为要移动节点的指针,所以直接求的话最终的P1、p2是为空的,所以把他放在一个函数里。
class Solution:
def FindFirstCommonNode(self, p1, p2):
if not p1 or not p2:
return None
len1=self.length(p1)
len2=self.length(p2)
sub=abs(len1-len2)
if len1>len2:
plong=p1
pshort=p2
else:
plong=p2
pshort=p1
for i in range(sub):
plong=plong.next
while plong!=None and pshort!=None and plong.val!=pshort.val:
plong=plong.next
pshort=pshort.next
return plong
def length(self,p):
length=0
while p:
p=p.next
length+=1
return length