一. 相关知识要点:

    学习或了解基础数据结构和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下:

结果如上图所示.

总结:

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

同时希望大家可以指正.

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

  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. Django数据同步过程中遇到的问题:

    1.raise ImproperlyConfigured('mysqlclient 1.3.13 or newer is required; you have %s.' % Database.__ve ...

  2. 小菜鸟从0基础开始学Linux系统

    随着当今信息时代的迅速发展,Linux凭借其诸多优势从操作系统中脱颖而出,受到越来越多电脑用户的青睐.Linux是一个集安全.稳定.自由等众多优点于一身的操作系统,不可思议的是这么好的系统还是免费的! ...

  3. C# 应用程序单例(禁止多开) 获取.net版本号 以及 管理员权限

    Mutex不仅提供跨线程的服务,还提供跨进程的服务.当在构造函数中为Mutex指定名称时,则会创建一个命名了的Mutex.其他线程创建Mutex时,如果指定的名称相同,则返回同一个互斥体,不论该线程位 ...

  4. ES(Elasticsearch)

    基本概念 Elasticsearch是一个实时分布式搜索和分析引擎 支持: 全文搜索 结构化搜索 分析 可以这样进行描述: 分布式的实时文件存储,每个字段都被索引并可被搜索 分布式的实时分析搜索引擎 ...

  5. 浅析android系统设计中的回调思想

    一.为何写作本文  在慢慢深入接触android开发的过程中,我越来越发现android中(至少应用曾的开发)用到了很多回调的思想.比如activity的生命周期,fragment的生命周期,皆是回调 ...

  6. pip install beautifulsoup4.失败

    在学习python爬虫时,用到bs4解析网页,开始遇到安装bs出错 Collecting beautifulsoup4Exception:Traceback (most recent call las ...

  7. table-layout:fixed; 表格比例固定

    固定表格布局: 固定表格布局与自动表格布局相比,允许浏览器更快地对表格进行布局. 在固定表格布局中,水平布局仅取决于表格宽度.列宽度.表格边框宽度.单元格间距,而与单元格的内容无关. 通过使用固定表格 ...

  8. C# 鼠标点击移动窗体代码,可以实现无边框窗体的拖动

    private static bool IsDrag = false; private int enterX; private int enterY; private void setForm_Mou ...

  9. 2D 加速图形界面开发源代码亲写 想买来学习得加qq 313244484 20万当前代码,完整400万包写完

    #include "StdAfx.h" #include "GUIFrame.h" #include <stdlib.h> #include < ...

  10. SpringCloud系列------Config-Server

    关于Config-Server的那些事&踩过的坑! 一.概述 在一个相对成熟,高可用的Spring cloud项目中,都会配置一个配置中心去管理各个服务的配置文件,而往往配置文件不会放到本地配 ...