707_設計連結串列
【問題描述】
你可以選擇使用單連結串列或者雙連結串列,設計並實現自己的連結串列。
單連結串列中的節點應該具備兩個屬性:val
和 next
。val
是當前節點的值,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 庫。
- 呼叫
get
、addAtHead
、addAtTail
、addAtIndex
和deleteAtIndex
的次數不超過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) # 刪除指定索引處的節點