「程式碼隨想錄演算法訓練營」第四天 | 連結串列 part2

云雀AC了一整天發表於2024-07-06

24.兩兩交換連結串列中的節點

題目連結:https://leetcode.cn/problems/swap-nodes-in-pairs/
題目難度:中等
文章講解:https://programmercarl.com/0024.兩兩交換連結串列中的節點.html#演算法公開課
影片講解: https://www.bilibili.com/video/BV1YT411g7br
題目狀態:有思路,但細節缺乏考慮

個人思路:

  1. 首先判斷head和其下一節點是否是nullptr,若是,就直接返回head
  2. 建立兩個指標firstsecond,再建立一個輔助指標tmp用於儲存second的下一節點;
  3. 在迴圈中利用tmp指標,將firstsecond交換;
  4. 返回head

出現的問題:

  1. 迴圈的結束條件比較難以設定;
  2. 當返回head時,只是從交換後的連結串列的第二節點返回,需要再使用一個節點用來指向第一節點。

修改思路:

新增一個哨兵指標dummy指向head,並且改變輔助指標tmp的指向,指向first的前一節點。

最終程式碼:

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     ListNode *next;
 *     ListNode() : val(0), next(nullptr) {}
 *     ListNode(int x) : val(x), next(nullptr) {}
 *     ListNode(int x, ListNode *next) : val(x), next(next) {}
 * };
 */
class Solution {
public:
    ListNode* swapPairs(ListNode* head) {
        if(head == nullptr || head->next == nullptr) return head;

        ListNode dummy(0);
        dummy.next = head;
        ListNode *prev = &dummy;
        while(prev->next != nullptr && prev->next->next != nullptr) {
            ListNode *first = prev->next;
            ListNode *second = first->next;
            prev->next = second;
            first->next = second->next;
            second->next = first;
            prev = first;
        }
        return dummy.next;
    }
};

19.刪除連結串列的倒數第N個節點

題目連結:https://leetcode.cn/problems/remove-nth-node-from-end-of-list/
題目難度:中等
文章講解:https://programmercarl.com/0019.刪除連結串列的倒數第N個節點.html
影片講解: https://www.bilibili.com/video/BV1vW4y1U7Gf
題目狀態:透過,有思路,且細節把控方面比上題強

個人思路:

使用快慢指標進行遍歷。

  1. 不管三七二十一,先建立一個哨兵指標dummy
  2. 初始化兩個快慢指標:slowfast,指向dummy
  3. 先將fast向後遍歷n步,之後fastslow指標一起向後遍歷,直到fast->next == nullptr
  4. 當遍歷完成後,slow->next就是我們要刪除的節點,建立一個輔助指標tmp指向slow->next,將slow->next = slow->next->next,然後刪除tmp
  5. 返回dummy.next

最終程式碼:

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     ListNode *next;
 *     ListNode() : val(0), next(nullptr) {}
 *     ListNode(int x) : val(x), next(nullptr) {}
 *     ListNode(int x, ListNode *next) : val(x), next(next) {}
 * };
 */
class Solution {
public:
    ListNode* removeNthFromEnd(ListNode* head, int n) {
        ListNode dummy(0);
        dummy.next = head;
        ListNode *fast = &dummy;
        ListNode *slow = &dummy;
        for(int i = 0; i < n; ++i) {
            fast = fast->next;
        }
        while(fast->next != nullptr) {
            fast = fast->next;
            slow = slow->next;
        }
        ListNode *tmp = slow->next;
        slow->next = slow->next->next;
        delete tmp;
        return dummy.next;
    }
};

面試題 02.07.連結串列相交

題目連結:https://leetcode.cn/problems/intersection-of-two-linked-lists-lcci/
題目難度:簡單
文章講解:https://programmercarl.com/面試題02.07.連結串列相交.html
題目狀態:無法理解題目,看文章講解勉強透過

紗布題,根本不說人話,看了文章講解才看懂講的什麼意思,交點不是數值相同,而是指標相同

程式碼展示:

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     ListNode *next;
 *     ListNode(int x) : val(x), next(NULL) {}
 * };
 */
class Solution {
public:
    ListNode *getIntersectionNode(ListNode *headA, ListNode *headB) {
        ListNode *curA = headA;
        ListNode *curB = headB;
        int lenA = 0, lenB = 0;
        while(curA != nullptr) {
            lenA++;
            curA = curA->next;
        }
        while(curB != nullptr) {
            lenB++;
            curB = curB->next;
        }
        curA = headA;
        curB = headB;
        if(lenB > lenA) {
            swap(lenA, lenB);
            swap(curA, curB);
        }
        int gap = lenA - lenB;
        while(gap--) curA = curA->next;
        while(curA != nullptr) {
            if(curA == curB) return curA;
            curA = curA->next;
            curB = curB->next;
        }
        return nullptr;
    }
};

142. 環形連結串列 II

題目連結:https://leetcode.cn/problems/linked-list-cycle-ii/
題目難度:中等
文章講解:https://programmercarl.com/0142.環形連結串列II.html
影片講解: https://www.bilibili.com/video/BV1if4y1d7ob
題目狀態:之前在《劍指Offer》中看到過這個題,還記得大致思路,但是實際編寫程式碼的時候還是出現了一些問題,最終透過

思路:
總體上是使用兩次快慢指標解決

  1. 首先判斷其連結串列是不是環形的,採用快慢指標解決,當慢指標與快指標相遇時,表明該連結串列是環形的,返回相遇的節點;
  2. 利用相遇的節點繼續向下遍歷,當節點再一次與自己相等時,表明該節點圍繞了環形一圈,由此獲得環形的大小;
  3. 透過環形的大小,再次利用快慢指標從頭節點遍歷,當快指標和慢指標相等時,獲得了環形連結串列的入口節點。

⚠️注意:
這兩次快慢指標用法不同。

  1. 第一次使用的快慢指標中,快指標每次走兩步,慢指標每次走一步;
  2. 第二次使用的快慢指標中,快指標先走一段,之後快慢指標按照相同的速度向後遍歷。

具體程式碼:

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     ListNode *next;
 *     ListNode(int x) : val(x), next(NULL) {}
 * };
 */
class Solution {
public:
    ListNode *detectCycle(ListNode *head) {
        ListNode *CycleNode = InCycle(head);

        if(CycleNode == nullptr) return nullptr;
        int CycleNum = 1;
        for(ListNode *n = CycleNode; n->next != CycleNode; n = n->next) {
            CycleNum++;
        }

        ListNode *fast = head;
        for(int i = 0; i < CycleNum; ++i) fast = fast->next;
        ListNode *slow = head;
        while(fast != slow) {
            fast = fast->next;
            slow = slow->next;
        }
        return slow;
    }

    ListNode *InCycle(ListNode *head) {
        if(head == nullptr || head->next == nullptr) return nullptr;
        ListNode *slow = head->next;
        ListNode *fast = slow->next;
        while(slow != nullptr && fast != nullptr && fast->next != nullptr) {
            if(slow == fast) return slow;
            slow = slow->next;
            fast = fast->next->next;
        }
        return nullptr;
    }
};

連結串列總結(來自程式碼隨想錄):

相關文章