代码随想录算法训练营第四天| 24. 两两交换链表中的节点、19.删除链表的倒数第N个节点、面试题 02.07. 链表相交

76 阅读4分钟

24. 两两交换链表中的节点 - 力扣(LeetCode)

1. 文章链接

代码随想录 (programmercarl.com)

2. 看到题目的第一想法

还是用指针操作。

起初pre_pt, cur_pt, next_pt三个按照顺序指向数组前三个节点。

pre_ptcur_pt之间的连接做反向。

pre_pt.next指向next

pre_ptcur_pt移动到nextnext.next

但是问题的难点在于,有可能是奇数个节点的链表。

那就要判断了,在pre_ptcur_pt整体移动时:

先移动pre_pt, 如果pre_ptNone的话,就是偶数个节点,且已经遍历完了,所以也直接结束。

再移动cur_pt, 如果cur_ptNone的话,就是奇数个节点,直接结束。

3. 实现过程中遇到的困难

cur_pt.next指向pre_ptpre_pt.next指向next_pt后,没有指针指向cur_pt

需要上一对pre_ptcur_pt翻转完成后,pre_pt.next指向的是next_pt.next,而非next_pt

链表这个题非常麻烦。

必须要想清楚pre_pt, cur_pt, next_pt三个指针之间的关系。

而且要使用dummy_head

pre_pt:指向互换节点对的前一个节点。

cur_pt:指向互换节点对的第一个节点。

next_pt:指向互换节点对的第二个节点。

互换的循环体中:

  1. pre_pt.next指向next_pt
  2. cur_pt.next指向next_pt.next
  3. next_pt.next指向cur_pt 这里需要着重注意的是这三步的顺序,一定要先做2.再做3.。否则会丢失next_pt.next

另外向前移动指针时,pre_pt, cur_pt两个都要移动。


# Definition for singly-linked list.
# class ListNode:
#     def __init__(self, val=0, next=None):
#         self.val = val
#         self.next = next
class Solution:
    def swapPairs(self, head: Optional[ListNode]) -> Optional[ListNode]:
        if head == None:
            return head
        dummy_head = ListNode(val=-1, next=head)
        pre_pt = dummy_head
        cur_pt = pre_pt.next
        while cur_pt.next:
            next_pt = cur_pt.next
            pre_pt.next = next_pt
            cur_pt.next = next_pt.next
            next_pt.next = cur_pt
            pre_pt = cur_pt
            cur_pt = pre_pt.next
            if cur_pt == None:
                break
            
            
        return dummy_head.next

4. 看完代码随想录之后的想法

我觉得文档中使用的两个判断条件更简单。

而且做法更符合人类思维。


# Definition for singly-linked list.
# class ListNode:
#     def __init__(self, val=0, next=None):
#         self.val = val
#         self.next = next
class Solution:
    def swapPairs(self, head: Optional[ListNode]) -> Optional[ListNode]:
        dummy_head = ListNode(val=-1, next=head)
        cur_pt = dummy_head
        while (cur_pt.next != None) and (cur_pt.next.next != None):
            # 有两个节点就满足交换条件了。
            num1 = cur_pt.next
            num2 = cur_pt.next.next.next  # 之后节点对的num1
            cur_pt.next = cur_pt.next.next
            cur_pt.next.next = num1
            num1.next = num2
            # 移动cur_pt
            cur_pt = cur_pt.next.next
        return dummy_head.next

5. 学习时长

初次实现:2小时。

19. 删除链表的倒数第 N 个结点 - 力扣(LeetCode)

1. 文章链接

代码随想录 (programmercarl.com)

2. 看到题目的第一想法

我觉的一个简单的想法是:

  1. 先遍历一遍,统计链表的总长度。
  2. 计算是正数第几个元素。
  3. 再遍历到那个元素的位置,然后删除该元素即可。

3. 实现过程中遇到的困难

4. 看完代码随想录之后的想法

我隐约感觉我的第一个想法不是最好的。 所以先看了文档再实现。 果然:

  1. 快慢指针
  2. 快指针和慢指针差n+1n+1步。 使得当快指针走到None的时刻,慢指针就刚好到了要删除节点的前一个节点。 举个例子:
[1, 2, 3, 4, 5] n=2
Slow=1, fast=4
Slow=2, fast=5
Slow=3, fast=None
Delete 4

# Definition for singly-linked list.
# class ListNode:
#     def __init__(self, val=0, next=None):
#         self.val = val
#         self.next = next
class Solution:
    def removeNthFromEnd(self, head: Optional[ListNode], n: int) -> Optional[ListNode]:
        dummy_head = ListNode(val=0, next=head)
        fast_pt = dummy_head
        slow_pt = dummy_head
        for i in range(n + 1):
            fast_pt = fast_pt.next
        while fast_pt:
            fast_pt = fast_pt.next
            slow_pt = slow_pt.next
        # delete
        slow_pt.next = slow_pt.next.next
        return dummy_head.next
            

5. 学习时长

40分钟。

面试题 02.07. 链表相交 - 力扣(LeetCode)

1. 文章链接

代码随想录 (programmercarl.com)

2. 看到题目的第一想法

至少应该要从headAheadB发出两个指针,用来向前推进。

统计两条路的长度,做差得到一条路比另一条路多多少节点。

让路长的那个先走完多出的节点个数。

然后再一起推进,判断id()是否一致,一致说明有交点,返回即可,否则返回None。


# Definition for singly-linked list.
# class ListNode:
#     def __init__(self, x):
#         self.val = x
#         self.next = None

class Solution:
    def getIntersectionNode(self, headA: ListNode, headB: ListNode) -> ListNode:
        a_pt = headA
        b_pt = headB
        is_a_longer = False
        is_b_longer = False
        diff = 0
        while a_pt and b_pt:
            a_pt = a_pt.next
            b_pt = b_pt.next
        while a_pt:
            a_pt = a_pt.next
            diff += 1
            is_a_longer = True
        while b_pt:
            b_pt = b_pt.next
            diff += 1
            is_b_longer = True
        a_pt = headA
        b_pt = headB
        if is_a_longer:
            while diff > 0:
                a_pt = a_pt.next
                diff -= 1
        if is_b_longer:
            while diff > 0:
                b_pt = b_pt.next
                diff -= 1
        while a_pt:
            if id(a_pt) == id(b_pt):
                break
            a_pt = a_pt.next
            b_pt = b_pt.next
        return a_pt

3. 实现过程中遇到的困难

无。

4. 看完代码随想录之后的想法

跟我思路一样,只不过是后期固定了较长链表为a_pt

5. 学习时长

40分钟。

142. 环形链表 II - 力扣(LeetCode)

1. 文章链接

代码随想录 (programmercarl.com)

2. 看到题目的第一想法

快慢指针,一个快指针一次走两步,一个慢指针一次走一步。

根据这个速度差一只做,如果两个指针在快指针为None前碰到了,那就说明有环。

碰到的点就是入环的第一个节点。(❌)

3. 实现过程中遇到的困难

相遇点并不是环的入口!!!

4. 看完代码随想录之后的想法

相遇点时刻:

slow_pt的路程=x+yx + y

fast_pt的路程=x+n(y+z)+yx + n(y + z) + y

两个路程是二倍的关系。

所以最后得到x=(n1)(y+z)+zx=(n-1)(y + z) + z

所以只要有一个指针从相遇点出发,一个指针从head出发,两个指针同速一直往前走,从相遇点出发的指针在转了若干圈并额外走了一个zz之后,一定会遇从head出发的指针在距离head为xx的位置相遇。

即我们所求的xx号节点位置。


# 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_pt = head
        slow_pt = head
        while fast_pt and fast_pt.next:
            fast_pt = fast_pt.next.next
            slow_pt = slow_pt.next
            if id(fast_pt) == id(slow_pt):
                break
        if fast_pt == None or fast_pt.next == None:
            return None
        a_pt = head
        b_pt = fast_pt
        while a_pt:
            if id(a_pt) == id(b_pt):
                break
            a_pt = a_pt.next
            b_pt = b_pt.next
        return a_pt
            

5. 学习时长

1小时。