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

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

203.移除連結串列元素

題目連結:https://leetcode.cn/problems/remove-linked-list-elements/
題目難度:簡單
文章講解:https://programmercarl.com/0203.移除連結串列元素.html
影片講解: https://www.bilibili.com/video/BV18B4y1s7R9
題目狀態:透過

個人思路:

從連結串列頭部開始依次往下遍歷,當遇到和val值相等的元素就將其移除(walk->next = walk->next->next),最後返回修改後的連結串列。

遇到的問題:

  1. 直接將遍歷指標walk初始化在了連結串列頭,沒能考慮到頭節點就是需要移除的節點的情況。
  2. 採用 C++ 編寫程式碼的時候,忘記要從記憶體中刪除移除的節點,清理節點記憶體

最終程式碼如下:

/**
 * 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* removeElements(ListNode* head, int val) {
        while(head != nullptr && head->val == val)
        {
            head = head->next;
        }

        ListNode *dummy = new ListNode(0);
        dummy->next = head;

        ListNode* walk = dummy;
        while(walk->next != nullptr)
        {
            if(walk->next->val == val)
            {
                ListNode *tmp = walk->next;
                walk->next = tmp->next;
                delete tmp;
            }
            else
            {
                walk = walk->next;
            }
        }
        ListNode *newHead = dummy->next;
        delete dummy;
        return newHead;
    }
};

707.設計連結串列

題目連結:https://leetcode.cn/problems/design-linked-list/
題目難度:中等
文章講解:https://programmercarl.com/0707.設計連結串列.html
影片講解: https://www.bilibili.com/video/BV1FU4y1X7WD
題目狀態:思路混亂,看卡哥程式碼

最終程式碼如下:

class MyLinkedList {
public:
    struct LinkedNode {
        int val;
        LinkedNode *next;
        
        LinkedNode(int val)
        : val(val)
        , next(nullptr)
        {}
    };

    MyLinkedList() {
        _dummyHead = new LinkedNode(0);
        _size = 0;
    }
    
    int get(int index) {
        if(index >= _size || index < 0) return -1;
        LinkedNode *cur = _dummyHead->next;
        while(index--) {
            cur = cur->next;
        }
        return cur->val;
    }
    
    void addAtHead(int val) {
        LinkedNode *newNode = new LinkedNode(val);
        newNode->next = _dummyHead->next;
        _dummyHead->next = newNode;
        _size++;
    }
    
    void addAtTail(int val) {
        LinkedNode *newNode = new LinkedNode(val);
        LinkedNode *cur = _dummyHead;
        while(cur->next != nullptr) {
            cur = cur->next;
        }
        cur->next = newNode;
        _size++;
    }
    
    void addAtIndex(int index, int val) {
        if(index > _size) return;
        if(index < 0) index = 0;

        LinkedNode *cur = _dummyHead;
        LinkedNode *newNode = new LinkedNode(val);
        while(index--) {
            cur = cur->next;
        }
        newNode->next = cur->next;
        cur->next = newNode;
        _size++;

    }
    
    void deleteAtIndex(int index) {
        if(index >= _size || index < 0) return;
        LinkedNode *cur = _dummyHead;
        while(index--) {
            cur = cur->next;
        }
        LinkedNode *tmp = cur->next;
        cur->next = tmp->next;
        delete tmp;
        tmp = nullptr;
        _size--;
    }
private:
    int _size;
    LinkedNode *_dummyHead;
};

/**
 * Your MyLinkedList object will be instantiated and called as such:
 * MyLinkedList* obj = new MyLinkedList();
 * int param_1 = obj->get(index);
 * obj->addAtHead(val);
 * obj->addAtTail(val);
 * obj->addAtIndex(index,val);
 * obj->deleteAtIndex(index);
 */

⚠️注意:

deleteAtIndex介面中使用delete釋放了tmp指標原本所指的那部分記憶體,但是被delete後的指標tmp的值並不是nullptr,而是隨機值,即tmp成為了野指標,因此需要將tmp = nullptr

206.反轉連結串列

題目連結:https://leetcode.cn/problems/reverse-linked-list/
題目難度:簡單
文章講解:https://programmercarl.com/0206.翻轉連結串列.html
影片講解: https://www.bilibili.com/video/BV1nB4y1i7eL
題目狀態:透過

個人思路:

構建一個新連結串列用於儲存反轉後的連結串列,再迴圈舊的連結串列,每迴圈到一個節點,就將該節點的值採用頭插法插入到新連結串列中,最後返回新連結串列。

實現程式碼:

/**
 * 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* reverseList(ListNode* head) {
        if(head == nullptr) return head;

        ListNode *newList = new ListNode();
        ListNode *cur = head;
        while(cur != nullptr) {
            ListNode *tmp = new ListNode(cur->val);
            tmp->next = newList->next;
            newList->next = tmp;
            cur = cur->next;
        }
        return newList->next;
    }
};

過是過了,但看著那兩個大大的new並且沒有delete我就噁心,感覺自己又寫了一坨……

其他思路:

1. 雙指標法

a. 直接定義兩個指標curprecur用於遍歷,pre用於儲存cur的前一個節點。
b. 每次遍歷到下一個節點的時候,cur就會將它的下一個節點的指向指到自己的前一個節點pre,最後更新自己到原本的下一個節點。

程式碼:

/**
 * 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* reverseList(ListNode* head) {
        if(head == nullptr) return head;

        ListNode *cur = head;
        ListNode *pre = nullptr;
        ListNode *tmp;
        while(cur != nullptr) {
            tmp = cur->next;
            cur->next = pre;
            pre = cur;
            cur = tmp;
        }
        return pre;
    }
};

2. 遞迴法

思路和雙指標法相同,程式碼如下:

/**
 * 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* reverseList(ListNode* head) {
        return reverse(nullptr, head);
    }
    ListNode *reverse(ListNode *pre, ListNode *cur) {
        if(cur == nullptr) return pre;
        ListNode *tmp = cur->next;
        cur->next = pre;
        return reverse(cur, tmp);
    }
};

還有種思路但是沒看懂:

從後往前翻轉指標指向

class Solution {
public:
    ListNode* reverseList(ListNode* head) {
        // 邊緣條件判斷
        if(head == NULL) return NULL;
        if (head->next == NULL) return head;
        
        // 遞迴呼叫,翻轉第二個節點開始往後的連結串列
        ListNode *last = reverseList(head->next);
        // 翻轉頭節點與第二個節點的指向
        head->next->next = head;
        // 此時的 head 節點為尾節點,next 需要指向 NULL
        head->next = NULL;
        return last;
    }
}; 

相關文章