- 1、鏈表基本概念以及注意事項
- a、構造函數與析構函數
- b、插入
- c、重載運算符[]
- 2、打印鏈表
- 3、刪除鏈表節點
- 4、鏈表中倒數第k個節點
- 5、反轉鏈表
- 6、合並兩個排序的鏈表
- 7、兩個鏈表當中的第一個公共節點
【查看之前筆記】
在編寫函數之前,請務必注意以下三點:
i、輸入的指針、各類容器可能是空的
ii、輸入的容器可能只有一個元素
iii、輸入的容器可能有多個元素,這個是最常見的情況。
【做完下面代碼后,請你再按照上面三條,將異常情況加上!!!】
1 #include<iostream> 2 #include<set> 3 using namespace std; 4 5 class Node 6 { 7 public: 8 int data_;//數據閾 9 Node* next_;//指針閾 10 public: 11 Node():data_(-1), next_(nullptr) {} 12 }; 13 14 class List 15 { 16 public: 17 List() 18 { 19 this->head_ = new Node();// 不分配空間,下面賦值是不合理的! 20 //this->head_->data_ = 0;//多余? 21 this->head_->next_ = nullptr; 22 this->size_ = 0; 23 }; 24 void insert(int pos, int value); 25 void remove(int pos); 26 int get_reverse_element(int reverse_pos);//鏈表中倒數第k個節點 27 void reverse(); 28 29 int operator[](int i); 30 void print(); 31 ~List(); 32 33 34 public: 35 Node* head_; 36 int size_;//維護一個size 37 }; 38 //在第pos個元素前一個位置插入(創建、找到位置、入鏈表) 39 void List::insert(int pos, int value) 40 { 41 if (pos < 0 || pos > size_) 42 return; 43 44 //創建新的節點接受數據 45 Node* newnode = new Node(); 46 newnode->data_ = value; 47 //cout << "newnode->data_ = " << *newnode->data_ << endl; 48 newnode->next_ = nullptr; 49 50 //利用輔助指針找到pos前一個節點 51 // 其實這里不斷next,無非就是希望p_curr = nullptr 52 // 然后56行 讓newnode->next_ = nullptr(這個nullptr是從head_->next 傳過來的);也就是尾部插入嘛 53 // 而循環鏈表 同理 讓newnode->next_ = &(head_)(這個 &(head_) 是從head_->next 傳過來的); 54 Node* p_curr = head_; 55 for (int i = 0; i < pos; i++) //這個for循環本質上是head_->next_->next_...... 56 { 57 p_curr = p_curr->next_; 58 } 59 //現在p_curr就是pos前一個節點的指針閾 60 //新節點入鏈表 61 newnode->next_ = p_curr->next_;//右邊 62 p_curr->next_ = newnode;//左邊 63 size_++; 64 } 65 66 void List::remove(int pos) 67 { 68 if (pos < 0 || pos > size_) 69 { 70 return; 71 } 72 Node* p_curr = head_; 73 for (int i = 0; i < pos; i++)// 3 74 { 75 p_curr = p_curr->next_; 76 } 77 p_curr->next_ = p_curr->next_->next_; 78 size_--; 79 } 80 81 //鏈表中倒數第k個節點 82 int List::get_reverse_element(int reverse_pos) 83 { 84 int pos = size_ - reverse_pos; 85 Node* p_curr = head_; 86 for (int i = 0; i < pos; i++) 87 { 88 p_curr = p_curr->next_; 89 } 90 return p_curr->data_; 91 } 92 93 //反轉鏈表 94 void List::reverse() 95 { 96 // head -> 1 -> 2 -> 3 -> 4 -> nullptr 97 //nullptr <- 1 <- 2 <- 3 <- 4 98 99 Node* p_curr = head_->next_; 100 Node* p_prev = nullptr; 101 while (p_curr != nullptr) 102 { 103 Node* p_next = p_curr->next_; 104 if (p_next == nullptr) 105 if(p_curr->next_ == nullptr) 106 { 107 head_->next_ = p_curr; 108 } 109 p_curr->next_ = p_prev; 110 p_prev = p_curr; 111 p_curr = p_next; 112 } 113 } 114 115 int List::operator[](int i) 116 { 117 Node* p_curr = head_; 118 int count = 0; 119 while (count <= i) 120 { 121 p_curr = p_curr->next_; 122 count++; 123 } 124 return p_curr->data_; 125 } 126 void List::print() 127 { 128 if (size_ == 0) 129 { 130 cout << "size = 0" << endl; 131 return; 132 } 133 //遍歷 134 Node* p_curr = head_->next_;//【注意這里next】 135 while (p_curr != nullptr) 136 { 137 cout << p_curr->data_ << " "; 138 p_curr = p_curr->next_; 139 } 140 cout << endl; 141 } 142 List::~List() 143 { 144 while (size_ != 0) 145 { 146 Node* p_curr = head_; 147 for (int i = 0; i < (size_ - 1); i++)// 012345 i < 5 148 { 149 p_curr = p_curr->next_;//for循環執行完,p_curr指向4 150 } 151 delete p_curr->next_;//刪除最后一個元素 152 p_curr->next_ = nullptr;//末尾元素 空指針 153 size_--; 154 print(); 155 } 156 delete head_; //【這個容易忘記!】 157 cout << "delete!" << endl; 158 } 159 160 //合並兩個排序鏈表 161 void mergeLists(List& list3, List& list4, List& list34) 162 { 163 Node* p_curr3 = list3.head_->next_; 164 Node* p_curr4 = list4.head_->next_; 165 Node* p_curr34 = list34.head_->next_; 166 int location = 0; 167 while ((p_curr3 != nullptr) || (p_curr4 != nullptr)) 168 { 169 if ((p_curr3 != nullptr) && (p_curr4 != nullptr)) 170 { 171 if (p_curr3->data_ < p_curr4->data_) 172 { 173 list34.insert(location, p_curr3->data_); 174 location++; 175 list34.insert(location, p_curr4->data_); 176 location++; 177 } 178 else 179 { 180 list34.insert(location, p_curr4->data_); 181 location++; 182 list34.insert(location, p_curr3->data_); 183 location++; 184 } 185 p_curr3 = p_curr3->next_; 186 p_curr4 = p_curr4->next_; 187 } 188 else if ((p_curr3 != nullptr) && (p_curr4 == nullptr)) 189 { 190 list34.insert(location, p_curr3->data_); 191 location++; 192 p_curr3 = p_curr3->next_; 193 } 194 else if ((p_curr3 == nullptr) && (p_curr4 != nullptr)) 195 { 196 list34.insert(location, p_curr4->data_); 197 location++; 198 p_curr4 = p_curr4->next_; 199 } 200 } 201 } 202 203 204 int main() 205 { 206 List list1; 207 //插入 208 for (int i = 0; i < 15; i++) 209 { 210 list1.insert(i, i); 211 } 212 213 //刪除 214 list1.remove(10); 215 list1.remove(5); 216 //打印 217 list1.print(); 218 list1.reverse(); 219 list1.print(); 220 //訪問倒數元素 221 for (int i = 1; i < 4; i++) 222 { 223 cout << "倒數第" << i << "個元素是:" << list1.get_reverse_element(i) << endl; 224 } 225 list1.insert(2, 9999); 226 //重載符[] 227 for (int i = list1.size_ - 1; i >= 0; i--) 228 { 229 cout << list1[i] << " "; 230 } 231 cout << endl; 232 List list2; 233 list2.insert(0, 10); 234 list2.insert(1, 20); 235 list2.insert(2, 30); 236 list2.print(); 237 int size2 = list2.size_; 238 239 //合並兩個排序鏈表 240 List list3, list4; 241 for (int i = 0; i < 5; i++) 242 { 243 list3.insert(i, 2 * i); 244 list4.insert(i, 2 * i + 1); 245 } 246 list4.insert(5, 12); 247 list4.insert(6, 21); 248 list3.print(); 249 list4.print(); 250 251 List list34; 252 mergeLists(list3, list4, list34); 253 list34.print(); 254 255 256 return 1; 257 }
a、構造函數與析構函數
節點 ,初始化數據一定要做。
1 class Node 2 { 3 public: 4 int data_;//數據閾 5 Node* next_;//指針閾 6 public: 7 Node():data_(-1), next_(nullptr) 8 {} 9 };
構造函數
1 List() 2 { 3 this->head_ = new Node();// 分配空間 4 this->size_ = 0; 5 };
整個鏈表,實現任何功能,我們都要維護一個頭節點和size_
析構函數
1 List::~List() 2 { 3 while (size_ != 0) 4 { 5 Node* p_curr = head_; 6 for (int i = 0; i < (size_ - 1); i++)// 012345 i < 5 7 { 8 p_curr = p_curr->next_;//for循環執行完,p_curr指向4 9 } 10 delete p_curr->next_;//刪除最后一個元素 11 p_curr->next_ = nullptr;//末尾元素 空指針 12 size_--; 13 print(); 14 } 15 delete head_; // 16 cout << "delete!" << endl; 17 }
在保證不斷鏈的前提下;釋放整個鏈表,似乎只能從后面開始delete。循環當中,每次遍歷到 待刪除節點前一個p_curr, 然后delete p_curr->next_
b、插入
1 //在第pos個元素前一個位置插入(創建、找到位置、入鏈表) 2 void List::insert(int pos, int value) 3 { 4 if (pos < 0 || pos > size_) 5 return; 6 7 //創建新的節點接受數據 8 Node* newnode = new Node(); 9 newnode->data_ = value; 10 11 Node* p_curr = head_; 12 for (int i = 0; i < pos; i++) 13 { 14 p_curr = p_curr->next_; 15 } 16 17 //新節點入鏈表 18 newnode->next_ = p_curr->next_; 19 p_curr->next_ = newnode; 20 size_++; 21 }
18行、19行代碼很好詮釋了插入過程;其中18行代碼:將當前節點的指針域指向 對象的地址 賦給 newnode->next_;保證了新數據與鏈表中后面連接;
19行代碼:當前節點指針域指向 新的節點
例如: 1 2 3 4 5 要在 4 和 5 之間插入 一個 666
步驟如下:
遍歷至節點4; 1 2 3 4 5
將666 指向5:; 1 2 3 4 666 5
再將 4 指向 666; 1 2 3 4 666 5
c、重載運算符
1 int List::operator[](int i) 2 { 3 Node* p_curr = head_; 4 int count = 0; 5 while (count <= i) 6 { 7 p_curr = p_curr->next_; 8 count++; 9 } 10 return p_curr->data_; 11 }
在做上面幾道題之前,我們先給出一個基本鏈表。
總代碼,順便解釋構造函數、析構函數為什么這樣寫
2、打印鏈表
1 void List::print() 2 { 3 if (size_ == 0) 4 { 5 cout << "size = 0" << endl; 6 return; 7 } 8 //遍歷 9 Node* p_curr = head_->next_;//【注意這里next】 10 while (p_curr != nullptr) 11 { 12 cout << p_curr->data_ << " "; 13 p_curr = p_curr->next_; 14 } 15 cout << endl; 16 }
3、刪除鏈表節點
1 //功能:刪除索引位置為pos的節點 2 void List::remove(int pos) 3 { 4 if (pos < 0 || pos > size_) 5 { 6 return; 7 } 8 Node* p_curr = head_; 9 for (int i = 0; i < pos; i++)// 3 10 { 11 p_curr = p_curr->next_; 12 } 13 p_curr->next_ = p_curr->next_->next_; 14 size_--; 15 }
思想就是找到要刪除的Node的前一個節點,讓前一個節點的指針指向Node的下一個節點就行了。
例如:pos = 3的時候,for循環執行完畢,p_curr表示索引值為2的節點地址,接着我們讓p_curr->next 指向 下一個節點的下一個節點。
4、鏈表中倒數第k個節點
1 //鏈表中倒數第k個節點 2 int List::get_reverse_element(int reverse_pos) 3 { 4 int pos = size_ - reverse_pos; 5 Node* p_curr = head_; 6 for (int i = 0; i < pos; i++) 7 { 8 p_curr = p_curr->next_; 9 } 10 return p_curr->data_; 11 }
你可以去看看劍指offer上的做法,我這里類List維護了一個size_,所以比較簡單。
5、反轉鏈表
修改前:
1 //反轉鏈表 2 void List::reverse() 3 { 4 // head -> 1 -> 2 -> 3 -> 4 -> nullptr 5 //nullptr <- 1 <- 2 <- 3 <- 4 6 7 Node* p_curr = head_; 8 Node* p_prev = nullptr; 9 while (p_curr != nullptr) 10 { 11 Node* p_next = p_curr->next_; 12 if (p_next == nullptr) 13 { 14 head_ = p_curr; 15 } 16 p_curr->next_ = p_prev; 17 p_prev = p_curr; 18 p_curr = p_next; 19 } 20 }

可以看到,有一個元素錯位了。由於第7行 p_curr一開始 = 頭節點指針,執行到第16行的時候,可以看到頭節點的下一個節點是nullptr,但是頭節點本身 data_ = -1; 當循環執行到if語句的時候,你又把第一個節點直接賦值給頭節點,那么頭節點的data_ = 15;這個在類List的print()中是不會打印出來的。
第7、14行做了如下修改:
1 //反轉鏈表 2 void List::reverse() 3 { 4 // head -> 1 -> 2 -> 3 -> 4 -> nullptr 5 //nullptr <- 1 <- 2 <- 3 <- 4 6 7 Node* p_curr = head_->next_; 8 Node* p_prev = nullptr; 9 while (p_curr != nullptr) 10 { 11 Node* p_next = p_curr->next_; 12 if (p_next == nullptr) 13 { 14 head_->next_ = p_curr; 15 } 16 p_curr->next_ = p_prev; 17 p_prev = p_curr; 18 p_curr = p_next; 19 } 20 }

思考:
當初寫出來的鏈表應當是這樣的:
1 //反轉鏈表 2 void List::reverse() 3 { 4 // head -> 1 -> 2 -> 3 -> 4 -> nullptr 5 //nullptr <- 1 <- 2 <- 3 <- 4 6 7 Node* p_curr = head_->next_; 8 Node* p_prev = nullptr; 9 while (p_curr != nullptr) 10 { 11 //Node* p_next = p_curr->next_; 12 //if (p_next == nullptr) 13 if(p_curr->next_ == nullptr) 14 { 15 head_->next_ = p_curr; 16 } 17 p_curr->next_ = p_prev; 18 p_prev = p_curr; 19 //p_curr = p_next; 20 p_curr = p_curr->next_; 21 } 22 }
輸出結果:

首先,必須知道算法第17行是核心!
這里顯然斷鏈了。17行 p_curr->next_ 的賦值導致20行(為了遍歷原鏈表)賦值錯誤。原本,在17行,我們的目的是:將next_指向前一個節點,以達到倒序的目的。所以在執行17行之前,我們必須想辦法緩存地址值:p_curr->next_,也就是11行,緩存之后用於19行刷新p_curr。18行的刷新很好理解。
6、合並兩個排序鏈表
1 //合並兩個排序鏈表 2 void mergeLists(List& list3, List& list4, List& list34) 3 { 4 Node* p_curr3 = list3.head_->next_; 5 Node* p_curr4 = list4.head_->next_; 6 Node* p_curr34 = list34.head_->next_; 7 int location = 0; 8 while ((p_curr3 != nullptr) || (p_curr4 != nullptr)) 9 { 10 if ((p_curr3 != nullptr) && (p_curr4 != nullptr)) 11 { 12 if (p_curr3->data_ < p_curr4->data_) 13 { 14 list34.insert(location, p_curr3->data_); 15 location++; 16 list34.insert(location, p_curr4->data_); 17 location++; 18 } 19 else 20 { 21 list34.insert(location, p_curr4->data_); 22 location++; 23 list34.insert(location, p_curr3->data_); 24 location++; 25 } 26 p_curr3 = p_curr3->next_; 27 p_curr4 = p_curr4->next_; 28 } 29 else if ((p_curr3 != nullptr) && (p_curr4 == nullptr)) 30 { 31 list34.insert(location, p_curr3->data_); 32 location++; 33 p_curr3 = p_curr3->next_; 34 } 35 else if ((p_curr3 == nullptr) && (p_curr4 != nullptr)) 36 { 37 list34.insert(location, p_curr4->data_); 38 location++; 39 p_curr4 = p_curr4->next_; 40 } 41 } 42 }
例如現在有兩個升序序列:
A:0 2 4 6 8 14
B:1 3 5 7 9 12 21 31
要將他們變成一個生序序列;
思路:假設現在兩個序列元素個數相等;我們將 0 1對比將得到 0 1, 再將1和2 3 對比 得到 0 1 2 3;再將3和4 5 對比;依次類推,(對應第10行代碼)
現在考慮 A序列長度 > B序列長度;對應第29行代碼; A序列長度 < B序列長度;對應第35行代碼
1 //合並兩個排序鏈表 2 List list3,list4; 3 for (int i = 0; i < 5; i++) 4 { 5 list3.insert(i, 2*i); 6 list4.insert(i, 2 * i + 1); 7 } 8 list3.insert(5, 14); 9 list4.insert(5, 12); 10 list4.insert(6, 21); 11 list4.insert(7, 31); 12 list3.print(); 13 list4.print(); 14 15 List list34; 16 mergeLists(list3, list4, list34); 17 list34.print();
測試用例:
