1. 程式人生 > >[LeetCode] Design Linked List 設計連結串列 LeetCode All in One 題目講解彙總(持續更新中...)

[LeetCode] Design Linked List 設計連結串列 LeetCode All in One 題目講解彙總(持續更新中...)

 

Design your implementation of the linked list. You can choose to use the singly linked list or the doubly linked list. A node in a singly linked list should have two attributes: val and nextval is the value of the current node, and next is a pointer/reference to the next node. If you want to use the doubly linked list, you will need one more attribute prev

 to indicate the previous node in the linked list. Assume all nodes in the linked list are 0-indexed.

Implement these functions in your linked list class:

  • get(index) : Get the value of the index-th node in the linked list. If the index is invalid, return -1.
  • addAtHead(val) : Add a node of value val
     before the first element of the linked list. After the insertion, the new node will be the first node of the linked list.
  • addAtTail(val) : Append a node of value val to the last element of the linked list.
  • addAtIndex(index, val) : Add a node of value val before the index
    -th node in the linked list. If index equals to the length of linked list, the node will be appended to the end of linked list. If index is greater than the length, the node will not be inserted.
  • deleteAtIndex(index) : Delete the index-th node in the linked list, if the index is valid.

Example:

MyLinkedList linkedList = new MyLinkedList();
linkedList.addAtHead(1);
linkedList.addAtTail(3);
linkedList.addAtIndex(1, 2);  // linked list becomes 1->2->3
linkedList.get(1);            // returns 2
linkedList.deleteAtIndex(1);  // now the linked list is 1->3
linkedList.get(1);            // returns 3

Note:

  • All values will be in the range of [1, 1000].
  • The number of operations will be in the range of [1, 1000].
  • Please do not use the built-in LinkedList library.

 

這道題讓我們實現一個連結串列的資料結構,說我們不能使用現成的連結串列資料結構,需要自己定義結點,說是可以實現雙向或者單向的連結串列。既然有的選,那肯定選簡單的單項鍊表了。首先就是要先自己定義一個結點的資料結構了,好在LeetCode中有很多很多的連結串列有關的題目,隨便開啟一個,並且參考一下結點的定義即可。然後看需要實現的哪些函式,分別是根據座標取結點,分別在連結串列開頭和末尾加結點,根據座標位置加結點,根據座標位置刪除結點。既然是結點組成的連結串列,那麼肯定不能向陣列那樣可以根據座標直接訪問元素。那麼我們肯定至少要知道表頭的位置。同時,在根據連結串列取結點函式說明了給定的位置可能是非法的,那麼我們也要知道連結串列中所有元素的個數,這樣可以快速的判定給定的位置是否合法。

好,下面來看每個函式如何實現。首先來看根據座標取結點函式,先判定index是否合法,然後從表頭向後移動index個位置,找到要返回的結點即可。對於增加表頭函式就比較簡單了,新建一個頭結點,next連上head,然後head重新指向這個新結點,同時size自增1。同樣,對於增加表尾結點函式,首先遍歷到表尾,然後在之後連上一個新建的結點,同時size自增1。下面是根據位置來加結點,那麼肯定還是先來判定index是否合法,然後再處理一個corner case,就是當index為0的時候,直接呼叫前面的表頭加結點函式即可。然後就是往後遍歷index-1個結點,這裡為啥要減1呢,因為要加入結點的話,必須要知道加入位置前面一個結點才行,連結串列加入結點的問題之前的題目中做過很多,這裡就不說細節了,最後size還是要自增1。根據位置刪除結點也是大同小異,沒有太大的難度,參見程式碼如下:

 

解法一:

class MyLinkedList {
public:
    /** Initialize your data structure here. */
    MyLinkedList() {
        head = NULL;
        size = 0;
    }
    
    /** Get the value of the index-th node in the linked list. If the index is invalid, return -1. */
    int get(int index) {
        if (index < 0 || index >= size) return -1;
        Node *cur = head;
        for (int i = 0; i < index; ++i) cur = cur->next;
        return cur->val;
    }
    
    /** Add a node of value val before the first element of the linked list. After the insertion, the new node will be the first node of the linked list. */
    void addAtHead(int val) {
        Node *t = new Node(val, head);
        head = t;
        ++size;
    }
    
    /** Append a node of value val to the last element of the linked list. */
    void addAtTail(int val) {
        Node *cur = head;
        while (cur->next) cur = cur->next;
        cur->next = new Node(val, NULL);
        ++size;
    }
    
    /** Add a node of value val before the index-th node in the linked list. If index equals to the length of linked list, the node will be appended to the end of linked list. If index is greater than the length, the node will not be inserted. */
    void addAtIndex(int index, int val) {
        if (index < 0 || index > size) return;
        if (index == 0) {addAtHead(val); return;}
        Node *cur = head;
        for (int i = 0; i < index - 1; ++i) cur = cur->next;
        Node *t = new Node(val, cur->next);
        cur->next = t;
        ++size;
    }
    
    /** Delete the index-th node in the linked list, if the index is valid. */
    void deleteAtIndex(int index) {
        if (index < 0 || index >= size) return;
        if (index == 0) {
            head = head->next;
            --size;
            return;
        }
        Node *cur = head;
        for (int i = 0; i < index - 1; ++i) cur = cur->next;
        cur->next = cur->next->next;
        --size;
    }
    
private:
    struct Node {
        int val;
        Node *next;
        Node(int x, Node* n): val(x), next(n) {}
    };
    Node *head, *tail;
    int size;
};

 

我們可以對上面的解法做一丟丟的優化,主要在末尾新增結點函式addAtTail()上,如果要大量呼叫這個方法的話,每次都要遍歷到連結串列末尾,很不高效。於是我們可以同時記錄表頭和表尾的位置,這樣我們就可以直接訪問末尾結點了,缺點是在其他一些地方如果末尾元素改變了,要更新tail指標,否則就會出錯,參見程式碼如下:

 

解法二:

class MyLinkedList {
public:
    /** Initialize your data structure here. */
    MyLinkedList() {
        head = NULL; tail = NULL;
        size = 0;
    }
    
    /** Get the value of the index-th node in the linked list. If the index is invalid, return -1. */
    int get(int index) {
        if (index < 0 || index >= size) return -1;
        Node *cur = head;
        for (int i = 0; i < index; ++i) cur = cur->next;
        return cur->val;
    }
    
    /** Add a node of value val before the first element of the linked list. After the insertion, the new node will be the first node of the linked list. */
    void addAtHead(int val) {
        Node *t = new Node(val, head);
        head = t;
        if (size == 0) tail = t;
        ++size;
    }
    
    /** Append a node of value val to the last element of the linked list. */
    void addAtTail(int val) {
        Node *t = new Node(val, NULL);
        if (size == 0) {tail = t; head = t;}
        tail->next = t;
        tail = t;
        ++size;
    }
    
    /** Add a node of value val before the index-th node in the linked list. If index equals to the length of linked list, the node will be appended to the end of linked list. If index is greater than the length, the node will not be inserted. */
    void addAtIndex(int index, int val) {
        if (index < 0 || index > size) return;
        if (index == 0) {addAtHead(val); return;}
        if (index == size) {addAtTail(val); return;}
        Node *cur = head;
        for (int i = 0; i < index - 1; ++i) cur = cur->next;
        Node *t = new Node(val, cur->next);
        cur->next = t;
        ++size;
    }
    
    /** Delete the index-th node in the linked list, if the index is valid. */
    void deleteAtIndex(int index) {
        if (index < 0 || index >= size) return;
        if (index == 0) {
            head = head->next;
            --size;
            return;
        }
        Node *cur = head;
        for (int i = 0; i < index - 1; ++i) cur = cur->next;
        cur->next = cur->next->next;
        if (index == size - 1) tail = cur;
        --size;
    }
    
private:
    struct Node {
        int val;
        Node *next;
        Node(int x, Node* n): val(x), next(n) {}
    };
    Node *head, *tail;
    int size;
};

 

最後來看一種很簡潔的方法,用到了內建的雙向佇列deque這個資料結構,很難說這樣算不算cheating,但是巨簡潔,大愛之~ 參見程式碼如下:

 

解法三:

class MyLinkedList {
public:
    /** Initialize your data structure here. */
    MyLinkedList() {}
    
    /** Get the value of the index-th node in the linked list. If the index is invalid, return -1. */
    int get(int index) {
        return (index >= 0 && index < data.size()) ? data[index] : -1;
    }
    
    /** Add a node of value val before the first element of the linked list. After the insertion, the new node will be the first node of the linked list. */
    void addAtHead(int val) {
        data.push_front(val);
    }
    
    /** Append a node of value val to the last element of the linked list. */
    void addAtTail(int val) {
        data.push_back(val);
    }
    
    /** Add a node of value val before the index-th node in the linked list. If index equals to the length of linked list, the node will be appended to the end of linked list. If index is greater than the length, the node will not be inserted. */
    void addAtIndex(int index, int val) {
        if (index < 0 || index > data.size()) return;
        data.insert(data.begin() + index, val);
    }
    
    /** Delete the index-th node in the linked list, if the index is valid. */
    void deleteAtIndex(int index) {
        if (index < 0 || index >= data.size()) return;
        data.erase(data.begin() + index);
    }
    
private:
    deque<int> data;
};

 

參考資料:

https://leetcode.com/problems/design-linked-list/

https://leetcode.com/problems/design-linked-list/discuss/154116/C%2B%2B-deque

https://leetcode.com/problems/design-linked-list/discuss/145380/C%2B%2B-SOLUTION-24ms

https://leetcode.com/problems/design-linked-list/discuss/150999/C%2B%2B-simple-solution-beats-97.27!

 

LeetCode All in One 題目講解彙總(持續更新中...)