对于"单链表逆置和递归"的问题的理解.
一. 相关知识要点:
学习或了解基础数据结构和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. ...
随机推荐
- centos6.8安装mysql5.5
在使用阿里云的时候发现centos6.8系统自带的源当中没有mysql5.5,在网易这些源里面也是5.1的版本.这里安装mysql5.5的话需要另外添加一个源. 1.查看是否已经安装了MySQL rp ...
- 大米网赚项目介绍,官方唯一客服QQ:486594009
大米平台项目来源 QQ:486594009 大米软件本质上是一个高质量网赚项目收集和发布平台,该平台的所有项目都是经过专业的测试团队实测有效的项目和教程,只要去做绝对可以赚钱.平台里面的项目类型包 ...
- Python _Mix*10
函数的动态参数 *args位置参数,动态传参 **kwargs关键字参数,动态传参 顺序:位置→*args→默认值→**kwargs 形参的位置*,**:聚合 实参的位置*,**:打散 (可以把lis ...
- 201671010142 2017-2 《java第十一章学习感悟》
事件处理基础 事件源,事件监听器,事件监听器 监听器接口的实现,监听器对象所属类必须实现与事件源相对应的接口,即必须提供接口中方法的实现. 适配器类 当程序用户试图关闭一个框架窗口时,Jframe对 ...
- 在Linux CentOS6系统中安装开源CMS程序OpenCart的教程
OpenCart是一个开放源码的店面,旨在为您提供灵活和细粒度的在线店面管理.在开始之前,您应该已经在您的Linode上设置了一个LAMP堆栈.您还应该设置主机名. PHP设置 为了使用OpenCar ...
- explor img file
1, get offset # parted bone-debian----4gb.img GNU Parted 3.1 Using /workspace/bone-debian----4gb.img ...
- ecplise的Jsp红叉错误消除
当做项目的时候,引入的jsp文件可能会出现红叉的情况,重新输入任意字符再删除,保存后恢复正常. 原因是由于jsp中的方法还没有实现,等方法写完之后,再使jsp文件重新处于编译状态,保存后恢复正常.
- day13 内置函数一
见如下网址 https://www.processon.com/mindmap/5bdc3f49e4b0844e0bc6b5d3
- Linux:Debian系统的安装
这期就安装与Ubantu.Linux Mint差不多的系统,但是这个Debian系统的话,它安装时却有些不一样,什么不一样,我们接下来看看吧! 安装Debian系统 系统下载 Debian 9.5下载 ...
- 关于MySQL5.7开启bin-log主从复制
主从复制:一般用于实时备份.也可配合mycat,实现读写分离. 传统的基于 ROW的主从复制 简单说下步骤: master主库配置同步,slave从库配置同步,master锁表/备份,slave恢复数 ...