【原】手写链表(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();

测试用例:

原文地址:https://www.cnblogs.com/winslam/p/10089004.html