一. 相关知识要点:

    学习或了解基础数据结构和C语言, 对基础链表知识或相关知识有概况性认识.

  例如: 本题目结构为:

  1. #define elem_type int
  2. typedef struct _single_list {
  3. elem_type data;  //所存的数据元素
  4. _single_list *next;  //所存的指针元素
  5. }ListNode;

二. 问题的思考过程(本题以3种不同的方法解决):

  <1>类似于我们学习的C语言基础知识中的冒泡排序(参考C程序设计 第四版谭浩强P147)

说明:

   输入数据: 1  7  2  8  4.

   得到数据: 4  8   2  7  1.

    过程如下:

  

  

   如图上所示: 第一轮循环结束:   

  第二轮循环将B放到倒数第二的位置: 如图:

  

  同理: 再循环三次即可完成单链表的逆置: 结果为:

  

  结果如上:  代码如下:

  1. void ReverseList_1(ListNode *phead)
  2. {
  3. assert(phead);
  4. ListNode *ptr = phead->next;
  5. int len = ;//作为循环控制变量
  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 = ; i < len-; i++)
  16. {
  17. ptr = phead->next;
  18. for(int j = ; j < len-i-; 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 = ;//作为循环控制变量
  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 = ; i < len-; i++)
  105. {
  106. ptr = phead->next;
  107. for(int j = ; j < len-i-; 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 = ;
  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[] = {, , , , , , , };
  237. //int arr[] = {1, 7, 2, 8, 4};
  238. int len = sizeof(arr)/sizeof(arr[]);
  239.  
  240. for(int i = ; 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. printf("\n_test_2R \tRecursionReverseList_2(phead)\n");
  263. RecursionReverseList_2(&phead);
  264. PrintAddrData(&phead);
  265. /*
  266. * 删除单链表
  267. */
  268. RecursionDeleteList(&phead);
  269. return ;
  270. }

  测试结果为:  在linux下 gcc编译:

 在vs下:

结果如上图所示.

总结:

  通过以上分析, 和自己的测试, 对于单链表的逆置递归问题, 我觉得大家应该有一个清晰地认识了. 如果以上有任何的问题和错误,

同时希望大家可以指正.

对于"单链表逆置和递归"的问题的理解.的更多相关文章

  1. java实现单链表逆置

    class LNode { public LNode next; public int data; } /*逆置链表*/ class Nizhi { private static LNode head ...

  2. 数据结构-链表逆置(c++模板类实现)

    链表结点类模板定义: template <class T> class SingleList; template <class T> class Node { private: ...

  3. PTA 链表逆置

    6-3 链表逆置 (20 分)   本题要求实现一个函数,将给定单向链表逆置,即表头置为表尾,表尾置为表头.链表结点定义如下: struct ListNode { int data; struct L ...

  4. 基于visual Studio2013解决面试题之0504单链表逆序

     题目

  5. C# 单向链表 逆序(递归)

    static void Main(string[] args) { while (true) { LinkedList L = new LinkedList(); L.Add(new Node(&qu ...

  6. Java 单链表逆序

    代码: package com.wangzhu.linkedlist; public class LinkedListDemo { /** * @param args */ public static ...

  7. C++面试笔记--单链表

    1.编程实现单链表删除节点.       解析:如果删除的是头节点,如下图: 则把head指针指向头节点的下一个节点.同时free p1,如下图所示: 如果删除的是中间节点,如下图所示: 则用p2的n ...

  8. 【链表问题】打卡9:将单链表的每K个节点之间逆序

    前言 以专题的形式更新刷题贴,欢迎跟我一起学习刷题,相信我,你的坚持,绝对会有意想不到的收获.每道题会提供简单的解答,如果你有更优雅的做法,欢迎提供指点,谢谢. 注:如果代码排版出现了问题麻烦通知我下 ...

  9. C语言 链表的创建--打印--逆置--新增--删除--排序--释放

    #define _CRT_SECURE_NO_WARNINGS #include<stdio.h> #include<stdlib.h> #include<string. ...

随机推荐

  1. 关于Servlet的一些归纳(2)

    1.web项目结构 根路径: 文件夹 文件 WEB-INF: lib(存放一些jar文件) classes(存放class文件) web.xml 2.GenericServlet类 实现了Servle ...

  2. jdk和cglib动态代理

    一.原理区别:java动态代理是利用反射机制生成一个实现代理接口的匿名类,在调用具体方法前调用InvokeHandler来处理. 而cglib动态代理是利用asm开源包,对代理对象类的class文件加 ...

  3. 小白的首个maven web项目Step1软件安装一(jdk的下载配置和eclipse的安装)

    因为电脑太卡加了一个固态硬盘导致系统重装, 把之前的C盘和D盘合并成一个盘,这下之前下的杂七杂八的软件应该差不多都清干净了. 申请这个博客是想记录最近写项目学习的一些事,系统重装之后,发现自己都已经忘 ...

  4. windows+nginx 查看并发链接数

    1.windows下nginx查看并发链接数要使用stable版本 2.配置代码: location /status { stub_status on; } 3.访问地址:http://localho ...

  5. xcode的打包上线出问题:导入此构建版本时出错

    原因:升级mac系统到了High sierra(10.13,目前还是测试版,并没有正式版,全新的文件系统 APFS (Apple File System))发现没有这个问题,于是乎,所以给出这种解决方 ...

  6. url参数解析 and 日期格式化

    ~function (pro) { //url解析 function queryURLParameter() { var reg = /([^?&=#]+)=([^?&=#]+)/g, ...

  7. java 构造方法详解

    构造方法(构造器)    是一种特殊的方法,该方法只有功能:构造对象    特点:        1.没有返回值        2.构造方法的名称一定和类名一致        3.不能在构造方法中写r ...

  8. jsp脚本的九个内置对象

    JSP脚本中包含9个内置对象, 这9个内置对象都是Servlet API 接口实例, 只是JSP规范对它们进行了默认初始化(由JSP 页面对应Servlet 的jspService()方法来创建这些实 ...

  9. Linux安装软件、python开发环境

    软件安装与卸载 更新Ubuntu软件下载地址 1. 寻找国内镜像源 所谓的镜像源:可以理解为提供下载软件的地方,比如Android手机上可以下载软件的91手机助手:iOS手机上可以下载软件的AppSt ...

  10. Java中的公平锁和非公平锁实现详解

    前言 Java语言中有许多原生线程安全的数据结构,比如ArrayBlockingQueue.CopyOnWriteArrayList.LinkedBlockingQueue,它们线程安全的实现方式并非 ...