对于"单链表逆置和递归"的问题的理解.
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了对于"单链表逆置和递归"的问题的理解.相关的知识,希望对你有一定的参考价值。
一. 相关知识要点:
学习或了解基础数据结构和C语言, 对基础链表知识或相关知识有概况性认识.
例如: 本题目结构为:
1 #define elem_type int
2
3 typedef struct _single_list {
4 elem_type data; //所存的数据元素
5 _single_list *next; //所存的指针元素
6 }ListNode;
二. 问题的思考过程(本题以3种不同的方法解决):
<1>类似于我们学习的C语言基础知识中的冒泡排序(参考C程序设计 第四版谭浩强P147)
说明:
输入数据: 1 7 2 8 4.
得到数据: 4 8 2 1 7.
过程如下:
如图上所示: 第一轮循环结束:
第二轮循环将B放到倒数第二的位置: 如图:
同理: 再循环三次即可完成单链表的逆置: 结果为:
结果如上: 代码如下:
1 void ReverseList_1(ListNode *phead) 2 { 3 assert(phead); 4 ListNode *ptr = phead->next; 5 int len = 0;//作为循环控制变量 6 while(ptr) 7 { 8 ++len; 9 ptr = ptr->next; 10 } 11 12 //p和ptr作为中间变换的参量, 一般情况下p = ptr->next; 13 ListNode *p = NULL; 14 15 for(int i = 0; i < len-1; i++) 16 { 17 ptr = phead->next; 18 for(int j = 0; j < len-i-1; j++) 19 { 20 //用内外循环来控制变量的指向. 21 p = ptr->next; 22 if(ptr == phead->next) 23 { 24 ptr->next = p->next; 25 p->next = ptr; 26 phead->next = p; 27 //在每一次外循环头指针需要维护 28 continue; 29 } 30 ListNode *s = phead->next; 31 //s的目的是找的ptr的前驱, p为ptr后驱 32 while(s->next != ptr) 33 { 34 s = s->next; 35 } 36 ptr->next = p->next; 37 p->next = ptr; 38 s->next = p; 39 //将ptr 放到p的后面 40 } 41 } 42 }
优点: 思想上比较好理解, 容易联系课本来解决问题.
缺点: 代码上仍需几个指针进行控制, 而且三层循环, 不易实现, 效率不是很高.
<2>利用链表的特性, 进行头插.(参考 数据结构 第二版 严蔚敏 P14)
结果如上: 代码如下:
1 void ReverseList_2(ListNode *phead) 2 { 3 assert(phead); 4 ListNode *ptr = phead->next; //相当于图上的p指针 5 ListNode *s = NULL; //一般指向ptr->next 并保存下一个地址 6 phead->next = NULL; 7 while(ptr != NULL) 8 { 9 /* 10 * 先将ptr 头指针的先一个位置进行头插. 11 */ 12 s = ptr->next; 13 ptr->next = phead->next; 14 phead->next = ptr; 15 ptr = s; 16 } 17 }
优点: 联系结构实际, 进行方法调用 , 代码简单, 思想较易.
缺点: 无.
<3> 利用递归调用, 来进行逆置, 这个比较难理解(可以忽略... 参考C程序设计 第四版 谭浩强 P184)
如上所示: 其代码为:
1 ListNode* Recursion_1(ListNode *ptr, ListNode *p) 2 { 3 if(!p) 4 { 5 //递归调用的退出条件 6 return ptr; 7 } 8 else 9 { 10 /* 11 * 递归进入, 并作后续的逆置, 12 */ 13 ListNode *s = Recursion_1(ptr->next, p->next); 14 ptr->next = NULL; 15 p->next = ptr; 16 return s; 17 } 18 } 19 20 ListNode* RecursionReverseList_1(ListNode *phead) 21 { 22 if(!phead && !phead->next) 23 { 24 //判断是否错误 25 return phead; 26 } 27 return Recursion_1(phead->next, phead->next->next); 28 //这个将来为phead->next = s, 成立 29 }
优点:可以对递归的整个流程有一个大致的了解, 并对问题的解决有了一个更深的认识.
缺点:不好想, 代码难实现,
额外扩展: 可不可以将方法二转换成"递归"形式: 其代码如下:
1 void Recursion_2(ListNode *phead, ListNode *ptr) 2 { 3 if(!ptr->next) 4 { 5 return; 6 } 7 /* 8 * 进行头插 9 */ 10 ListNode *s = ptr->next; 11 ptr->next = s->next; 12 s->next = phead->next; 13 phead->next = s; 14 Recursion_2(phead, ptr); 15 } 16 17 void RecursionReverseList_2(ListNode *phead) 18 { 19 if(!phead) 20 { 21 return ; 22 } 23 Recursion_2(phead, phead->next); 24 }
接下来我们进行测试下代码:
1 #include<stdio.h> 2 #include<string.h> 3 #include<assert.h> 4 #include<malloc.h> 5 6 #define ElemType int 7 typedef struct SList 8 { 9 ElemType data; 10 SList *next; 11 }ListNode; 12 /* 13 *购买内存 14 * */ 15 ListNode *buyNode() 16 { 17 ListNode *p = (ListNode*)malloc(sizeof(ListNode)); 18 assert(p); 19 memset(p, ‘\\0‘, sizeof(ListNode)); 20 return p; 21 } 22 /* 23 *删除节点 24 * */ 25 void DeleteNode(ListNode *ptr) 26 { 27 if(NULL == ptr) 28 { 29 return ; 30 } 31 free(ptr); 32 ptr = NULL; 33 } 34 /* 35 *普通的删除单链表 36 * */ 37 void DeleteList(ListNode *phead) 38 { 39 ListNode *ptr = phead->next; 40 if(NULL == ptr) 41 { 42 return ; 43 } 44 ListNode *p = NULL; 45 while(ptr) 46 { 47 p = ptr->next; 48 DeleteNode(ptr); 49 ptr = p; 50 } 51 } 52 /* 53 *递归删除单链表 54 * */ 55 void RecursionDeleteList(ListNode *ptr) 56 { 57 if(NULL == ptr->next) 58 { 59 return ; 60 } 61 RecursionDeleteList(ptr->next); 62 DeleteNode(ptr->next); 63 ptr->next = NULL; 64 } 65 /* 66 *打印单链表 67 * */ 68 void PrintList(ListNode *phead) 69 { 70 ListNode *ptr = phead->next; 71 while(ptr) 72 { 73 printf(" %d", ptr->data); 74 ptr = ptr->next; 75 } 76 } 77 /* 78 *头插法建立单链表 79 * */ 80 void PushFront(ListNode *phead, ElemType key) 81 { 82 ListNode *p = buyNode(); 83 p->data = key; 84 p->next = phead->next; 85 phead->next = p; 86 } 87 /* 88 *方法_1 89 * */ 90 void ReverseList_1(ListNode *phead) 91 { 92 assert(phead); 93 ListNode *ptr = phead->next; 94 int len = 0;//作为循环控制变量 95 while(ptr) 96 { 97 ++len; 98 ptr = ptr->next; 99 } 100 101 //p和ptr作为中间变换的参量, 一般情况下p = ptr->next; 102 ListNode *p = NULL; 103 104 for(int i = 0; i < len-1; i++) 105 { 106 ptr = phead->next; 107 for(int j = 0; j < len-i-1; j++) 108 { 109 //用内外循环来控制变量的指向. 110 p = ptr->next; 111 if(ptr == phead->next) 112 { 113 ptr->next = p->next; 114 p->next = ptr; 115 phead->next = p; 116 //在每一次外循环头指针需要维护 117 continue; 118 } 119 ListNode *s = phead->next; 120 //s的目的是找的ptr的前驱, p为ptr后驱 121 while(s->next != ptr) 122 { 123 s = s->next; 124 } 125 ptr->next = p->next; 126 p->next = ptr; 127 s->next = p; 128 //将ptr 放到p的后面 129 } 130 } 131 } 132 /* 133 *方法_2 134 * */ 135 void ReverseList_2(ListNode *phead) 136 { 137 assert(phead); 138 ListNode *ptr = phead->next; //相当于图上的p指针 139 ListNode *s = NULL; //一般指向ptr->next 并保存下一个地址 140 phead->next = NULL; 141 while(ptr != NULL) 142 { 143 /* 144 * 先将ptr 头指针的先一个位置进行头插. 145 */ 146 s = ptr->next; 147 ptr->next = phead->next; 148 phead->next = ptr; 149 ptr = s; 150 } 151 } 152 /* 153 *方法_3 154 * */ 155 ListNode* Recursion_1(ListNode *ptr, ListNode *p) 156 { 157 if(!p) 158 { 159 //递归调用的退出条件 160 return ptr; 161 } 162 else 163 { 164 /* 165 * 递归进入, 并作后续的逆置, 166 */ 167 ListNode *s = Recursion_1(ptr->next, p->next); 168 ptr->next = NULL; 169 p->next = ptr; 170 return s; 171 } 172 } 173 174 ListNode* RecursionReverseList_1(ListNode *phead) 175 { 176 if(!phead && !phead->next) 177 { 178 //判断是否错误 179 return phead; 180 } 181 return Recursion_1(phead->next, phead->next->next); 182 //这个将来为phead->next = s, 成立 183 } 184 185 void Recursion_2(ListNode *phead, ListNode *ptr) 186 { 187 if(!ptr->next) 188 { 189 return; 190 } 191 /* 192 * 进行头插 193 */ 194 ListNode *s = ptr->next; 195 ptr->next = s->next; 196 s->next = phead->next; 197 phead->next = s; 198 Recursion_2(phead, ptr); 199 } 200 /* 201 *方法_2R 202 * */ 203 void RecursionReverseList_2(ListNode *phead) 204 { 205 if(!phead) 206 { 207 return ; 208 } 209 Recursion_2(phead, phead->next); 210 } 211 /* 212 * 打印地址和数据, 目的是为了更好的测试 213 */ 214 void PrintAddrData(ListNode *phead) 215 { 216 assert(phead); 217 ListNode *p = phead->next; 218 printf("phead = %p\\t, phead->next = %p\\n", phead, phead->next); 219 int i = 0; 220 while(p) 221 { 222 i++; 223 printf("\\ttimes = %d\\t Address = %p\\t, data = %d\\n", i, p, p->data); 224 p = p->next; 225 } 226 } 227 228 int main() 229 { 230 231 /* 232 *初始化过程 233 */ 234 ListNode phead; 235 memset(&phead, ‘\\0‘, sizeof(phead)); 236 int arr[] = {12, 45, 62, 9, 89, 32, 16, 54}; 237 //int arr[] = {1, 7, 2, 8, 4}; 238 int len = sizeof(arr)/sizeof(arr[0]); 239 240 for(int i = 0; i < len; ++i) 241 { 242 PushFront(&phead, arr[i]); 243 } 244 printf("\\nprintList(phead), test\\n"); 245 PrintList(&phead); 246 247 /* 248 * 测试text 249 */ 250 printf("\\n_test_1 \\tReverseList_1(phead)\\n"); 251 ReverseList_1(&phead); 252 PrintAddrData(&phead); 253 254 printf("\\n_test_2 \\t ReverseList_2(phead)\\n"); 255 ReverseList_2(&phead); 256 PrintAddrData(&phead); 257 258 printf("\\n_text_3 \\tRecursionReverseList_1(phead)\\n"); 259 phead.next = RecursionReverseList_1(&phead); 260 PrintAddrData(&phead); 261 262 263 printf("\\n_test_2R \\tRecursionReverseList_2(phead)\\n"); 264 RecursionReverseList_2(&phead); 265 PrintAddrData(&phead); 266 /* 267 * 删除单链表 268 */ 269 RecursionDeleteList(&phead); 270 return 0; 271 }
测试结果为:
结果如上图所示.
总结:
通过以上分析, 和自己的测试, 对于单链表的逆置递归问题, 我觉得大家应该有一个清晰地认识了. 如果以上有任何的问题和错误,
同时希望大家可以指正.
以上是关于对于"单链表逆置和递归"的问题的理解.的主要内容,如果未能解决你的问题,请参考以下文章
单链表的逆置使用递归算法出现-842150451这个值,请求高手予以解决。。