707_設計連結串列

zeta186012發表於2024-10-07

707_設計連結串列

【問題描述】

你可以選擇使用單連結串列或者雙連結串列,設計並實現自己的連結串列。

單連結串列中的節點應該具備兩個屬性:valnextval 是當前節點的值,next 是指向下一個節點的指標/引用。

如果是雙向連結串列,則還需要屬性 prev 以指示連結串列中的上一個節點。假設連結串列中的所有節點下標從 0 開始。

實現 MyLinkedList 類:

  • MyLinkedList() 初始化 MyLinkedList 物件。
  • int get(int index) 獲取連結串列中下標為 index 的節點的值。如果下標無效,則返回 -1
  • void addAtHead(int val) 將一個值為 val 的節點插入到連結串列中第一個元素之前。在插入完成後,新節點會成為連結串列的第一個節點。
  • void addAtTail(int val) 將一個值為 val 的節點追加到連結串列中作為連結串列的最後一個元素。
  • void addAtIndex(int index, int val) 將一個值為 val 的節點插入到連結串列中下標為 index 的節點之前。如果 index 等於連結串列的長度,那麼該節點會被追加到連結串列的末尾。如果 index 比長度更大,該節點將 不會插入 到連結串列中。
  • void deleteAtIndex(int index) 如果下標有效,則刪除連結串列中下標為 index 的節點。
示例:

輸入:
["MyLinkedList", "addAtHead", "addAtTail", "addAtIndex", "get", "deleteAtIndex", "get"]
[[], [1], [3], [1, 2], [1], [1], [1]]

輸出:
[null, null, null, null, 2, null, 3]

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

提示:

  • 0 <= index, val <= 1000
  • 請不要使用內建的 LinkedList 庫。
  • 呼叫 getaddAtHeadaddAtTailaddAtIndexdeleteAtIndex 的次數不超過 2000

【演算法設計思想】

  • 1.建立連結串列 (myLinkedListCreate):

​ • 分配記憶體為連結串列例項並初始化頭指標為 NULL,大小為 0。

​ • 2.獲取元素值 (myLinkedListGet):

​ • 檢查索引是否有效(範圍內)。

​ • 遍歷連結串列到達指定索引,並返回該節點的值。

​ • 3.新增節點到頭部 (myLinkedListAddAtHead):

​ • 建立新節點,設定其值並將其 next 指標指向當前的頭節點。

​ • 更新頭指標為新節點,並增加連結串列大小。

​ • 4.新增節點到尾部 (myLinkedListAddAtTail):

​ • 建立新節點,設定其值並將 next 指向 NULL(表示尾部)。

​ • 如果連結串列為空,則新節點成為頭節點。

​ • 否則,遍歷到連結串列末尾,將最後一個節點的 next 指向新節點。

​ • 5.在指定索引插入節點 (myLinkedListAddAtIndex):

​ • 檢查索引是否有效。如果索引小於等於 0,呼叫 addAtHead。

​ • 建立新節點,遍歷到達指定索引前一個節點。

​ • 更新前一個節點的 next 指向新節點,設定新節點的 next 為當前節點的 next。

​ • 6.刪除指定索引的節點 (myLinkedListDeleteAtIndex):

​ • 檢查索引是否有效。如果刪除頭節點,則更新頭指標為下一個節點。

​ • 否則,遍歷到達指定索引前一個節點,儲存要刪除的節點並更新前一個節點的 next 指向要刪除節點的下一個節點。

​ • 釋放被刪除節點的記憶體。

​ • 7.釋放連結串列記憶體 (myLinkedListFree):

​ • 從頭節點開始遍歷整個連結串列,釋放每個節點的記憶體。

​ • 最後釋放連結串列結構體本身的記憶體。

【演算法描述】

C:

// 定義連結串列節點結構體
typedef struct Node {
    int val; // 節點儲存的值
    struct Node* next; // 指向下一個節點的指標
} Node;

// 定義連結串列結構體
typedef struct {
    Node* head; // 連結串列頭指標
    int size; // 連結串列中節點的數量
} MyLinkedList;

// 建立一個新的連結串列例項
MyLinkedList* myLinkedListCreate() {
    MyLinkedList* obj = (MyLinkedList*)malloc(sizeof(MyLinkedList)); // 分配記憶體
    obj->head = NULL; // 初始化頭指標為NULL
    obj->size = 0; // 初始化大小為0
    return obj; // 返回新建立的連結串列例項
}

// 獲取連結串列中指定索引處的元素值
int myLinkedListGet(MyLinkedList* obj, int index) {
    if (index < 0 || index >= obj->size) { // 檢查索引是否有效
        return -1; // 如果索引無效,返回-1
    }
    Node* current = obj->head; // 從頭開始遍歷
    for (int i = 0; i < index; i++) { // 移動到指定索引位置
        current = current->next;
    }
    return current->val; // 返回該位置節點的值
}

// 在連結串列頭部新增一個新元素
void myLinkedListAddAtHead(MyLinkedList* obj, int val) {
    Node* newNode = (Node*)malloc(sizeof(Node)); // 建立新節點
    newNode->val = val; // 設定新節點的值
    newNode->next = obj->head; // 新節點指向當前頭節點
    obj->head = newNode; // 更新頭指標為新節點
    obj->size++; // 增加連結串列大小
}

// 在連結串列尾部新增一個新元素
void myLinkedListAddAtTail(MyLinkedList* obj, int val) {
    Node* newNode = (Node*)malloc(sizeof(Node)); // 建立新節點
    newNode->val = val; // 設定新節點的值
    newNode->next = NULL; // 尾部節點的next應為NULL

    if (obj->size == 0) { // 如果連結串列為空
        obj->head = newNode; // 新節點成為頭節點
    } else {
        Node* current = obj->head; // 從頭開始遍歷
        while (current->next != NULL) { // 找到最後一個節點
            current = current->next;
        }
        current->next = newNode; // 最後一個節點的next指向新節點
    }
    obj->size++; // 增加連結串列大小
}

// 在指定索引處插入一個新元素
void myLinkedListAddAtIndex(MyLinkedList* obj, int index, int val) {
    if (index > obj->size) { // 如果索引超出連結串列長度,不做任何操作
        return;
    }

    if (index <= 0) { // 如果索引小於等於0,在頭部插入
        myLinkedListAddAtHead(obj, val);
        return;
    }

    Node* newNode = (Node*)malloc(sizeof(Node)); // 建立新節點
    newNode->val = val; // 設定新節點的值

    Node* current = obj->head; // 從頭開始遍歷
    for (int i = 0; i < index - 1; i++) { // 移動到指定索引前一個位置
        current = current->next;
    }
    newNode->next = current->next; // 新節點的next指向當前節點的next
    current->next = newNode; // 當前節點的next指向新節點
    obj->size++; // 增加連結串列大小
}

// 刪除指定索引處的元素
void myLinkedListDeleteAtIndex(MyLinkedList* obj, int index) {
    if (index < 0 || index >= obj->size) { // 檢查索引是否有效
        return;
    }
    if (index == 0) { // 如果刪除的是頭節點
        Node* temp = obj->head; // 儲存頭節點
        obj->head = obj->head->next; // 更新頭指標為下一個節點
        free(temp); // 釋放舊頭節點的記憶體
    } else {
        Node* current = obj->head; // 從頭開始遍歷
        for (int i = 0; i < index - 1; i++) { // 移動到指定索引前一個位置
            current = current->next;
        }
        Node* temp = current->next; // 儲存要刪除的節點
        current->next = temp->next; // 跳過要刪除的節點
        free(temp); // 釋放該節點的記憶體
    }
    obj->size--; // 減少連結串列大小
}

// 釋放連結串列佔用的所有記憶體
void myLinkedListFree(MyLinkedList* obj) {
    Node* current = obj->head; // 從頭開始
    while (current != NULL) { // 遍歷整個連結串列
        Node* temp = current; // 儲存當前節點
        current = current->next; // 移動到下一個節點
        free(temp); // 釋放當前節點的記憶體
    }
    free(obj); // 釋放連結串列結構體本身的記憶體
}

C++:

class MyLinkedList {
public:
    // 定義連結串列節點的結構
    typedef struct Node {
        int val;  // 節點的值
        Node* next;  // 指向下一個節點的指標

        // 建構函式,初始化節點的值和指標
        Node(int val) {
            this->val = val;
            this->next = nullptr;
        }
    } Node;

    // 建構函式,初始化連結串列
    MyLinkedList() {
        dummy = new Node(0);  // 初始化一個虛擬頭節點
        size = 0;  // 初始化連結串列大小為0
    }

    // 獲取連結串列中第index個節點的值,如果索引無效則返回-1
    int get(int index) {
        if (index > size - 1 || index < 0) {  // 邊界檢查,索引範圍超出時返回-1
            return -1;
        }
        Node* current = dummy->next;  // 從連結串列第一個有效節點開始遍歷
        for (int i = 0; i < index; i++) {  // 遍歷直到第index個節點
            current = current->next;
        }
        return current->val;  // 返回第index個節點的值
    }

    // 在連結串列頭部新增一個值為val的節點
    void addAtHead(int val) {
        Node* newNode = new Node(val);  // 建立新節點
        newNode->next = dummy->next;  // 新節點的next指向當前連結串列的第一個節點
        dummy->next = newNode;  // 虛擬頭節點的next指向新節點
        size++;  // 連結串列大小加1
    }

    // 在連結串列尾部新增一個值為val的節點
    void addAtTail(int val) {
        Node* newNode = new Node(val);  // 建立新節點
        Node* current = dummy;  // 從虛擬頭節點開始遍歷
        while (current->next != nullptr) {  // 遍歷到連結串列的最後一個節點
            current = current->next;
        }
        current->next = newNode;  // 將最後一個節點的next指向新節點
        size++;  // 連結串列大小加1
    }

    // 在連結串列的第index個位置插入一個值為val的節點
    void addAtIndex(int index, int val) {
        if (index < 0 || index > size) {  // 檢查插入的索引是否合法
            return;  // 如果不合法,則不進行插入操作
        }
        if (index == 0) {  // 如果在連結串列頭部插入
            Node* newNode = new Node(val);  // 建立新節點
            newNode->next = dummy->next;  // 新節點的next指向當前連結串列的第一個節點
            dummy->next = newNode;  // 虛擬頭節點的next指向新節點
            size++;  // 連結串列大小加1
            return;  // 提前返回
        }
        Node* newNode = new Node(val);  // 建立新節點
        Node* current = dummy;  // 從虛擬頭節點開始遍歷
        for (int i = 0; i < index; i++) {  // 遍歷直到index之前的位置
            current = current->next;
        }
        newNode->next = current->next;  // 新節點的next指向當前節點的下一個節點
        current->next = newNode;  // 當前節點的next指向新節點
        size++;  // 連結串列大小加1
    }

    // 刪除連結串列中第index個節點
    void deleteAtIndex(int index) {
        if (index < 0 || index > size - 1) {  // 檢查刪除的索引是否合法
            return;  // 如果不合法,則不進行刪除操作
        }
        Node* current = dummy;  // 從虛擬頭節點開始遍歷
        for (int i = 0; i < index; i++) {  // 遍歷到要刪除的節點之前的位置
            current = current->next;
        }
        Node* toDelete = current->next;  // 記錄要刪除的節點
        current->next = toDelete->next;  // 將當前節點的next指向要刪除節點的下一個節點
        delete toDelete;  // 刪除節點
        size--;  // 連結串列大小減1
    }

private:
    int size;  // 記錄連結串列的長度
    Node* dummy;  // 虛擬頭節點,方便操作
};

/**
 * Your MyLinkedList object will be instantiated and called as such:
 * MyLinkedList* obj = new MyLinkedList();
 * int param_1 = obj->get(index);  // 獲取連結串列中第index個節點的值
 * obj->addAtHead(val);  // 在連結串列頭部插入一個節點
 * obj->addAtTail(val);  // 在連結串列尾部插入一個節點
 * obj->addAtIndex(index,val);  // 在第index個位置插入一個節點
 * obj->deleteAtIndex(index);  // 刪除第index個節點
 */

Java:

// 節點類,表示連結串列中的一個節點
class Node {
    public int val;     // 節點儲存的值
    public Node next;   // 指向下一個節點的指標

    // 建構函式,初始化節點的值和 next 指標
    Node(int val) {
        this.val = val;
        this.next = null; // 預設情況下,next 指標為 null
    }
}

// 單連結串列類,支援基本的連結串列操作
class MyLinkedList {
    private int size;   // 連結串列中的元素數量
    private Node dummy; // 虛擬頭節點,方便進行連結串列操作

    // 建構函式,初始化連結串列
    public MyLinkedList() {
        dummy = new Node(0); // 初始化虛擬頭節點
        size = 0;            // 初始連結串列為空
    }
    
    // 獲取連結串列中第 index 個節點的值,若 index 無效則返回 -1
    public int get(int index) {
        if (index < 0 || index > size - 1) {
            return -1;  // 如果 index 超出範圍,返回 -1
        }
        Node current = dummy.next; // 從虛擬頭節點的下一個節點開始遍歷
        for (int i = 0; i < index; i++) {
            current = current.next; // 移動到下一個節點
        }
        return current.val; // 返回目標節點的值
    }
    
    // 在連結串列的頭部插入一個值為 val 的節點
    public void addAtHead(int val) {
        Node newNode = new Node(val);   // 建立新節點
        newNode.next = dummy.next;      // 新節點的 next 指向原頭節點
        dummy.next = newNode;           // 虛擬頭節點的 next 指向新節點
        size++;                         // 連結串列大小加 1
    }
    
    // 在連結串列的尾部插入一個值為 val 的節點
    public void addAtTail(int val) {
        Node current = dummy;           // 從虛擬頭節點開始遍歷
        while (current.next != null) {
            current = current.next;     // 找到連結串列的最後一個節點
        }
        Node newNode = new Node(val);   // 建立新節點
        current.next = newNode;         // 將最後一個節點的 next 指向新節點
        size++;                         // 連結串列大小加 1
    }
    
    // 在連結串列中的第 index 個節點之前插入值為 val 的節點
    // 如果 index 等於連結串列的長度,則該節點將附加到連結串列末尾
    // 如果 index 大於連結串列長度,則不會插入節點
    public void addAtIndex(int index, int val) {
        if (index < 0 || index > size) {
            return; // 如果 index 無效,直接返回
        }
        if (index == 0) {
            addAtHead(val);  // 如果在頭部插入,直接呼叫 addAtHead 方法
            return;
        }
        Node newNode = new Node(val);  // 建立新節點
        Node current = dummy;          // 從虛擬頭節點開始遍歷
        for (int i = 0; i < index; i++) {
            current = current.next;    // 找到第 index-1 個節點
        }
        newNode.next = current.next;   // 新節點的 next 指向第 index 個節點
        current.next = newNode;        // 第 index-1 個節點的 next 指向新節點
        size++;                        // 連結串列大小加 1
    }
    
    // 刪除連結串列中第 index 個節點
    public void deleteAtIndex(int index) {
        if (index < 0 || index > size - 1) {
            return; // 如果 index 無效,直接返回
        }
        Node current = dummy;           // 從虛擬頭節點開始遍歷
        for (int i = 0; i < index; i++) {
            current = current.next;     // 找到第 index-1 個節點
        }
        Node toDelete = current.next;   // 找到第 index 個節點
        current.next = toDelete.next;   // 將第 index-1 個節點的 next 指向第 index+1 個節點
        toDelete = null;                // 將第 index 個節點置為 null(可選)
        size--;                         // 連結串列大小減 1
    }

}

/**
 * 示例用法:
 * MyLinkedList obj = new MyLinkedList();
 * int param_1 = obj.get(index);
 * obj.addAtHead(val);
 * obj.addAtTail(val);
 * obj.addAtIndex(index, val);
 * obj.deleteAtIndex(index);
 */

Python:

class Node:
    def __init__(self, val=0, next=None):
        self.val = val  # 節點的值
        self.next = next  # 指向下一個節點的指標

class MyLinkedList:

    def __init__(self):
        self.dummy = Node()  # 建立一個虛擬頭節點,方便操作
        self.size = 0  # 初始化連結串列的大小為 0

    def get(self, index: int) -> int:
        # 如果索引無效,返回 -1
        if index < 0 or index >= self.size:  # 使用 >= 進行邊界檢查
            return -1
        current = self.dummy.next  # 從虛擬頭節點的下一個節點開始
        for i in range(index):  # 遍歷到目標索引
            current = current.next
        return current.val  # 返回目標節點的值

    def addAtHead(self, val: int) -> None:
        # 在連結串列頭部新增新節點
        newNode = Node(val)  # 建立新節點
        newNode.next = self.dummy.next  # 新節點的 next 指向當前頭節點
        self.dummy.next = newNode  # 虛擬頭節點的 next 指向新節點
        self.size += 1  # 連結串列大小加 1

    def addAtTail(self, val: int) -> None:
        # 在連結串列尾部新增新節點
        current = self.dummy  # 從虛擬頭節點開始
        while current.next is not None:  # 遍歷到連結串列的末尾
            current = current.next
        newNode = Node(val)  # 建立新節點
        current.next = newNode  # 將新節點新增到連結串列尾部
        self.size += 1  # 連結串列大小加 1

    def addAtIndex(self, index: int, val: int) -> None:
        # 在指定索引處新增新節點
        if index < 0 or index > self.size:  # 檢查索引是否有效
            return
        if index == 0:  # 如果在頭部插入
            self.addAtHead(val)  # 呼叫 addAtHead 方法
            return
        current = self.dummy  # 從虛擬頭節點開始
        for i in range(index):  # 遍歷到 index-1 個節點
            current = current.next
        newNode = Node(val)  # 建立新節點
        newNode.next = current.next  # 新節點的 next 指向第 index 個節點
        current.next = newNode  # 將第 index-1 個節點的 next 指向新節點
        self.size += 1  # 連結串列大小加 1

    def deleteAtIndex(self, index: int) -> None:
        # 刪除指定索引處的節點
        if index < 0 or index >= self.size:  # 檢查索引是否有效
            return
        current = self.dummy  # 從虛擬頭節點開始
        for i in range(index):  # 遍歷到 index-1 個節點
            current = current.next
        toDelete = current.next  # 找到第 index 個節點
        current.next = toDelete.next  # 刪除該節點
        del toDelete  # Python 會自動處理記憶體,不需要顯式刪除
        self.size -= 1  # 連結串列大小減 1

# 使用示例
# obj = MyLinkedList()  # 建立一個新的連結串列物件
# param_1 = obj.get(index)  # 獲取指定索引處的節點值
# obj.addAtHead(val)  # 在連結串列頭部新增新值
# obj.addAtTail(val)  # 在連結串列尾部新增新值
# obj.addAtIndex(index, val)  # 在指定索引處新增新值
# obj.deleteAtIndex(index)  # 刪除指定索引處的節點

相關文章