对于"单链表逆置和递归"的问题的理解.
一. 相关知识要点:
学习或了解基础数据结构和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. ...
随机推荐
- MySQL数据库SQL语句基本操作
一.用户管理: 创建用户: create user '用户名'@'IP地址' identified by '密码'; 删除用户: drop user '用户名'@'IP地址'; 修改用户: renam ...
- FORTH 发展(部分)
body, table{font-family: 微软雅黑; font-size: 13.5pt} table{border-collapse: collapse; border: solid gra ...
- 第四周博客之一---Linux的基本命令(前5个)
一.Linux的系统结构 "/"根目录部分有以下子目录: 1./bin:系统启动时需要的执行文件(二进制),这些文件可以被普通用户使用. 2./boot:用于自举加载程序(LILO ...
- Python中的 *args 和 **kwargs
基本概念 Python支持可变参数,最简单的方法莫过于使用默认参数. def test_defargs(one, two=2): # 参数one没有默认值,two的默认值为2 print('Requi ...
- 慢阻肺疾病管理APP——第一次迭代心得
时光匆匆,不知不觉就到了第十二周.——第一次迭代都完成了,最终迭代还会远吗? 一.第一次迭代的设想和目标: 1. 我们的软件要解决什么问题?是否定义得很清楚?是否对典型用户和典型场景有清晰的描述? ...
- boostrap 日期插件(带中文显示)
不知道大家用什么样的日期插件,我最近用了bootstrap插件,整理了下,分享给大家,第四部分是我找的插件源码,可以省去大家的找的时间,按1-3的步骤用就好了,有些样式上的小问题就自己调一调: (1) ...
- Spring 基础知识(一)基本概念 DI、IOC、AOP
DI(依赖注入) 和IOC(控制反转)都是一种设计思想,要理解他们,让我们从coding中的一些痛点入手. 依赖注入 Dependency Injection : 如果A类要使用B类的一个方法,首先必 ...
- FINS/TCP_OMRON(1)
使用FINS/ TCP与欧姆龙PLC沟通 可参考下列教学 using System.Net; using System.Net.Sockets; 上面必须使用; IPAddress ipAddr = ...
- .NET并行计算和并发8:硬件支持
共享内存多核系统,分布式内存系统 区别 分布式内存系统主要通过Message passing interface在各个微处理器之间通信,但是MPI共享内存多核系统是没有必要的,会造成额外的开销. 分布 ...
- win8 tiles风格标签插件jquery.wordbox.js
http://www.html580.com/12180 jquery.wordbox.js轻松实现win8瓦片tiles式风格标签插件,只需要调用JS就能轻松实现瓦片菜单,自定义菜单背景颜色,支持响 ...