单向链表反转,就地逆置与递归反转(无表头结点)

最近在看链表,今天刷到一道链表的反转题,链表反转可以说是基础操作,但是可提供的方案也有很多,简单通过了该题后又学习了一下递归反转,现在把三种方法都公开出来做一个总结。

1.就地逆置

2.单参数的递归逆置

3.双参数的递归逆置

一、就地逆置

方法:头插。

由于这里是不带表头结点的单向链表,所以头插会稍微复杂一点,不想往下看的小伙伴也可以直接选择定义一个临时表头结点从头结点开始遍历链表将每一个链表头插,最后将头结点指向表头结点的next指针域,最后free掉那个表头结点即可。

虽然不带表头结点头插会稍微复杂一些,但是只要加一条语句就可以解决问题。

在不带表头结点的单向链表中进行头插,唯一要特殊处理的地方就是头结点。而中间结点和尾结点的头插可以简化为下面的代码段

pre->next  = head;

head = pre;

在找到普遍规律后,接下来就要考虑特殊情况是否能适应普遍规律。在这里如果pre指向链表头结点,pre->next = head;则产生一个环,导致的结果就是在链表遍历结束后,尾部会产生一个长度为1的环。

而我们想要的尾部应该是指向NULL的,所以很显然,if pre == head,  pre->next = NULL; 为了使代码更加美观具有统一性,所以我们增设一个指针 q,初始值为NULL即可。

代码如下

ElemSN *ReverseList(ElemSN *head)
{
  ElemSN *q = NULL;
  ElemSN *p = h; // 指针p 遍历链表
  while(p){
    ElemSN *pre = p; // pre指向当前要处理的结点
    p = p->next;  // p 走到下一个结点候命
    m->next = q;
    q = head = m;
  }
  return head;
}

二、单参数递归逆置

  如果单向链表可以从后向前遍历,那我想要逆置链表不就变得容易的多,只需要从尾结点开始不断的让尾结点指向前一个结点即可。

  但事实上单向链表只能单向遍历,如果想要直接逆向遍历是几乎不可能的,但是递归间接的帮我们实现了这个功能。

  先摆一下函数声明

  ElemSN *ReverseList(ElemSN *head);

  递归无非就是自己调用自己,理解递归其实很容易,在递归里,ReverseList函数每调用自己一次,都相当于将该函数全部代码粘贴到函数调用处一次,直到某次调用自己时触发了return 返回,然后不断的返回返回,直到返回到第一次调用,最终返回一个结果值。

  因此看一个简单的递归函数

ElemSN *ReverseList(ElemSN *head)
{
  if(head->next == NULL || head == NULL){
    return head;
  }  ElemSN *nextNode = head->next;
  ElemSN *reverseNode = ReverseList(nextNode);
  return reverseNode;
}

该函数通过不断调用自己实现对链表的遍历,并且该函数将head->next == NULL作为函数第一次的返回点,也就是当形参head到达尾结点时开始返回,之后每次返回都会将该结点传至上一层调用,直至到达最顶层调用时返回尾结点。

可见递归,从最顶层开始逐级调用自己的过程是从头到尾遍历链表的过程,而从最底层调用开始逐级返回则是一个由尾到头遍历链表的过程。而我们要做的逆置操作就从函数逐级返回开始,因此我们就可以利用这一特性写出下面的递归逆置代码

ElemSN *ReverseList(ElemSN *head)

{

  if(head->next == NULL || head == NULL){

    return head; // 设定最底层调用返回点,到达尾结点后开始返回
  }

  ElemSN *nextNode = head->next;  

  head->next = NULL; //很重要的一条语句哦,如果省略依旧会在新生成的链表尾部产生一个长度为1的环哦,可以注释掉本条代码运行再查看一下输出结果

  ElemSN *reverseNode = ReverseList(nextNode); // 递归遍历链表

  nextNode->next = head; // 开始逐级返回开始后,尾变头,原来的结点序中,后一个结点依次指向前一个结点

  return reverseNode; // 每次返回都带回原尾结点(现在的头结点)

}

三、双参数递归逆置

  从上一个方法中了解到递归逆置无非就是在逐级返回这个过程中 不断让后一个结点指向前一个结点。

  那我们显然可以直接将前后结点作为函数形参在递归过程中依次遍历链表,当后结点到达尾结点时,开始逐级返回并让后结点的next指向前结点,当然这里需要记得在函数返回时传递尾结点(逆置后的头结点)

代码如下

ElemSN *ReverseList(ElemSN *ptr, ElemSN *pre)
{
  if(ptr->next == NULL || ptr == NULL){
    ptr->next = pre;
    return ptr;
  }
  ElemSN *reverseNode = ReverseList(ptr->next, ptr);
  ptr->next = pre;
  return reverseNode;
}

在这个函数中不用担心会在尾部生成一个环,递归,从哪里开始,最终又会回到哪里,所以在调用该函数时 可以使用 head = ReverseList(head, NULL);的语句,这样子,函数的起点状态将是 ,ptr = head, pre = NULL,最终也会是这个状态,所以 在最后一次返回该函数时

ptr->next = pre;相当于我们传进去的头指针next指向了NULL,这样逆置后的链表尾部自然就是NULL了。

End。

原文地址:https://www.cnblogs.com/GuoYuying/p/11455844.html

时间: 2024-08-29 10:33:44

单向链表反转,就地逆置与递归反转(无表头结点)的相关文章

单链表的就地逆置问题

问题描述:编写一个单链表的成员函数,实现对带头结点的单链表的就地逆置操作 涉及变量:position:Node型变量,用于存放尚未反转的结点中首结点的位置 temp:用于标记执行反转操作的结点 涉及教材:<数据结构--Java语言描述(第2版)> 清华大学出版社 大致思路: 将头结点的指针域设为空,再将原来的结点从首结点开始依次连接在头结点之后,即可将原来位置倒置 代码如下: 以上是我对单链表就地逆置的一些理解,希望对各位有所帮助 190116 Rewivy 原文地址:https://www.

单链表就地逆置

题目:有一个线性表(a1,a2,a3,...,an),采用带头节点的单链表L存储,设计一个算法将其就地逆置,线性表变为(an,...a3,a2,a1).所谓"就地"指辅助存储空间为O(1). 解题思路: 如果是顺序存储的话,我们很容易想到解题思路,利用1个辅助变量让第1个元素与第n个元素交换,然后再利用这个辅助变量让第2个元素与第n-1个元素交换,...最后利用这个辅助变量让第n/2个元素与第n+1-n/2个元素交换. 如果不要求"就地"的话,可以创建一个n个元素辅

单链表就地逆置(Java版)

题目:有一个线性表(a1,a2,a3,...,an),采用带头节点的单链表L存储,设计一个算法将其就地逆置,线性表变为(an,...a3,a2,a1).所谓"就地"指辅助存储空间为O(1). 解题思路: 如果是顺序存储的话,我们很容易想到解题思路,利用1个辅助变量让第1个元素与第n个元素交换,然后再利用这个辅助变量让第2个元素与第n-1个元素交换,...最后利用这个辅助变量让第n/2个元素与第n+1-n/2个元素交换. 如果不要求"就地"的话,可以创建一个n个元素辅

数据结构与算法-链表就地逆置

链表操作,单链表就地逆置 void Inverse(LinkList &L) { LNode *p, *q; p = L->next; /*记录第一个结点地址*/ L->next = NULL; /*把链表设置成空表*/ while (p != NULL) /*依次按头插法将各结点插入,就实现了逆置*/ { q = p; /*用q记录待插入结点地址*/ p = p->next; /*用p记录待插入结点的后继结点地址*/*/ q->next = L->next; /*将

6-1 顺序表创建和就地逆置 (10 分)

本题要求实现顺序表的创建和就地逆置操作函数.L是一个顺序表,函数ListCreate_Sq(SqList &L)用于创建一个顺序表,函数ListReverse_Sq(SqList &L)是在不引入辅助数组的前提下将顺序表中的元素进行逆置,如原顺序表元素依次为1,2,3,4,则逆置后为4,3,2,1. 函数接口定义: Status ListCreate_Sq(SqList &L); void ListReverse_Sq(SqList &L); 裁判测试程序样例: //库函数

【C语言编程练习】7.1 线型表就地逆置

写在前面的话:直接从第5章跳到了第7章数据结构的趣题,原因是前面的数学趣题做久了,会觉得稍许疲倦,所以想“变个口味”,以后数学趣题和数据结构混合着练习. 1. 题目要求 编写一个函数,实现顺序表的就地逆置,也就是说利用原表的存储空间,将顺序表(a1,a2,a3,...,an)逆置为(an,an-1,...,a2,a1) 2. 题目分析 oh!太久没有看过数据结构的东西了,感觉非常的不习惯,脑子一片空白呢! 顺序表应该可以如下简单的描述 a->b->c->d->... 这里先简单复习

链表(二)——单向链表的基本操作(创建、删除、打印、结点个数统计)

1.指针的联动 通过两个指针分别指向前驱和后继结点,并在单向链表上进行移动,当指针指向待处理的结点时,该结点的前驱也有指针指向. 2.设有一个无序单向链表,且数据域的值均不相同,使指针pmin指向最小值结点,并使指针prem指向最小值结点的前驱结点: 代码片段: for(p = head; p; q = p, p = p->next) { if(pmin->data > p->data) { pmin = p; prem = q; } } 3.单向链表的删除算法 注:使用mallo

对于&quot;单链表逆置和递归&quot;的问题的理解.

一. 相关知识要点: 学习或了解基础数据结构和C语言, 对基础链表知识或相关知识有概况性认识. 例如: 本题目结构为: 1 #define elem_type int 2 3 typedef struct _single_list { 4 elem_type data; //所存的数据元素 5 _single_list *next; //所存的指针元素 6 }ListNode; 二. 问题的思考过程(本题以3种不同的方法解决): <1>类似于我们学习的C语言基础知识中的冒泡排序(参考C程序设计

【数据结构课程作业】单链表就地逆置

#include <stdio.h> #include <stdlib.h> typedef struct List { int val; struct List *next; }List; void InitList(List **h, List **t) { *h = (List *)malloc(sizeof(List));//*h就是头指针的地址 *t = (List *)malloc(sizeof(List));//*t就是尾指针的地址 if(!(*h) || !(*t)