究竟要如何反转呢?我们不妨拿一个例子来说明一下算法。
第一次交换
第二次交换
第三次交换
步骤:
- 定义当前结点 current,初始值为首元结点,current = L->next;
- 定义当前结点的后继结点 pnext, pnext = current->next;
- 只要 pnext 存在,就执行以下循环:
- 定义新节点 prev,它是 pnext的后继结点,prev = pnext->next;
- 把pnext的后继指向current, pnext->next = current;
- 此时,pnext 实际上已经到了 current 前一位成为新的current,所以这个时候 current 结点实际上成为新的 pnext,current = pnext;
- 此时,新的 current 就是 pnext,current = pnext;
- 而新的 pnext 就是 prev,pnext = prev;
- 最后将头结点与 current 重新连上即可,L->next = current;
函数设计如下:
01 | /* 单链表反转/逆序 */ |
02 | Status ListReverse(LinkList L) |
03 | { |
04 | LinkList current,pnext,prev; |
05 | if (L == NULL || L->next == NULL) |
06 | return L; |
07 | current = L->next; /* p1指向链表头节点的下一个节点 */ |
08 | pnext = current->next; |
09 | current->next = NULL; |
10 | while (pnext) |
11 | { |
12 | prev = pnext->next; |
13 | pnext->next = current; |
14 | current = pnext; |
15 | pnext = prev; |
16 | printf ( "交换后:current = %d,next = %d \n" ,current->data,current->next->data); |
17 | } |
18 | //printf("current = %d,next = %d \n",current->data,current->next->data); |
19 | L->next = current; /* 将链表头节点指向p1 */ |
20 | return L; |
21 | } |
01 | Status ListReverse2(LinkList L) |
02 | { |
03 | LinkList current, p; |
04 |
05 | if (L == NULL) |
06 | { |
07 | return NULL; |
08 | } |
09 | current = L->next; |
10 | while (current->next != NULL) |
11 | { |
12 | p = current->next; |
13 | current->next = p->next; |
14 | p->next = L->next; |
15 | L->next = p; |
16 | } |
17 | return L; |
18 | } |
- p = current->next; p 就相当于前面的 pnext。(图1中a2即为p)
- current->next = p->next; p->next 就相当于 prev的角色,这句代码意思是 current 的后继指向 prev.(相当于图1中a1->next = a3(a2->next))
- p->next = L->next; 这句就是 p 的后继直接指向首元节点。(相当于图1中a2->next = a1)
- L->next = p; 然后再将头结点指向 p。(相当于图1中L->next = a2)
这个是程序运行的结果。
01 | 整体创建L的元素(头插法): |
02 | // 原链表,current = 68, pnext = 55,68指向18,55指向18,头结点指向55 |
03 | -> 68 -> 55 -> 18 -> 45 -> 41 -> 43 -> 5 -> 28 -> 80 -> 67 |
04 |
05 | // 第一次交换后,原链表变成这样 |
06 | -> 55 -> 68 -> 18 -> 45 -> 41 -> 43 -> 5 -> 28 -> 80 -> 67 |
07 | // 进行第二次交换,pnext = 18,68指向45,18变成头结点 |
08 | -> 18 -> 55 -> 68 -> 45 -> 41 -> 43 -> 5 -> 28 -> 80 -> 67 |
09 | // 进行第三次交换,pnext = current->next = 45,68指向41,45变成头结点 |
10 | -> 45 -> 18 -> 55 -> 68 -> 41 -> 43 -> 5 -> 28 -> 80 -> 67 |
11 | // …… |
12 | -> 41 -> 45 -> 18 -> 55 -> 68 -> 43 -> 5 -> 28 -> 80 -> 67 |
13 |
14 | -> 43 -> 41 -> 45 -> 18 -> 55 -> 68 -> 5 -> 28 -> 80 -> 67 |
15 |
16 | -> 5 -> 43 -> 41 -> 45 -> 18 -> 55 -> 68 -> 28 -> 80 -> 67 |
17 |
18 | -> 28 -> 5 -> 43 -> 41 -> 45 -> 18 -> 55 -> 68 -> 80 -> 67 |
19 |
20 | -> 80 -> 28 -> 5 -> 43 -> 41 -> 45 -> 18 -> 55 -> 68 -> 67 |
21 | // current 68 没有后继,反转结束 |
22 | -> 67 -> 80 -> 28 -> 5 -> 43 -> 41 -> 45 -> 18 -> 55 -> 68 |
23 |
24 |
25 | 反转L后 |
26 | -> 67 -> 80 -> 28 -> 5 -> 43 -> 41 -> 45 -> 18 -> 55 -> 68 |
最后附上完整代码,反转有两个函数。
- 方法1,current始终保持在第一位,pnext与prev遍历并完成交换。
- 方法2,current始终是原链表的第一个数,然后把pnext不断移动到首位。
001 | #include "stdio.h" |
002 |
003 | #define OK 1 |
004 | #define ERROR 0 |
005 | #define TRUE 1 |
006 | #define FALSE 0 |
007 |
008 | #define MAXSIZE 20 /* 存储空间初始分配量 */ |
009 |
010 | typedef int Status; /* Status是函数的类型,其值是函数结果状态代码,如OK等 */ |
011 | typedef int ElemType; /* ElemType类型根据实际情况而定,这里假设为int */ |
012 |
013 | typedef struct Node |
014 | { |
015 | ElemType data; |
016 | struct Node *next; |
017 | }Node; |
018 | /* 定义LinkList */ |
019 | typedef struct Node *LinkList; |
020 |
021 | /* 初始化顺序线性表 */ |
022 | Status InitList(LinkList *L) |
023 | { |
024 | *L=(LinkList) malloc ( sizeof (Node)); /* 产生头结点,并使L指向此头结点 */ |
025 | if (!(*L)) /* 存储分配失败 */ |
026 | { |
027 | return ERROR; |
028 | } |
029 | (*L)->next=NULL; /* 指针域为空 */ |
030 |
031 | return OK; |
032 | } |
033 |
034 | /* 初始条件:顺序线性表L已存在。操作结果:返回L中数据元素个数 */ |
035 | int ListLength(LinkList L) |
036 | { |
037 | int i=0; |
038 | LinkList p=L->next; /* p指向第一个结点 */ |
039 | while (p) |
040 | { |
041 | i++; |
042 | p=p->next; |
043 | } |
044 | return i; |
045 | } |
046 |
047 | /* 初始条件:顺序线性表L已存在。操作结果:将L重置为空表 */ |
048 | Status ClearList(LinkList *L) |
049 | { |
050 | LinkList p,q; |
051 | p=(*L)->next; /* p指向第一个结点 */ |
052 | while (p) /* 没到表尾 */ |
053 | { |
054 | q=p->next; |
055 | free (p); |
056 | p=q; |
057 | } |
058 | (*L)->next=NULL; /* 头结点指针域为空 */ |
059 | return OK; |
060 | } |
061 |
062 | /* 初始条件:顺序线性表L已存在 */ |
063 | /* 操作结果:依次对L的每个数据元素输出 */ |
064 | Status ListTraverse(LinkList L) |
065 | { |
066 | LinkList p=L->next; |
067 | while (p) |
068 | { |
069 | visit(p->data); |
070 | p=p->next; |
071 | } |
072 | printf ( "\n" ); |
073 | return OK; |
074 | } |
075 |
076 | Status visit(ElemType c) |
077 | { |
078 | printf ( "-> %d " ,c); |
079 | return OK; |
080 | } |
081 |
082 | /* 初始条件:顺序线性表L已存在,1≤i≤ListLength(L) */ |
083 | /* 操作结果:用e返回L中第i个数据元素的值 */ |
084 | Status GetElem(LinkList L, int i,ElemType *e) |
085 | { |
086 | int j; |
087 | LinkList p; /* 声明一结点p */ |
088 | p = L->next; /* 让p指向链表L的第一个结点 */ |
089 | j = 1; /* j为计数器 */ |
090 | while (p && j < i) /* p不为空或者计数器j还没有等于i时,循环继续 */ |
091 | { |
092 | p = p->next; /* 让p指向下一个结点 */ |
093 | ++j; |
094 | } |
095 | if ( !p || j>i ) |
096 | return ERROR; /* 第i个元素不存在 */ |
097 | *e = p->data; /* 取第i个元素的数据 */ |
098 | return OK; |
099 | } |
100 |
101 | /* 初始条件:顺序线性表L已存在 */ |
102 | /* 操作结果:返回L中第1个与e满足关系的数据元素的位序。 */ |
103 | /* 若这样的数据元素不存在,则返回值为0 */ |
104 | int LocateElem(LinkList L,ElemType e) |
105 | { |
106 | int i=0; |
107 | LinkList p=L->next; |
108 | while (p) |
109 | { |
110 | i++; |
111 | if (p->data==e) /* 找到这样的数据元素 */ |
112 | return i; |
113 | p=p->next; |
114 | } |
115 |
116 | return 0; |
117 | } |
118 |
119 | /* 随机产生n个元素的值,建立带表头结点的单链线性表L(头插法) */ |
120 | void CreateListHead(LinkList *L, int n) |
121 | { |
122 | LinkList p; |
123 | int i; |
124 | srand ( time (0)); /* 初始化随机数种子 */ |
125 | *L = (LinkList) malloc ( sizeof (Node)); |
126 | (*L)->next = NULL; /* 先建立一个带头结点的单链表 */ |
127 | for (i=0; i < n; i++) |
128 | { |
129 | p = (LinkList) malloc ( sizeof (Node)); /* 生成新结点 */ |
130 | p->data = rand ()%100+1; /* 随机生成100以内的数字 */ |
131 | p->next = (*L)->next; |
132 | (*L)->next = p; /* 插入到表头 */ |
133 | } |
134 | } |
135 |
136 | /* 随机产生n个元素的值,建立带表头结点的单链线性表L(尾插法) */ |
137 | void CreateListTail(LinkList *L, int n) |
138 | { |
139 | LinkList p,r; |
140 | int i; |
141 | srand ( time (0)); /* 初始化随机数种子 */ |
142 | *L = (LinkList) malloc ( sizeof (Node)); /* L为整个线性表 */ |
143 | r=*L; /* r为指向尾部的结点 */ |
144 | for (i=0; i < n; i++) |
145 | { |
146 | p = (Node *) malloc ( sizeof (Node)); /* 生成新结点 */ |
147 | p->data = rand ()%100+1; /* 随机生成100以内的数字 */ |
148 | r->next=p; /* 将表尾终端结点的指针指向新结点 */ |
149 | r = p; /* 将当前的新结点定义为表尾终端结点 */ |
150 | } |
151 | r->next = NULL; /* 表示当前链表结束 */ |
152 | } |
153 |
154 | /* 初始条件:顺序线性表L已存在,1≤i≤ListLength(L), */ |
155 | /* 操作结果:在L中第i个位置之前插入新的数据元素e,L的长度加1 */ |
156 | Status ListInsert(LinkList *L, int i,ElemType e) |
157 | { |
158 | int j; |
159 | LinkList p,s; |
160 | p = *L; /* 声明一个结点 p,指向头结点 */ |
161 | j = 1; |
162 | while (p && j < i) /* 寻找第i个结点 */ |
163 | { |
164 | p = p->next; |
165 | ++j; |
166 | } |
167 | if (!p || j > i) |
168 | return ERROR; /* 第i个元素不存在 */ |
169 | s = (LinkList) malloc ( sizeof (Node)); /* 生成新结点(C语言标准函数) */ |
170 | s->data = e; |
171 | s->next = p->next; /* 将p的后继结点赋值给s的后继 */ |
172 | p->next = s; /* 将s赋值给p的后继 */ |
173 | return OK; |
174 | } |
175 |
176 | /* 初始条件:顺序线性表L已存在,1≤i≤ListLength(L) */ |
177 | /* 操作结果:删除L的第i个数据元素,并用e返回其值,L的长度减1 */ |
178 | Status ListDelete(LinkList *L, int i,ElemType *e) |
179 | { |
180 | int j; |
181 | LinkList p,q; |
182 | p = *L; |
183 | j = 1; |
184 | while (p->next && j < i) /* 遍历寻找第i个元素 */ |
185 | { |
186 | p = p->next; |
187 | ++j; |
188 | } |
189 | if (!(p->next) || j > i) |
190 | return ERROR; /* 第i个元素不存在 */ |
191 | q = p->next; |
192 | p->next = q->next; /* 将q的后继赋值给p的后继 */ |
193 | *e = q->data; /* 将q结点中的数据给e */ |
194 | free (q); /* 让系统回收此结点,释放内存 */ |
195 | return OK; |
196 | } |
197 |
198 | /* 单链表反转/逆序 */ |
199 | Status ListReverse(LinkList L) |
200 | { |
201 | LinkList current,pnext,prev; |
202 | if (L == NULL || L->next == NULL) |
203 | return L; |
204 | current = L->next; /* p1指向链表头节点的下一个节点 */ |
205 | pnext = current->next; |
206 | current->next = NULL; |
207 | while (pnext) |
208 | { |
209 | prev = pnext->next; |
210 | pnext->next = current; |
211 | current = pnext; |
212 | pnext = prev; |
213 | } |
214 | //printf("current = %d,next = %d \n",current->data,current->next->data); |
215 | L->next = current; /* 将链表头节点指向p1 */ |
216 | return L; |
217 | } |
218 |
219 | Status ListReverse2(LinkList L) |
220 | { |
221 | LinkList current, p; |
222 |
223 | if (L == NULL) |
224 | { |
225 | return NULL; |
226 | } |
227 | current = L->next; |
228 | while (current->next != NULL) |
229 | { |
230 | p = current->next; |
231 | current->next = p->next; |
232 | p->next = L->next; |
233 | L->next = p; |
234 | ListTraverse(L); |
235 | printf ( "current = %d, \n" , current -> data); |
236 | } |
237 | return L; |
238 | } |
239 |
240 | int main() |
241 | { |
242 | LinkList L; |
243 | Status i; |
244 | int j,k,pos,value; |
245 | char opp; |
246 | ElemType e; |
247 |
248 | i=InitList(&L); |
249 | printf ( "链表L初始化完毕,ListLength(L)=%d\n" ,ListLength(L)); |
250 |
251 | printf ( "\n1.整表创建(头插法) \n2.整表创建(尾插法) \n3.遍历操作 \n4.插入操作" ); |
252 | printf ( "\n5.删除操作 \n6.获取结点数据 \n7.查找某个数是否在链表中 \n8.置空链表" ); |
253 | printf ( "\n9.链表反转逆序" ); |
254 | printf ( "\n0.退出 \n请选择你的操作:\n" ); |
255 | while (opp != '0' ){ |
256 | scanf ( "%c" ,&opp); |
257 | switch (opp){ |
258 | case '1' : |
259 | CreateListHead(&L,10); |
260 | printf ( "整体创建L的元素(头插法):\n" ); |
261 | ListTraverse(L); |
262 | printf ( "\n" ); |
263 | break ; |
264 |
265 | case '2' : |
266 | CreateListTail(&L,10); |
267 | printf ( "整体创建L的元素(尾插法):\n" ); |
268 | ListTraverse(L); |
269 | printf ( "\n" ); |
270 | break ; |
271 |
272 | case '3' : |
273 | ListTraverse(L); |
274 | printf ( "\n" ); |
275 | break ; |
276 |
277 | case '4' : |
278 | printf ( "要在第几个位置插入元素?" ); |
279 | scanf ( "%d" ,&pos); |
280 | printf ( "插入的元素值是多少?" ); |
281 | scanf ( "%d" ,&value); |
282 | ListInsert(&L,pos,value); |
283 | ListTraverse(L); |
284 | printf ( "\n" ); |
285 | break ; |
286 |
287 | case '5' : |
288 | printf ( "要删除第几个元素?" ); |
289 | scanf ( "%d" ,&pos); |
290 | ListDelete(&L,pos,&e); |
291 | printf ( "删除第%d个元素成功,现在链表为:\n" , pos); |
292 | ListTraverse(L); |
293 | printf ( "\n" ); |
294 | break ; |
295 |
296 | case '6' : |
297 | printf ( "你需要获取第几个元素?" ); |
298 | scanf ( "%d" ,&pos); |
299 | GetElem(L,pos,&e); |
300 | printf ( "第%d个元素的值为:%d\n" , pos, e); |
301 | printf ( "\n" ); |
302 | break ; |
303 |
304 | case '7' : |
305 | printf ( "输入你需要查找的数:" ); |
306 | scanf ( "%d" ,&pos); |
307 | k=LocateElem(L,pos); |
308 | if (k) |
309 | printf ( "第%d个元素的值为%d\n" ,k,pos); |
310 | else |
311 | printf ( "没有值为%d的元素\n" ,pos); |
312 | printf ( "\n" ); |
313 | break ; |
314 |
315 | case '8' : |
316 | i=ClearList(&L); |
317 | printf ( "\n清空L后:ListLength(L)=%d\n" ,ListLength(L)); |
318 | ListTraverse(L); |
319 | printf ( "\n" ); |
320 | break ; |
321 |
322 | case '9' : |
323 | ListReverse2(L); |
324 | printf ( "\n反转L后\n" ); |
325 | ListTraverse(L); |
326 | printf ( "\n" ); |
327 | break ; |
328 |
329 | case '0' : |
330 | exit (0); |
331 | } |
332 | } |
333 |
334 | } |