【原】手寫鏈表(C++)


  • 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 }
View Code

 

 

 

  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();

測試用例:

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM