[LeetCode] 707. Design Linked List 設計鏈表

 

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.html

Implement these functions in your linked list class:node

  • 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. If index is negative, the node will be inserted at the head of the list.
  • deleteAtIndex(index) : Delete the index-th node in the linked list, if the index is valid.

Example:git

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:github

  • 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 是否合法,題目要求有過變更,新加一條說是當 index 爲負數時,要在表頭加個結點,這樣的話只須要判斷 index 是否大於 size 這一種非法狀況。而後再處理一個 corner case,就是當 index 小於等於0的時候,直接調用前面的表頭加結點函數便可。而後就是日後遍歷 index-1 個結點,這裏爲啥要減1呢,由於要加入結點的話,必需要知道加入位置前面一個結點才行,鏈表加入結點的問題以前的題目中作過不少,這裏就不說細節了,最後 size 仍是要自增1。根據位置刪除結點也是大同小異,沒有太大的難度,參見代碼以下:數據結構

 

解法一:app

class MyLinkedList {
public:
    MyLinkedList() {
        head = NULL;
        size = 0;
    }
    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;
    }
    void addAtHead(int val) {
        Node *t = new Node(val, head);
        head = t;
        ++size;
    }
    void addAtTail(int val) {
        Node *cur = head;
        while (cur->next) cur = cur->next;
        cur->next = new Node(val, NULL);
        ++size;
    }
    void addAtIndex(int index, int val) {
        if (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;
    }
    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 指針,不然就會出錯,參見代碼以下:函數

 

解法二:post

class MyLinkedList {
public:
    MyLinkedList() {
        head = NULL; tail = NULL;
        size = 0;
    }
    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;
    }
    void addAtHead(int val) {
        Node *t = new Node(val, head);
        head = t;
        if (size == 0) tail = t;
        ++size;
    }
    void addAtTail(int val) {
        Node *t = new Node(val, NULL);
        if (size == 0) {tail = t; head = t;}
        tail->next = t;
        tail = t;
        ++size;
    }
    void addAtIndex(int index, int val) {
        if (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;
    }
    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:
    MyLinkedList() {}

    int get(int index) {
        return (index >= 0 && index < data.size()) ? data[index] : -1;
    }
    void addAtHead(int val) {
        data.push_front(val);
    }
    void addAtTail(int val) {
        data.push_back(val);
    }
    void addAtIndex(int index, int val) {   
        if (index > (int)data.size()) return;
        if (index <= 0) { addAtHead(val); return; }
        data.insert(data.begin() + index, val);
    }
    void deleteAtIndex(int index) {
        if (index < 0 || index >= data.size()) return;
        data.erase(data.begin() + index);
    }
    
private:
    deque<int> data;
};

 

Github 同步地址:

https://github.com/grandyang/leetcode/issues/707

 

參考資料:

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 題目講解彙總(持續更新中...)

相關文章
相關標籤/搜索