您的位置:首页 > 其它

单向链表逆序 图解

2014-06-19 09:46 423 查看

单链表反转/逆序的两种方法

比较两种思路的差异

在 2012年07月05日 那天写的     已经有 12799 次阅读了
感谢
参考或原文

前面我们大约把单链表 ADT 的基本操作都过了一遍,但是这还不够。单链表在面试与笔试中出现的几率很高,接下来我们再花点时间把常见的单链表面试题尽可能过一遍,彻底掌握单链表~

那开始我们的第一个面试题?不妨做做“单链表反转”,或者说“单链表逆序”吧?还是基于前面的例子。

究竟要如何反转呢?我们不妨拿一个例子来说明一下算法。

我先画一个单链表,这个单链表有4个元素。我的思路就是,每次把第二个元素提到最前面来。比如下面是第一次交换,我们先让头结点的next域指向结点a2,再让结点a1的next域指向结点a3,最后将结点a2的next域指向结点a1,就完成了第一次交换。



[align=center]第一次交换[/align]

然后进行相同的交换将结点a3移动到结点a2的前面,然后再将结点a4移动到结点a3的前面就完成了反转。



[align=center]第二次交换[/align]



[align=center]第三次交换[/align]

思路有了,那就可以写代码了。这里我们需要额外的两个工作指针来辅助交换。这个下面的步骤慢慢理解下,结合图片。注意结点之间的关系要先断再连。

步骤:

定义当前结点 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
}
其实在你写函数的时候,我也写了个函数,也能运行。思路也差不多,不过你的current一直是表的第一个结点,我这里的current始终是首元结点的值,我的函数需要每次对pnext重新赋值。一会解释下。

01
Status ListReverse2(LinkList L)
02
{
03
    
LinkList current, p;
04
 
05
    
if

(L == NULL)
06
    
{
07
<
2075b
/td>
        
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)

参照图就很容易理解上面的步骤了。我觉得我这么写比你的清晰一些。我先将current指向prev,再将pnext指向current,最后将头结点指向pnext。

这个是程序运行的结果。

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

原文地址:http://www.nowamagic.net/librarys/veda/detail/2241
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
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: