程式碼隨想錄演算法訓練營第三天|203.移除連結串列元素,707.設計連結串列,206.反轉連結串列

W-Vicky11發表於2024-09-28

203.移除連結串列元素

文章連結:https://programmercarl.com/0203.移除連結串列元素.html#演算法公開課
影片講解:https://www.bilibili.com/video/BV18B4y1s7R9
題目出處:https://leetcode.cn/problems/remove-linked-list-elements/

卡哥在這裡講解了為什麼要使用虛擬頭節點,以及使用和不使用的區別,不清楚的話可以看影片講解

  • 方一:直接刪除頭節點
    寫的時候有幾個小問題需要注意,已經在程式碼中標出
class Solution {
public:
    ListNode* removeElements(ListNode* head, int val) {
        //不使用虛擬頭節點,直接刪除
        //刪除頭節點(頭節點特殊,刪除方式不一樣,直接將head後移一個節點)
        while(head!=NULL&&head->val==val){ //這裡一開始寫的時候用的if,但是如果head後面的節點依然符合要求,則會漏掉這種情況
            ListNode* tmp=head;
            head=head->next;
            delete tmp;
        }
        //刪除非頭節點
        ListNode* cur=head;
        while(cur!=NULL&&cur->next!=NULL){   //這裡一開始寫的時候沒有寫cur!=NULL,這樣是不行的,因為如果它為空了再操作會導致空指標錯誤
            if(cur->next->val==val){ 
                ListNode* tmp=cur->next;
                cur->next=cur->next->next;
                delete tmp;
            }
            else{
                cur=cur->next;
            }
        
        }
        return head;
    }
};
  • 方二:使用虛擬頭節點
    錯誤如下圖所示:

這裡會報錯的原因是 dummyHead 沒有被初始化。dummyHead 是一個指向 ListNode 型別的指標,但沒有給它分配記憶體空間。當前程式碼只宣告瞭一個指標,但它指向的是未定義的記憶體區域,因此在訪問 dummyHead->next 時會導致未定義行為或崩潰。
所以這裡應該寫成:

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

總的程式碼如下所示:

class Solution {
public:
    ListNode* removeElements(ListNode* head, int val) {
        //使用虛擬頭節點
        ListNode* dummyHead=new ListNode(0); //注意將指標初始化
        dummyHead->next=head;
        ListNode* cur=dummyHead;
        while(cur->next!=NULL){
            if(cur->next->val==val){
                ListNode* tmp=cur->next;
                cur->next=cur->next->next;
                delete tmp;
            }
            else{
                cur=cur->next;
            }
        }
        head=dummyHead->next;
        return head;
    }
};
  • 方三:使用遞迴也很有意思
class Solution {
public:
    ListNode* removeElements(ListNode* head, int val) {
        //使用遞迴
        if(head==nullptr){
            return nullptr;
        }
        
        if(head->val==val){
            ListNode* newHead=removeElements(head->next,val);
            delete head;
            return newHead;
        }
        else{
            head->next=removeElements(head->next,val); //這裡一開始沒有想到head->next
            return head;
        }
    }
};

707.設計連結串列

文章連結:https://programmercarl.com/0707.設計連結串列.html
影片連結:https://www.bilibili.com/video/BV1FU4y1X7WD
題目連結:https://leetcode.cn/problems/design-linked-list/description/

先構造節點,併為類寫建構函式進行初始化。

class MyLinkedList {
public:
    //定義節點
    struct LinkNode{
        int val;
        LinkNode* next;
        LinkNode():next(nullptr){}//建構函式
        LinkNode(int val):val(val),next(nullptr){}//過載建構函式
    };
    MyLinkedList() { //建構函式
        dummyHead=new LinkNode();
        size=0;
    }
private:
    int size;
    LinkNode* dummyHead;
};

以下為學校內學到的寫法:

  • 頭插法:
void addAtHead(int val) {
        LinkNode* s=new LinkNode(val);
        s->next=dummyHead->next;
        dummyHead->next=s;
    }
  • 尾插法:
void addAtTail(int val) {
        LinkNode* s=new LinkNode(val);
        LinkNode* r=dummyHead;
        while(r->next!=NULL){
            r=r->next;
        }
        r->next=s;
        size++;
    }
  • 在index之前插入
void addAtIndex(int index, int val) {
        if(index>size||index<0){
            return;
        }
        LinkNode* cur=dummyHead;
        while(index--){
            cur=cur->next;
        }
        LinkNode* s=new LinkNode(val);
        s->next=cur->next;
        cur->next=s;
        size++;
    }
  • 獲取第index個節點值
int get(int index) {
        if(index>(size-1)||index<0) return -1;
        LinkNode* cur=dummyHead->next;
        while(index--){
            cur=cur->next;
        }
        return cur->val;
    }
  • 刪除第index個節點
void deleteAtIndex(int index) {
        if(index<0||index>=size) return;
        LinkNode* cur=dummyHead;
        while(index--){
            cur=cur->next;
        }
        LinkNode* tmp= cur->next;
        cur->next=cur->next->next;
        delete tmp;
        tmp=nullptr;//這裡最好將其賦為空指標
        //delete命令指示釋放了tmp指標原本所指的那部分記憶體,
        //被delete後的指標tmp的值(地址)並非就是NULL,而是隨機值。也就是被delete後,
        //如果不再加上一句tmp=nullptr,tmp會成為亂指的野指標
        //如果之後的程式不小心使用了tmp,會指向難以預想的記憶體空間
        size--;
    }

206.反轉連結串列

  • 雙指標的寫法:
class Solution {
public:
    ListNode* reverseList(ListNode* head) {
        ListNode* cur=head;
        ListNode* pre=nullptr;
        while(cur!=NULL){
            ListNode* tmp=cur->next;
            cur->next=pre;
            pre=cur;
            cur=tmp;
        }
        return pre;
    }
};
  • 遞迴寫法:
class Solution {
public:
    ListNode* reverse(ListNode* pre,ListNode*cur){
        // if(cur!=NULL){
        //     ListNode* tmp=cur->next;
        //     cur->next=pre;
        //     reverse(cur,tmp);
        // }
        // else return pre;
        if(cur==NULL) return pre;
        ListNode* tmp=cur->next;
        cur->next=pre;
        return reverse(cur,tmp);
    }
    ListNode* reverseList(ListNode* head) {
        ListNode* pre=nullptr;
        ListNode* cur=head;
        return reverse(pre,cur);
    }
};

第一次寫,寫成了註釋掉的那部分,說明對遞迴還不太熟

相關文章