LeetCode707:設計連結串列 Design Linked List

mug發表於2021-09-09

愛寫bug (ID:iCodeBugs)

設計連結串列的實現。您可以選擇使用單連結串列或雙連結串列。單連結串列中的節點應該具有兩個屬性:valnextval 是當前節點的值,next 是指向下一個節點的指標/引用。如果要使用雙向連結串列,則還需要一個屬性 prev 以指示連結串列中的上一個節點。假設連結串列中的所有節點都是 0-index 的。

在連結串列類中實現這些功能:

  • get(index):獲取連結串列中第 index 個節點的值。如果索引無效,則返回-1
  • addAtHead(val):在連結串列的第一個元素之前新增一個值為 val 的節點。插入後,新節點將成為連結串列的第一個節點。
  • addAtTail(val):將值為 val 的節點追加到連結串列的最後一個元素。
  • addAtIndex(index,val):在連結串列中的第 index 個節點之前新增值為 val 的節點。如果 index 等於連結串列的長度,則該節點將附加到連結串列的末尾。如果 index 大於連結串列長度,則不會插入節點。
  • deleteAtIndex(index):如果索引 index 有效,則刪除連結串列中的第 index 個節點。

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 next. val 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.

示例:

MyLinkedList linkedList = new MyLinkedList();
linkedList.addAtHead(1);
linkedList.addAtTail(3);
linkedList.addAtIndex(1,2);   //連結串列變為1-> 2-> 3
linkedList.get(1);            //返回2
linkedList.deleteAtIndex(1);  //現在連結串列是1-> 3
linkedList.get(1);            //返回3

提示:

  • 所有值都在 [1, 1000] 之內。
  • 操作次數將在 [1, 1000] 之內。
  • 請不要使用內建的 LinkedList 庫。

解題思路:

先看圖解:

  • 單連結串列新增操作

如果我們想在給定的結點 prev 之後新增新值,我們應該:

  1. 使用給定值初始化新結點 cur;圖片描述
  2. cur的“next”欄位連結到 prev 的下一個結點 next圖片描述
  3. prev 中的“next”欄位連結到 cur圖片描述
  • 刪除第一個結點

如果我們想刪除第一個結點,策略會有所不同。

正如之前所提到的,我們使用頭結點 head 來表示連結串列。我們的頭是下面示例中的黑色結點 23。

圖片描述

如果想要刪除第一個結點,我們可以簡單地將下一個結點分配給 head。也就是說,刪除之後我們的頭將會是結點 6。

圖片描述

連結串列從頭結點開始,因此結點 23 不再在我們的連結串列中。

圖片來源於LeetCode中國官網

高階程式設計語言中一般都有內建連結串列,這道題就是讓復現連結串列,看到有很多是用ArrayList、List等資料結構解的,很搞笑,題目說不能使用 LinkedList 庫,但 LinkedList 是繼承的ArrayList、List,,直接用這兩個庫一點意義都沒有。其實理解一下連結串列原理就好,高階語言都封裝好了連結串列,如果專案真的到了需要改寫連結串列底層結構來最佳化效能的那一步,那時候在實踐中基本已經摸清了這些東西。

Java:

class Node {//定義Node
    int val;
    Node next;

    Node(int val) {
        this.val = val;
        this.next = null;
    }
}

class MyLinkedList {

    Node head;//頭
    Node tail;//尾
    int size = 0;//連結串列長度

    public MyLinkedList() {//初始化資料
        head = new Node(0);//為了方便初始化一個本不存在的head,值為0
        tail = head;//初始下尾也指向和頭同一個物件
        size = 0;
    }

    public int get(int index) {
        if (index >= size || index < 0) {//index不在查詢區間返回-1
            return -1;
        }
        Node cur = head;
        for (int i = 0; i <= index; i++) {//從head一個一個向下遍歷,到index
            cur = cur.next;
        }
        return cur.val;//返回值
    }

    public void addAtHead(int val) {
        Node temp = head.next;//temp物件是真實連結串列的第一個節點(因為head一直是初始化的 0 )
        head.next = new Node(val);//構造的虛擬節點head的下一個節點指向新插入的節點
        head.next.next = temp;//新插入節點指向原本第一個真實節點
        size++;//計數
        if (size == 1) {
            tail = head.next;//如果只有一個節點此時尾節點也指向新加入的節點
        }
    }

    public void addAtTail(int val) {//新增尾節點
        tail.next = new Node(val);//把尾節點下一個物件指向新加入節點即可
        tail = tail.next;//重新整理尾節點為新加入的節點
        size++;
    }

    public void addAtIndex(int index, int val) {
        if (index > size) {//插入值不在範圍直接返回。
            return;
        }
        Node cur = head;//當前節點從頭節點開始
        for (int i = 0; i < index; i++) {//遍歷到 插入位置的前一個節點 因為要求是插入到index的前面
            cur = cur.next;
        }
        Node temp = cur.next;//暫存當前節點的下一個節點
        cur.next = new Node(val);//把當前節點下一個物件指向新節點
        if (index == size) {
            tail = cur.next;//如果插入位置剛好是最後一個則把尾節點指向新加入節點
        }
        cur.next.next = temp;//新節點的下一個節點指向暫存節點位置
        size++;
    }

    public void deleteAtIndex(int index) {
        if (index >= size || index < 0) {
            return;
        }
        Node cur = head;//從頭節點遍歷到index目標節點的前一個節點 因為要刪除目標節點
        for (int i = 0; i < index; i++) {
            cur = cur.next;
        }

        cur.next = cur.next.next;//目標節點前一個節點的下一個節點指向目標節點的下一個節點
        size--;//重新整理節點數量
        if (cur.next == null) {
            tail = cur;
        }
    }
}

Python3:

class Node:
    def __init__(self, val, _next=None):
        self.next = _next
        self.val = val


class MyLinkedList:
    def __init__(self):
        self.head = None
        self.tail = None
        self.size = 0

    def get(self, index: int) -> int:
        if index > self.size - 1 or index < 0:
            return -1
        node = self.head
        for i in range(index):
            node = node.next
        return node.val

    def addAtHead(self, val: int) -> None:
        node = Node(val, self.head)
        self.head = node
        if self.size == 0:
            self.tail = node
        self.size = self.size + 1

    def addAtTail(self, val: int) -> None:
        node = Node(val)
        if self.size == 0:
            self.head = self.tail = node  # 原連結串列為空時,新增新節點後,更新連結串列的頭指標和尾指標為新增節點。
        else:
            self.tail.next = node  # 原連結串列不為空時,使原尾指標指向新節點,即可將新節點新增至原連結串列尾部
            self.tail = node  # 更新尾指標
        self.size = self.size + 1  # 更新此時連結串列的長度

    def addAtIndex(self, index: int, val: int) -> None:
        node = Node(val)
        if index > self.size:
            return
        if index <= 0:
            return self.addAtHead(val)  # index 小於等於0都預設為頭指標後新增節點

        if index == self.size:  # 如果index等於連結串列的長度新增尾指標後新增節點
            return self.addAtTail(val)
        prev = self.head  # 第一個節點物件開始遍歷
        for i in range(index - 1):
            prev = prev.next
        temp = prev.next
        prev.next = node
        node.next = temp
        self.size = self.size + 1

    def deleteAtIndex(self, index: int) -> None:
        if index < 0 or index >= self.size:
            return
        prev = self.head
        if index == 0:
            self.head = self.head.next
            self.size = self.size - 1
            return
        for i in range(index - 1):
            prev = prev.next
        if index == self.size - 1:
            self.tail = prev
        prev.next = prev.next.next
        self.size = self.size - 1

來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/506/viewspace-2823291/,如需轉載,請註明出處,否則將追究法律責任。

相關文章