部分圖文參考於:程式碼隨想錄 - 707. 設計連結串列。
這道題目設計連結串列的五個介面:
● 獲取連結串列第index個節點的數值
● 在連結串列的最前面插入一個節點
● 在連結串列的最後面插入一個節點
● 在連結串列第index個節點前面插入一個節點
● 刪除連結串列的第index個節點
可以說這五個介面,已經覆蓋了連結串列的常見操作,是練習連結串列操作非常好的一道題目。
1.題目連結
707.設計連結串列
2.思路
我們引入一個虛擬頭結點來進行操作(dummyHead)。
1.get(index) 獲取第index個結點的值。如果索引無效,返回-1。
(1)索引越界:當index<0或是index>size-1,索引無效。
(2)獲取第index個結點的值:當前結點cur指向dummyHead的next,在index=0時,即獲取頭結點的值(cur->val)。在index為其他值時,透過while(index--){cur = cur -> next;}將當前結點cur向後移,直到移到index處,結束while迴圈。
2.addAtHead(val) 頭部插入結點
注意插入時的順序:
①newNode->next = dummyHead->next;
②dummyHead->next = newNode;
3.addAtTail(val) 尾部插入結點
當前結點的next結點不為空時,持續向後找最後一個結點。找到後,在當前結點後插入新結點:cur -> next = newNode;
4.addAtIndex(index,val) 第index個結點前插入結點
(1)index如果大於連結串列長度,不會插入結點。index小於0,在頭部插入結點。index等於連結串列長度,則該結點將附加到連結串列末尾。
(2)當前結點cur指向dummyHead,使用while(index--){cur = cur->next}找到index所在位置。
(3)插入結點。newNode->next = cur->next; cur->next = newNode;
2-4需要size++。
5.deleteAtIndex(index) 刪除第index個結點
(1)需要判斷index是否有效,index<0或index≥size無效。
(2)透過while(index--){cur=cur->next;}找到要刪除的結點。
(3)刪除操作:cur->next = cur->next->next;
(4)C++需要delete釋放無效結點記憶體。
class MyLinkedList {
public:
// 定義連結串列節點結構體
struct LinkedNode {
int val;
LinkedNode* next;
LinkedNode(int val):val(val), next(nullptr){}
};
// 初始化連結串列
MyLinkedList() {
_dummyHead = new LinkedNode(0); // 這裡定義的頭結點 是一個虛擬頭結點,而不是真正的連結串列頭結點
_size = 0;
}
// 獲取到第index個節點數值,如果index是非法數值直接返回-1, 注意index是從0開始的,第0個節點就是頭結點
int get(int index) {
if (index > (_size - 1) || index < 0) {
return -1;
}
LinkedNode* cur = _dummyHead->next;
while(index--){ // 如果--index 就會陷入死迴圈
cur = cur->next;
}
return cur->val;
}
// 在連結串列最前面插入一個節點,插入完成後,新插入的節點為連結串列的新的頭結點
void addAtHead(int val) {
LinkedNode* newNode = new LinkedNode(val);
newNode->next = _dummyHead->next;
_dummyHead->next = newNode;
_size++;
}
// 在連結串列最後面新增一個節點
void addAtTail(int val) {
LinkedNode* newNode = new LinkedNode(val);
LinkedNode* cur = _dummyHead;
while(cur->next != nullptr){
cur = cur->next;
}
cur->next = newNode;
_size++;
}
// 在第index個節點之前插入一個新節點,例如index為0,那麼新插入的節點為連結串列的新頭節點。
// 如果index 等於連結串列的長度,則說明是新插入的節點為連結串列的尾結點
// 如果index大於連結串列的長度,則返回空
// 如果index小於0,則在頭部插入節點
void addAtIndex(int index, int val) {
if(index > _size) return;
if(index < 0) index = 0;
LinkedNode* newNode = new LinkedNode(val);
LinkedNode* cur = _dummyHead;
while(index--) {
cur = cur->next;
}
newNode->next = cur->next;
cur->next = newNode;
_size++;
}
// 刪除第index個節點,如果index 大於等於連結串列的長度,直接return,注意index是從0開始的
void deleteAtIndex(int index) {
if (index >= _size || index < 0) {
return;
}
LinkedNode* cur = _dummyHead;
while(index--) {
cur = cur ->next;
}
LinkedNode* tmp = cur->next;
cur->next = cur->next->next;
delete tmp;
//delete命令指示釋放了tmp指標原本所指的那部分記憶體,
//被delete後的指標tmp的值(地址)並非就是NULL,而是隨機值。也就是被delete後,
//如果不再加上一句tmp=nullptr,tmp會成為亂指的野指標
//如果之後的程式不小心使用了tmp,會指向難以預想的記憶體空間
tmp=nullptr;
_size--;
}
// 列印連結串列
void printLinkedList() {
LinkedNode* cur = _dummyHead;
while (cur->next != nullptr) {
cout << cur->next->val << " ";
cur = cur->next;
}
cout << endl;
}
private:
int _size;
LinkedNode* _dummyHead;
};
● 時間複雜度: 涉及 index 的相關操作為 O(index), 其餘為 O(1)。
● 空間複雜度: O(n)。