对于"单链表逆置和递归"的问题的理解.
一. 相关知识要点:
学习或了解基础数据结构和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. ...
随机推荐
- scrapy框架学习第一天
今天是学习的第一天: 知识总结如下: 1,调试器相当于原料出口地(URL提供) 2,scrapy相当于中间加工商(具有销售权利)封装URL为request(请求) 3,下载器使用request(请求) ...
- 正则表达式,grep,sed,
答案详见:http://www.cnblogs.com/linhaifeng/p/6596660.html 作业一:整理正则表达式博客 ^ # 行首定位 $ # 行尾定位 . # 匹配除换行符以外的任 ...
- 20. Valid Parentheses ★
题目内容: Given a string containing just the characters '(', ')', '{', '}', '[' and ']', determine if th ...
- 7. Reverse Integer Add to List★
题目内容: Reverse digits of an integer. Example1: x = 123, return 321Example2: x = -123, return -321 题目分 ...
- ts文件分析(纯c解析代码)
参考链接: 1. MPEG-2 TS码流分析 https://blog.csdn.net/zhubin215130/article/details/8958567 TS Header PAT PMT ...
- generator mybatis逆向工程
mybatis逆向工程:根据数据库中的表在项目中生成对应的 实体类,dao接口与mapper.xml映射文件 在eclipse中,以插件的形式的存在,把设置好的配置文件,直接通过eclipse中的插件 ...
- python orm框架
#!/usr/bin/python# -*- coding: utf-8 -*-from sqlalchemy import create_enginefrom sqlalchemy import T ...
- Async CallBack promise fetch
Callback (回调函数) 在计算机程序设计中,回调函数,或简称回调(Callback 即call then back 被主函数调用运算后会返回主函数),是指通过函数参数传递到其它代码的,某一块可 ...
- Handler使用小结
个人概念里面handler用来更新UI.一直有一个问题困恼我,为什么我在主线程里面创建一个Handler不需要传递传递Looper,而在一个子线程里面必须调用Looper.prepare, Loope ...
- python 游戏 —— 汉诺塔(Hanoita)
python 游戏 —— 汉诺塔(Hanoita) 一.汉诺塔问题 1. 问题来源 问题源于印度的一个古老传说,大梵天创造世界的时候做了三根金刚石柱子,在一根柱子上从下往上按照大小顺序摞着64片黄金圆 ...