算法随想录之链表的又一天
两两交换链表中的节点
给定一个链表,两两交换其中相邻的节点,并返回交换后的链表。不能只是单纯的改变节点内部的值,而是需要实际的进行节点交换。
我感觉这道题首先应该明确cur初始化指向虚拟头节点还是头节点,可以手画链表结构图知道应该是指向虚拟头节点才能进行后续两两节点的交换。还需要注意的就是遍历条件的确定,由于我们是从虚拟头节点开始遍历,交换往后两个节点,也就是说当cur指向某个节点,它后面没有节点(链表节点数为偶数)或者只有一个节点(链表节点数为奇数)时则意味着遍历结束。即,遍历条件为cur.next不为null且cur.next.next不为null。(ps:cur.next的判断要在cur.next.next判断之前以防止出现空指针异常)
然后就是交换过程,按如下的步骤进行交换,且cur指针更新规则为cur=cur.next.next。
class Solution(object):
def swapPairs(self, head):
"""
:type head: ListNode
:rtype: ListNode
"""
dummy_head = ListNode(next=head)
cur = dummy_head
while cur.next and cur.next.next:
tmp1 = cur.next
tmp2 = cur.next.next.next
cur.next = cur.next.next #步骤一
cur.next.next = tmp1 #步骤二
tmp1.next = tmp2 #步骤三:注意这里是tmp1.next而不是cur.next(因为此时cur.next已经改变)
cur = cur.next.next
return dummy_head.next
将步骤二与步骤三顺序调换一下,可以只用一个临时指针。
class Solution(object):
def swapPairs(self, head):
"""
:type head: ListNode
:rtype: ListNode
"""
dummy_head = ListNode(next=head)
cur = dummy_head
while cur.next and cur.next.next:
tmp1 = cur.next
cur.next = cur.next.next #步骤一
tmp1.next = tmp1.next.next #步骤三:注意这里是tmp1.next而不是cur.next(因为此时cur.next已经改变)
cur.next.next = tmp1 #步骤二
cur = cur.next.next
return dummy_head.next
删除链表的倒数第N个节点
给定一个链表,删除链表的倒数第 n 个结点,并且返回链表的头结点。
这几天以来第一道独立思考做出来的题目,感觉妙不可言!鼓掌啪啪啪(咳咳咳注意戒骄戒躁)
class Solution(object):
def removeNthFromEnd(self, head, n):
"""
:type head: ListNode
:type n: int
:rtype: ListNode
"""
dummy_head = ListNode(next=head)
cur = dummy_head
while cur.next:
cur_tmp = cur
for _ in range(n):
cur_tmp = cur_tmp.next
if cur_tmp.next == None:
cur.next = cur.next.next
break
cur = cur.next
return dummy_head.next
做到这儿,感觉链表的题目最重要的一点就是画图,能够清晰地知道算法过程,还有就是明确指针的初始化以及遍历条件。这样,基本就没多大问题了。(如果后面的题目没做出来,当我没说)
链表相交
两个单链表的头节点headA和headB,找出并返回两个单链表相交的起始节点。如果两个链表没有交点,返回null。
这里的逻辑:两个链表相交,在相交节点之前有多少个节点是未知的,但是从相交节点到尾节点对于两个链表来说是完全相等的。也就是说,我们可以求出两个链表的长度,对于长链表来说,只用从后一段长度为短链表那么长的地方开始遍历。也可以画种思路理解:从头到相交节点的数量不确定,但是从尾到相交节点的两个链表是完全一致的。
基于以上分析,我们得到长链表的初始遍历节点后,然后令短链表从头开始,同时遍历两个链表,直到两个链表的当前节点相等即为相交节点。
class Solution(object):
def getIntersectionNode(self, headA, headB):
"""
:type head1, head1: ListNode
:rtype: ListNode
"""
## 获取链表长度
cur = headA
lenA = 0
while cur:
lenA += 1
cur = cur.next
cur = headB
lenB = 0
while cur:
lenB += 1
cur = cur.next
curA = headA
curB = headB
## 令B链表固定为长链表
if lenA > lenB:
curA,curB = curB,curA
lenA,lenB = lenB,lenA
## 获取长链表的起始遍历节点
for _ in range(lenB-lenA):
curB = curB.next
## 遍历长短链表节点直到出现相等节点即为相交节点
while curA:
if curA == curB:
return curA
curA = curA.next
curB = curB.next
return None
环形链表(好难)
给定一个链表的头节点 head ,返回链表开始入环的第一个节点。 如果链表无环,则返回null。
【开始这道题的复盘之前,需要做足万全的心理准备,我在看卡哥视频讲解的时候,就觉得有一种深深的无力感,我觉得这种逻辑我自己完全没有独立思考出来的能力,而且可能遇到类似思想的题目极大可能还是做不出来。】
首先,两个步骤:
- 判断是否有环:用两个指针,一个每次跨越两个节点快指针,一个每次跨一个节点的慢指针,那么如果有环的话这两个指针一定会相遇。
- 判断环的入口:假设头节点到环入口节点数为x,环入口节点到两个指针相遇节点数为y,相遇节点到环形入口节点数为z。(如下图所示为快慢指针走过的节点数,且快指针为慢指针速度的两倍,即x+y+n(y+z)=2(x+y),化简可得x=(n-1)(y+z)+z,即x等于若干圈环形加上z。)
敲重点敲重点: x等于若干圈环形加上z,那么在头节点和相遇节点分别定义两个指针,逐节点遍历相遇的点即为环形入口节点。(妙不妙咱们就是说,看到这有一种酣畅淋漓之后的无力感,感慨这里算法逻辑,以我这颗平平无奇的大脑是没办法想出来的。)
class Solution(object):
def detectCycle(self, head):
"""
:type head: ListNode
:rtype: ListNode
"""
fast = head
slow = head
while fast and fast.next:
fast = fast.next.next
slow = slow.next
if fast == slow:
index1 = fast #相遇节点
index2 = head #头节点
while index1 != index2:
index1 = index1.next
index2 = index2.next
return index1
return None