链表有很多种,有单项、双向链表,带环、不带环链表,带头节点、不带头节点链表,而这些链表又可以两两或者三个三个组成一种链表,下面主要是说带不头节点不带环单向链表的一些操作。
链表初始化
链表初始化有三种方法
方法一:
4 //1.链表初始化
5 void LinkListInit(LinkNode** node)
6 {
7 *node = NULL;
8 }
9
方法二:
10 void LinkListInit2(LinkNode* node)
11 {
12 node = NULL;
13 }
14
15 LinkNode* LinkListInit3()
16 {
17 return NULL;
18 }
19
尾部插入一个节点——尾插
首先要创建一个新的节点
20 LinkNode* CreateNode(LinkNodeType value)
21 {
22 LinkNode *new_node=(LinkNode*)malloc(sizeof(LinkNode));
23 new_node->data=value;
24 new_node->next=NULL;
25 return new_node;
26 }
然后要判断链表是否合法,和链表是否为空,若链表为空,则这个问题就直接转化为创建一个新节点的问题了,若链表不为空,则设一个指针cur指向链表的头节点,并建立一个循环,让cur指针一直指向cur的下一个节点,直至cur的下一个节点为NULL,则循环结束,此时cur指针一定指向链表的最后一个节点,这时创建一个新节点,并让cur的next指向新创建的节点,并且让新创建的节点的next重新指向NULL,详细见图
![](https://i-blog.csdnimg.cn/blog_migrate/5825508270963c8f3da753a10206803b.png)
27 //2.尾插
28 void LinkListPushBack(LinkNode** phead, LinkNodeType value)
29 {
30 if(phead == NULL)
31 {
32 //非法输入
33 return ;
34 }
35 if(*phead == NULL)
36 {
37 //空链表
38 *phead=CreateNode(value);
39 return ;
40 }
41 //链表非空
42 LinkNode *cur=*phead;
43 while(cur->next!=NULL)
44 {
45 cur=cur->next;
46 }
47 LinkNode *new_node=CreateNode(value);
48 cur->next=new_node;
49 new_node->next=NULL;
50 return ;
51 }
尾部删除一个节点——尾删
首先
要判断链表是否合法、是否为空,若链表链表只有一个元素,则这个问题就转化为销毁链表节点的问题
53 void DestroyNode(LinkNode *node)
54 {
55 //free(*node->data);
56 free(node);
57 return ;
58 }
59
若链表不只有一个元素,则设一个指针cur指向链表的头节点,设一个指针pre指向NULL,并建立一个循环,让pre指针一直指向指针cur,cur指向cur的下一个节点,直至cur的下一个节点为NULL,则循环结束,此时cur指针一定指向链表的最后一个节点,而pre指针指向链表的倒数第二个节点,这时让pre的next指向NULL,并销毁cur节点,详细见图
![](https://i-blog.csdnimg.cn/blog_migrate/01f3b717d798af08b25fb075a40bc67f.png)
60 //3.尾删
61 void LinkListPopBack(LinkNode** phead)
62 {
63 if(phead == NULL)
64 {
65 //非法输入
66 return ;
67 }
68 if(*phead==NULL)
69 {
70 //空链表
71 return ;
72 }
73 if((*phead)->next==NULL)
74 {
75 //只有一个元素
76 DestroyNode(*phead);
77 *phead=NULL;
78 return ;
79 }
80 LinkNode *cur=*phead;
81 LinkNode *pre=NULL;
82 while(cur->next!=NULL)
83 {
84 pre=cur;
85 cur=cur->next;
86 }
87 //当循环结束,cur指向最后一个节点,pre指向倒数第二个节点
88 pre->next=NULL;
89 DestroyNode(cur);
90 return ;
91 }
92
头部插入一个节点——头插
首先
要判断链表是否合法,然后要创建一个新的节点new_node,让new_node的next指向链表的头节点,再让链表的头节点等于new_node,详细见图
![](https://i-blog.csdnimg.cn/blog_migrate/f714dc50dbd72a9f92f13a52d0266a36.png)
93 //4.头插
94 void LinkListPushFront(LinkNode** phead,LinkNodeType value)
95 {
96 if(phead == NULL)
97 {
98 //非法输入
99 return ;
100 }
101 LinkNode* new_node=CreateNode(value);
102 new_node->next=*phead;
103 *phead=new_node;
104 }
105
头部删除一个节点——头删
首先
要判断链表是否合法、是否为空,然后定义一个指针to_erase指向要删除的元素—头节点phead,让原先的头节点phead,指向第二个节点,然后销毁to_erase几点
,详细见图
![](https://i-blog.csdnimg.cn/blog_migrate/84dc7b22462e4c4380becb129ae14e90.png)
106 //5.头删
107 void LinkListPopFront(LinkNode** phead)
108 {
109 if(phead == NULL)
110 {
111 //非法输入
112 return ;
113 }
114 if(*phead == NULL)
115 {
116 //空链表
117 return ;
118 }
119 LinkNode* to_erase=*phead;
120 *phead=(*phead)->next;
121 DestroyNode(to_erase);
122 return ;
123 }
将一个新节点插入到pos位置之后
首先要创建一个新的节点new_node,然后让new_node的next指向pos位置的next,再让pos位置的next指向new_node,详细见图
![](https://i-blog.csdnimg.cn/blog_migrate/6f3e1993aab65191ef167d442dcb5f2a.png)
125 //6.将一个新节点插入到pos之后
126 void LinkListInsert(LinkNode* phead,LinkNode* pos,LinkNodeType value)
127 {
128 if(pos == NULL)
129 {
130 //非法输入
131 //pos表示一个节点的指针,若pos为空,则表示根本不存在这样的节点
132 return ;
133 }
134 LinkNode* new_node=CreateNode(value);
135 new_node->next=pos->next;
136 pos->next=new_node;
137 return ;
138
139 }
将一个新节点插入到pos位置之前
首先要判断pos位置在哪,若pos为第一个元素,则问题转化为链表的头插法;若pos位置不是链表的第一个元素,则要对链表进遍历来找到pos位置,若找到了就进行插入操作,若没找到,则不进行操作
141 将一个新节点插入到pos之前
142 void LinkListInsertBefore(LinkNode** phead,LinkNode* pos,LinkNodeType value)
143 {
144 if(phead == NULL || pos == NULL)
145 {
146 //非法输入
147 return ;
148 }
149 if(*phead == pos)
150 {
151 //要插入的位置为头结点
152 LinkListPushFront(phead,value);
153 return ;
154 }
155 LinkNode *cur=*phead;
156 for(;cur!=NULL;cur=cur->next)
157 {
158 if(cur->next==pos)
159 {
160 break;
161 }
162 }
163 //循环结束后要知道是由于哪种情况导致的循环结束到底找没找到pos
164 if(cur == NULL)
165 {
166 //没找到
167 return ;
168 }
169 LinkListInsert(phead,cur,&value);
170 return ;
171 }
172
173
上面
这种方法的时间复杂度为O(n),下面将对上面的代码进行优化,使时间复杂度从O(n)变为O(1)
即先将元素插入到pos位置之后,再将Pos位置的元素和新插入的元素互换详细见图
![](https://i-blog.csdnimg.cn/blog_migrate/89e0148926acc5827514ea1992b2028b.png)
175 //7.对LinkListInsertBefore进行优化O(n)->O(1)
176 void LinkListInsertBefore2(LinkNode* pos,LinkNodeType value)
177 {
178 if(pos == NULL)
179 {
180 //非法输入
181 return ;
182 }
183 LinkNode* new_node=CreateNode(pos->data);
184 new_node->next=pos->next;
185 pos->next=new_node;
186 pos->data=value;
187 //或者将上面四行代码改为
188 //LinkListInsert(pos,pos->data);
189 //pos->data=value;
190 }
删除pos位置节点
要删除pos位置的节点首先要找到pos节点的位置,那么就要遍历链表,这样这个方法的时间复杂度就为O(n)
192 //8.删除pos位置节点
193 void LinkListErase(LinkNode** phead,LinkNode* pos)
194 {
195 if(phead == NULL || pos == NULL)
196 {
197 //非法输入
198 return ;
199 }
200 if(*phead == NULL)
201 {
202 //空链表
203 return ;
204 }
205 LinkNode *cur=*phead;
206 for(;cur!=NULL;cur=cur->next)
207 {
208 if(cur->next==pos)
209 {
210 break;
211 }
212 }
213 //循环结束之后要判定是找到了退出还是没找到pos退出
214 if(cur == NULL)
215 {
216 return ;
217 }
218 cur->next=pos->next;
219 DestroyNode(pos);
220 return ;
221 }
下面对上面的方法进行优化,将时间复杂度从O(n)变为O(1)
223 //对LinkListErase进行优化
224 void LinkListErase2(LinkNode** phead,LinkNode* pos)
225 {
226 if(phead == NULL || pos == NULL)
227 {
228 //非法输入
229 return ;
230 }
231 if(*phead == NULL)
232 {
233 //空链表
234 return ;
235 }
236 if(pos->next == NULL)
237 {
238 //要删除的元素为最后一个元素
239 LinkListPopBack(phead);
240 return ;
241 }
242 pos->data=pos->next->data;
243 LinkNode* to_erase=pos->next;
244 pos->next=to_erase->next;
245 DestroyNode(to_erase);
246 }
查找指定元素的位置
首先要创建一个指针cur指向链表的头部,然后构造一个循环,让如果cur的值就是要找的元素,则返回cur指针的位置,此循环直到cur为NULL时,循环结束
248 //9.找到节点就返回节点对应的地址,若没找到就返回NULL
249 LinkNode* LinkListFind(LinkNode* head,LinkNodeType to_find)
250 {
251 if(head == NULL)
252 {
253 //空链表
254 return NULL;
255 }
256 LinkNode* cur=head;
257 while(cur!=NULL)
258 {
259 if(cur->data==to_find)
260 {
261 return cur;
262 }
263 cur=cur->next;
264 }
265 return NULL;
266 }
删除指定元素
首先要判断是否合法,和链表是否为空,然后若要删除的元素恰好为第一个元素,则就是头删问题,否则,就需要创建一个指针cur,开始是指向链表的开头,然后让cr指针依次往后面移动,直至cur->next指向要删除的元素,即cur指向要删除的元素的前一个元素,然后方法类似于尾删,具体见下面程序
268 //10删除指定元素
269 void LinkListRemove(LinkNode** phead,LinkNodeType to_remove)
270 {
271 if(phead == NULL)
272 {
273 //非法输入
274 return ;
275 }
276 if(*phead == NULL)
277 {
278 //空链表
279 return ;
280 }
281 //删除的元素恰好是第一个
282 if((*phead)->data==to_remove)
283 {
284 LinkNode* to_delete=*phead;
285 *phead=(*phead)->next;
286 DestroyNode(to_delete);
287 return ;
288 }
289 LinkNode* cur=*phead;
290 for(;cur->next!=NULL;cur=cur->next)
291 {
292 if(cur->next->data==to_remove)
293 {
294 //cur指向要删除的元素的前一个元素的位置
295 LinkNode* to_delete=cur->next;
296 cur->next=to_delete->next;
297 DestroyNode(to_delete);
298 }
299 }
300 return ;
301 }
根据值删除所有的元素
首先需要判断链表是否合法和链表是否为空,然后创建一个循环,来找到链表中所有要删除元素的位置,若找到了,则将元素全部删除
303 //11.删除所有指定的元素
304 void LinkListRemoveall(LinkNode** phead,LinkNodeType to_remove)
305 {
306 if(phead == NULL)
307 {
308 //非法输入
309 return ;
310 }
311 if(*phead == NULL)
312 {
313 //空链表
314 return ;
315 }
316 while(1)
317 {
318 LinkNode* pos=LinkListFind(*phead,to_remove);
319 if(pos==NULL)
320 {
321 //没有找到
322 return ;
323 }
324 LinkListErase(phead,pos);
325 }
326 return ;
327 }