对于"单链表逆置和递归"的问题的理解.
一. 相关知识要点:
学习或了解基础数据结构和C语言, 对基础链表知识或相关知识有概况性认识.
例如: 本题目结构为:
- #define elem_type int
- typedef struct _single_list {
- elem_type data; //所存的数据元素
- _single_list *next; //所存的指针元素
- }ListNode;
二. 问题的思考过程(本题以3种不同的方法解决):
<1>类似于我们学习的C语言基础知识中的冒泡排序(参考C程序设计 第四版谭浩强P147)
说明:
输入数据: 1 7 2 8 4.
得到数据: 4 8 2 7 1.
过程如下:
如图上所示: 第一轮循环结束:
第二轮循环将B放到倒数第二的位置: 如图:
同理: 再循环三次即可完成单链表的逆置: 结果为:
结果如上: 代码如下:
- void ReverseList_1(ListNode *phead)
- {
- assert(phead);
- ListNode *ptr = phead->next;
- int len = ;//作为循环控制变量
- while(ptr)
- {
- ++len;
- ptr = ptr->next;
- }
- //p和ptr作为中间变换的参量, 一般情况下p = ptr->next;
- ListNode *p = NULL;
- for(int i = ; i < len-; i++)
- {
- ptr = phead->next;
- for(int j = ; j < len-i-; j++)
- {
- //用内外循环来控制变量的指向.
- p = ptr->next;
- if(ptr == phead->next)
- {
- ptr->next = p->next;
- p->next = ptr;
- phead->next = p;
- //在每一次外循环头指针需要维护
- continue;
- }
- ListNode *s = phead->next;
- //s的目的是找的ptr的前驱, p为ptr后驱
- while(s->next != ptr)
- {
- s = s->next;
- }
- ptr->next = p->next;
- p->next = ptr;
- s->next = p;
- //将ptr 放到p的后面
- }
- }
- }
优点: 思想上比较好理解, 容易联系课本来解决问题.
缺点: 代码上仍需几个指针进行控制, 而且三层循环, 不易实现, 效率不是很高.
<2>利用链表的特性, 进行头插.(参考 数据结构 第二版 严蔚敏 P14)
结果如上: 代码如下:
- void ReverseList_2(ListNode *phead)
- {
- assert(phead);
- ListNode *ptr = phead->next; //相当于图上的p指针
- ListNode *s = NULL; //一般指向ptr->next 并保存下一个地址
- phead->next = NULL;
- while(ptr != NULL)
- {
- /*
- * 先将ptr 头指针的先一个位置进行头插.
- */
- s = ptr->next;
- ptr->next = phead->next;
- phead->next = ptr;
- ptr = s;
- }
- }
优点: 联系结构实际, 进行方法调用 , 代码简单, 思想较易.
缺点: 无.
<3> 利用递归调用, 来进行逆置, 这个比较难理解(可以忽略... 参考C程序设计 第四版 谭浩强 P184)
如上所示: 其代码为:
- ListNode* Recursion_1(ListNode *ptr, ListNode *p)
- {
- if(!p)
- {
- //递归调用的退出条件
- return ptr;
- }
- else
- {
- /*
- * 递归进入, 并作后续的逆置,
- */
- ListNode *s = Recursion_1(ptr->next, p->next);
- ptr->next = NULL;
- p->next = ptr;
- return s;
- }
- }
- ListNode* RecursionReverseList_1(ListNode *phead)
- {
- if(!phead && !phead->next)
- {
- //判断是否错误
- return phead;
- }
- return Recursion_1(phead->next, phead->next->next);
- //这个将来为phead->next = s, 成立
- }
优点:可以对递归的整个流程有一个大致的了解, 并对问题的解决有了一个更深的认识.
缺点:不好想, 代码难实现,
额外扩展: 可不可以将方法二转换成"递归"形式: 其代码如下:
- void Recursion_2(ListNode *phead, ListNode *ptr)
- {
- if(!ptr->next)
- {
- return;
- }
- /*
- * 进行头插
- */
- ListNode *s = ptr->next;
- ptr->next = s->next;
- s->next = phead->next;
- phead->next = s;
- Recursion_2(phead, ptr);
- }
- void RecursionReverseList_2(ListNode *phead)
- {
- if(!phead)
- {
- return ;
- }
- Recursion_2(phead, phead->next);
- }
接下来我们进行测试下代码:
- #include<stdio.h>
- #include<string.h>
- #include<assert.h>
- #include<malloc.h>
- #define ElemType int
- typedef struct SList
- {
- ElemType data;
- SList *next;
- }ListNode;
- /*
- *购买内存
- * */
- ListNode *buyNode()
- {
- ListNode *p = (ListNode*)malloc(sizeof(ListNode));
- assert(p);
- memset(p, '\0', sizeof(ListNode));
- return p;
- }
- /*
- *删除节点
- * */
- void DeleteNode(ListNode *ptr)
- {
- if(NULL == ptr)
- {
- return ;
- }
- free(ptr);
- ptr = NULL;
- }
- /*
- *普通的删除单链表
- * */
- void DeleteList(ListNode *phead)
- {
- ListNode *ptr = phead->next;
- if(NULL == ptr)
- {
- return ;
- }
- ListNode *p = NULL;
- while(ptr)
- {
- p = ptr->next;
- DeleteNode(ptr);
- ptr = p;
- }
- }
- /*
- *递归删除单链表
- * */
- void RecursionDeleteList(ListNode *ptr)
- {
- if(NULL == ptr->next)
- {
- return ;
- }
- RecursionDeleteList(ptr->next);
- DeleteNode(ptr->next);
- ptr->next = NULL;
- }
- /*
- *打印单链表
- * */
- void PrintList(ListNode *phead)
- {
- ListNode *ptr = phead->next;
- while(ptr)
- {
- printf(" %d", ptr->data);
- ptr = ptr->next;
- }
- }
- /*
- *头插法建立单链表
- * */
- void PushFront(ListNode *phead, ElemType key)
- {
- ListNode *p = buyNode();
- p->data = key;
- p->next = phead->next;
- phead->next = p;
- }
- /*
- *方法_1
- * */
- void ReverseList_1(ListNode *phead)
- {
- assert(phead);
- ListNode *ptr = phead->next;
- int len = ;//作为循环控制变量
- while(ptr)
- {
- ++len;
- ptr = ptr->next;
- }
- //p和ptr作为中间变换的参量, 一般情况下p = ptr->next;
- ListNode *p = NULL;
- for(int i = ; i < len-; i++)
- {
- ptr = phead->next;
- for(int j = ; j < len-i-; j++)
- {
- //用内外循环来控制变量的指向.
- p = ptr->next;
- if(ptr == phead->next)
- {
- ptr->next = p->next;
- p->next = ptr;
- phead->next = p;
- //在每一次外循环头指针需要维护
- continue;
- }
- ListNode *s = phead->next;
- //s的目的是找的ptr的前驱, p为ptr后驱
- while(s->next != ptr)
- {
- s = s->next;
- }
- ptr->next = p->next;
- p->next = ptr;
- s->next = p;
- //将ptr 放到p的后面
- }
- }
- }
- /*
- *方法_2
- * */
- void ReverseList_2(ListNode *phead)
- {
- assert(phead);
- ListNode *ptr = phead->next; //相当于图上的p指针
- ListNode *s = NULL; //一般指向ptr->next 并保存下一个地址
- phead->next = NULL;
- while(ptr != NULL)
- {
- /*
- * 先将ptr 头指针的先一个位置进行头插.
- */
- s = ptr->next;
- ptr->next = phead->next;
- phead->next = ptr;
- ptr = s;
- }
- }
- /*
- *方法_3
- * */
- ListNode* Recursion_1(ListNode *ptr, ListNode *p)
- {
- if(!p)
- {
- //递归调用的退出条件
- return ptr;
- }
- else
- {
- /*
- * 递归进入, 并作后续的逆置,
- */
- ListNode *s = Recursion_1(ptr->next, p->next);
- ptr->next = NULL;
- p->next = ptr;
- return s;
- }
- }
- ListNode* RecursionReverseList_1(ListNode *phead)
- {
- if(!phead && !phead->next)
- {
- //判断是否错误
- return phead;
- }
- return Recursion_1(phead->next, phead->next->next);
- //这个将来为phead->next = s, 成立
- }
- void Recursion_2(ListNode *phead, ListNode *ptr)
- {
- if(!ptr->next)
- {
- return;
- }
- /*
- * 进行头插
- */
- ListNode *s = ptr->next;
- ptr->next = s->next;
- s->next = phead->next;
- phead->next = s;
- Recursion_2(phead, ptr);
- }
- /*
- *方法_2R
- * */
- void RecursionReverseList_2(ListNode *phead)
- {
- if(!phead)
- {
- return ;
- }
- Recursion_2(phead, phead->next);
- }
- /*
- * 打印地址和数据, 目的是为了更好的测试
- */
- void PrintAddrData(ListNode *phead)
- {
- assert(phead);
- ListNode *p = phead->next;
- printf("phead = %p\t, phead->next = %p\n", phead, phead->next);
- int i = ;
- while(p)
- {
- i++;
- printf("\ttimes = %d\t Address = %p\t, data = %d\n", i, p, p->data);
- p = p->next;
- }
- }
- int main()
- {
- /*
- *初始化过程
- */
- ListNode phead;
- memset(&phead, '\0', sizeof(phead));
- int arr[] = {, , , , , , , };
- //int arr[] = {1, 7, 2, 8, 4};
- int len = sizeof(arr)/sizeof(arr[]);
- for(int i = ; i < len; ++i)
- {
- PushFront(&phead, arr[i]);
- }
- printf("\nprintList(phead), test\n");
- PrintList(&phead);
- /*
- * 测试text
- */
- printf("\n_test_1 \tReverseList_1(phead)\n");
- ReverseList_1(&phead);
- PrintAddrData(&phead);
- printf("\n_test_2 \t ReverseList_2(phead)\n");
- ReverseList_2(&phead);
- PrintAddrData(&phead);
- printf("\n_text_3 \tRecursionReverseList_1(phead)\n");
- phead.next = RecursionReverseList_1(&phead);
- PrintAddrData(&phead);
- printf("\n_test_2R \tRecursionReverseList_2(phead)\n");
- RecursionReverseList_2(&phead);
- PrintAddrData(&phead);
- /*
- * 删除单链表
- */
- RecursionDeleteList(&phead);
- return ;
- }
测试结果为: 在linux下 gcc编译:
在vs下:
结果如上图所示.
总结:
通过以上分析, 和自己的测试, 对于单链表的逆置递归问题, 我觉得大家应该有一个清晰地认识了. 如果以上有任何的问题和错误,
同时希望大家可以指正.
对于"单链表逆置和递归"的问题的理解.的更多相关文章
- java实现单链表逆置
class LNode { public LNode next; public int data; } /*逆置链表*/ class Nizhi { private static LNode head ...
- 数据结构-链表逆置(c++模板类实现)
链表结点类模板定义: template <class T> class SingleList; template <class T> class Node { private: ...
- PTA 链表逆置
6-3 链表逆置 (20 分) 本题要求实现一个函数,将给定单向链表逆置,即表头置为表尾,表尾置为表头.链表结点定义如下: struct ListNode { int data; struct L ...
- 基于visual Studio2013解决面试题之0504单链表逆序
题目
- C# 单向链表 逆序(递归)
static void Main(string[] args) { while (true) { LinkedList L = new LinkedList(); L.Add(new Node(&qu ...
- Java 单链表逆序
代码: package com.wangzhu.linkedlist; public class LinkedListDemo { /** * @param args */ public static ...
- C++面试笔记--单链表
1.编程实现单链表删除节点. 解析:如果删除的是头节点,如下图: 则把head指针指向头节点的下一个节点.同时free p1,如下图所示: 如果删除的是中间节点,如下图所示: 则用p2的n ...
- 【链表问题】打卡9:将单链表的每K个节点之间逆序
前言 以专题的形式更新刷题贴,欢迎跟我一起学习刷题,相信我,你的坚持,绝对会有意想不到的收获.每道题会提供简单的解答,如果你有更优雅的做法,欢迎提供指点,谢谢. 注:如果代码排版出现了问题麻烦通知我下 ...
- C语言 链表的创建--打印--逆置--新增--删除--排序--释放
#define _CRT_SECURE_NO_WARNINGS #include<stdio.h> #include<stdlib.h> #include<string. ...
随机推荐
- 关于Servlet的一些归纳(2)
1.web项目结构 根路径: 文件夹 文件 WEB-INF: lib(存放一些jar文件) classes(存放class文件) web.xml 2.GenericServlet类 实现了Servle ...
- jdk和cglib动态代理
一.原理区别:java动态代理是利用反射机制生成一个实现代理接口的匿名类,在调用具体方法前调用InvokeHandler来处理. 而cglib动态代理是利用asm开源包,对代理对象类的class文件加 ...
- 小白的首个maven web项目Step1软件安装一(jdk的下载配置和eclipse的安装)
因为电脑太卡加了一个固态硬盘导致系统重装, 把之前的C盘和D盘合并成一个盘,这下之前下的杂七杂八的软件应该差不多都清干净了. 申请这个博客是想记录最近写项目学习的一些事,系统重装之后,发现自己都已经忘 ...
- windows+nginx 查看并发链接数
1.windows下nginx查看并发链接数要使用stable版本 2.配置代码: location /status { stub_status on; } 3.访问地址:http://localho ...
- xcode的打包上线出问题:导入此构建版本时出错
原因:升级mac系统到了High sierra(10.13,目前还是测试版,并没有正式版,全新的文件系统 APFS (Apple File System))发现没有这个问题,于是乎,所以给出这种解决方 ...
- url参数解析 and 日期格式化
~function (pro) { //url解析 function queryURLParameter() { var reg = /([^?&=#]+)=([^?&=#]+)/g, ...
- java 构造方法详解
构造方法(构造器) 是一种特殊的方法,该方法只有功能:构造对象 特点: 1.没有返回值 2.构造方法的名称一定和类名一致 3.不能在构造方法中写r ...
- jsp脚本的九个内置对象
JSP脚本中包含9个内置对象, 这9个内置对象都是Servlet API 接口实例, 只是JSP规范对它们进行了默认初始化(由JSP 页面对应Servlet 的jspService()方法来创建这些实 ...
- Linux安装软件、python开发环境
软件安装与卸载 更新Ubuntu软件下载地址 1. 寻找国内镜像源 所谓的镜像源:可以理解为提供下载软件的地方,比如Android手机上可以下载软件的91手机助手:iOS手机上可以下载软件的AppSt ...
- Java中的公平锁和非公平锁实现详解
前言 Java语言中有许多原生线程安全的数据结构,比如ArrayBlockingQueue.CopyOnWriteArrayList.LinkedBlockingQueue,它们线程安全的实现方式并非 ...