代码随想录算法训练营第四天| 24. 两两交换链表中的节点,19.删除链表的倒数第N个节点,链表相交,142.环形链表II
24. 两两交换链表中的节点
题目链接:两两交换链表中的节点
这个题想起来还比较容易的,就是交换节点,但是写代码的时候还是很容易绕不出去,我感觉还是少出现cur.next.next.next
会很乱,然后调整的时候也可以把后面的链条存下来去操作。
class Solution:
def swapPairs(self, head: Optional[ListNode]) -> Optional[ListNode]:
node = ListNode(next=head)
cur = node
while cur.next and cur.next.next:
tmp = cur.next
post = cur.next.next
tmp.next = post.next
post.next = tmp
cur.next = post
cur = cur.next.next
return node.next
19.删除链表的倒数第N个节点
题目链接:删除链表的倒数第N个节点
这题我自己的做法是直接把倒数第几个算成正数第几个,然后用昨天的删除元素来做,所以先算了一共有几个元素,然后再算正数。
class Solution:
def removeNthFromEnd(self, head: Optional[ListNode], n: int) -> Optional[ListNode]:
def long(head):
l = 0
while head:
l += 1
head = head.next
return l
l = long(head)
dummy_head = ListNode(next=head)
cur = dummy_head
num = l-n
for _ in range(num):
cur = cur.next
cur.next = cur.next.next
return dummy_head.next
链表相交
题目链接:链表相交
=。=这道题想的太多了,没考虑到他自己的特性,最后都准备暴力遍历了,但是没有必要,可以看一下这个题的特点,两个链表相交之后的那一段到结尾就是一样的了,所以完全去想长的那段怎么等短的那段,完全没有必要。
关于例子:[4,1,8,4,5]和[5,0,1,8,4,5]的相交点为什么是8不是1。因为相交不是数值相同,而是指针相同(指向同一位置且指针本身地址相同),4.next != 0.next
。
直接让他们在尾巴上对齐了从红线开始一起next知道找到(最后一个)就可以了。
还有一个点是谁长谁短,也不用管,直接任命长的叫A短的叫B就行。
class Solution:
def getIntersectionNode(self, headA: ListNode, headB: ListNode) -> ListNode:
def long(head):
l = 0
while head:
l += 1
head = head.next
return l
lA = long(headA)
lB = long(headB)
# 这里不用管谁是长的谁是短的,直接规定A是长的那个
if lA > lB:
d = lA - lB
curA = headA
curB = headB
else:
d = lB - lA
curA = headB
curB = headA
for _ in range(d):
curA = curA.next
while curA and curB:
if curA == curB:
return curA
else:
curA = curA.next
curB = curB.next
return None
142.环形链表II
题目链接:环形链表II
题目解析参考这个视频,卡哥讲的真的特别好。
首先就是把这个题分成两部分:
- 链表里有没有环
- 环的起点在哪里
链表里有没有环
这个问题用的是双指针,可以做成一个追击问题,一个fast指针和一个slow指针,fast指针每次走两个节点,slow指针每次走一个节点,在没有环的情况下,fast到达结尾None,无法相遇。
在有环的情况下,如果按照相对运动来说,在slow相对静止的情况下,fast以每次一个节点的速度靠近slow,这也是为什么他们一定会相遇的原因。
这里如果fast以三个节点的速度往前的话,对slow来说就是两个节点的相对运动,那就有可能会直接越过,不会相遇。
环的起点在哪里
Notation:x = 从head到环的起点;y = 环的起点到相遇位置;z = 相遇位置到环的起点。
fast和slow同时从head出发,fast先进入环内,继续往前走,此时的slow还在直线上(应该是想x/2的位置),然后slow走到环的起点,此时的fast应该在环内的某个位置(可能已经在环里转了几圈了)。
依然是从相对运动的角度来说,slow相对禁止停在环的起点,fast以一个节点的速度去靠近slow,那么fast将走过一部分的环回到环的起点遇到slow,而原始设定下slow就是以一个节点为速度运动,fast以两个节点为速度运动,也就是说slow走过了刚刚fast在相对运动中走过的那一部分环的距离就可以和fast相遇,这就是为什么slow和fast在一圈之内一定会相遇的原因。
接下来就可以写一个式子:
- slow: 1 × t = x + y 1 \times t = x+y 1×t=x+y
- fast: 2 × t = x + y + n ( y + z ) 2 \times t = x+y+n(y+z) 2×t=x+y+n(y+z) (n是fast转过的圈数)
- 最后得到: 2 × ( x + y ) = x + y + n ( y + z ) 2 \times (x+y)= x+y+n(y+z) 2×(x+y)=x+y+n(y+z)
- 整理一下: x + y = n ( y + z ) → x = ( n − 1 ) ( y + z ) + z x+y=n(y+z) \rightarrow x = (n-1)(y+z)+z x+y=n(y+z)→x=(n−1)(y+z)+z
首先我们先来看
n
=
1
n=1
n=1的情况,也就是
x
=
z
x=z
x=z,这个是式子可以解释为,如果有两个指针,
i
n
d
e
x
1
index_1
index1和
i
n
d
e
x
2
index_2
index2,
i
n
d
e
x
1
index_1
index1从head,
i
n
d
e
x
2
index_2
index2从相遇点以同一个速度出发,他们将在环的起点相遇。
那么当n取别的值时,情况是一样的,
i
n
d
e
x
1
index_1
index1从head,
i
n
d
e
x
2
index_2
index2从相遇点以同一个速度出发,
i
n
d
e
x
1
index_1
index1走直线到环的起点,
i
n
d
e
x
2
index_2
index2转了
n
−
1
n-1
n−1圈最后也到了环的起点,因为距离相等他们最终相遇。
写一下代码(感觉这个题还挺浪漫的):
# Definition for singly-linked list.
# class ListNode:
# def __init__(self, x):
# self.val = x
# self.next = None
class Solution:
def detectCycle(self, head: Optional[ListNode]) -> Optional[ListNode]:
fast = head
slow = head
while fast and fast.next: #fast每次走两个节点所以next也管
fast = fast.next.next
slow = slow.next
if fast == slow:
index2 = fast
index1 = head
while index1 != index2:
index1 = index1.next
index2 = index2.next
return index1
return None