C语言实现链表的逆序的几种方式

article/2025/9/20 18:06:02

文章目录

  • 通过头插法实现的
  • 通过双指针实现链表的逆序
  • 通过栈来实现的
  • 通过递归来实现

通过头插法实现的

1、通过头插法(两条链表)来实现的。通过遍历原来的链表,将遍历得到的每一个节点都插入到新链表的头结点,然后遍历新链表,得到的就是链表的逆序了。
实现链表逆序的代码:
过程分析:
在这里插入图片描述
看到上面的过程,有些伙伴可能会疑问,为什么每一次遍历的时候,都需要给Node重新分配空间呢?再进行循环之前,给Node分配一次空间不就好了?一开始我也是这样想的,但是运行的结果却是只有一个值,并且陷入了死循环.
在这里插入图片描述
请看下面的过程分析:
在这里插入图片描述

//将节点插入到头结点,从而实现逆序
Node * insertTop(Node *head,Node *root){Node *temp,*node;//node表示新节点temp = head;//将遍历得到的节点插入到root链表的头结点,从而实现逆序while(temp != NULL){/*给新结点分配空间,这一步十分重要的,如果没有重新给node分配空间,那么根据下面的代码,得到的就是root==node,即root和Node都指向同一个地址,进行后面的操作时就会形成一个环*/node = (Node *)malloc(sizeof(Node));node->number = temp->number;node -> next = NULL;/*if(root == NULL){//如果是空链表,那么新节点就是头结点root = node;printf("由于链表是空的,那么新节点就是头结点\n");}else{//如果不是空链表,那么原来的头结点就在新节点的后面,新节点就是头结点node->next = root;root = node;Node *t = root->next;printf("新头结点的值为%d,原来头结点的值为%d\n",root->number,t->number);}*//*上面的代码可以用下面两行代码代替,因为只需要将当前这个新节点作为头结点,这个新节点后面时原来头结点的下一个节点即可,而不用再判断root是否为空了*/node->next = root;root = node;temp = temp->next;}return root;
}

完整代码:

#include<stdio.h>
#include<stdlib.h>
typedef struct NODE{int number;struct NODE *next;
}Node;
Node * insert(int number, Node *root);//函数声明,使得这个函数可以定义在main函数的后面
void display(Node *root);
Node * insertTop(Node *head,Node *root);
int main(){Node *a,*b;//a表示原来的链表,b表示逆序的链表a = b = NULL;//初始化链表(空表)//创建链表int i,number;for(i = 0; i < 5; i++){printf("输入第 %d 个节点的值:",i + 1);scanf("%d",&number);a = insert(number,a);//将新节点插入到链表最后}display(a);b = insertTop(a,b);printf("链表a的逆序链表:\n");display(b);return 0;
}//将新节点插入在链表的最后
Node * insert(int number,Node *root){Node *current,*node;//node表示新节点node = (Node *)malloc(sizeof(Node));//给新结点分配空间node->number = number;node->next = NULL;current = root;if(current == NULL){//如果是空表,那么新节点就是根节点root = node;}else{//否则,将遍历到链表的最后,将新节点插入在链表尾while(current -> next != NULL){//如果不是最后一个节点,继续遍历current = current -> next;}current -> next = node;}return root;
}
//将节点插入到头结点,从而实现逆序
Node * insertTop(Node *head,Node *root){Node *temp,*node;//node表示新节点temp = head;//将遍历得到的节点插入到root链表的头结点,从而实现逆序while(temp != NULL){//给新结点分配空间,这一步十分重要的,如果没有重新给node分配空间,那么根据下面的代码,得到的就是root==nodenode = (Node *)malloc(sizeof(Node));node->number = temp->number;node -> next = NULL;/*if(root == NULL){//如果是空链表,那么新节点就是头结点root = node;printf("由于链表是空的,那么新节点就是头结点\n");}else{//如果不是空链表,那么原来的头结点就在新节点的后面,新节点就是头结点node->next = root;root = node;Node *t = root->next;printf("新头结点的值为%d,原来头结点的值为%d\n",root->number,t->number);}*/node -> next = root;root = node;//这两行代码可以代替上面的代码,不需要判断链表是否为空,因为只要将新节点成为头结点,并且这个新节点接在原来头结点的前面就好了temp = temp->next;}return root;
}
//遍历链表
void display(Node *root){Node *current;current = root;while(current != NULL){if(current -> next == NULL){//如果是最后一个节点,那么就直接输出他的值,然后换行printf("%d\n",current->number);}else{printf("%d ->",current->number);}current = current->next;//后移}
}

运行结果:
在这里插入图片描述

通过双指针实现链表的逆序

过程分析:
在这里插入图片描述
完整代码:

#include<stdio.h>
#include<stdlib.h> //引入声明,从而可以使用malloc函数
typedef struct NODE{int number;struct NODE *next;
}Node;
Node * insert(int number,Node *root);//函数声明
Node * reverse3(Node *a);
void display(Node *root);
int main(){Node *a,*b;int i,number;a = b = NULL;//空链表for(i = 0; i < 5; i++){printf("输入第 %d 个节点:",i + 1);scanf("%d",&number);a = insert(number,a);}printf("链表a:");display(a);printf("链表a的逆序:");b = reverse3(a);display(b);return 0;
}
/*
通过双指针实现链表的逆序
*/
Node * reverse3(Node *a){Node *cur = a,*pre = NULL,*t;while(cur != NULL){t = cur->next;//新建一个临时节点,从而使得这个临时节点是pre的下一个节点cur->next = pre;//将当前的节点的下一个节点是前一个节点,从而实现了链表的逆序pre = cur;cur = t;//将临时变量赋值给当前这个节点cur,从而实现节点的后移}return pre;}
//将节点插入到链表尾,从而创建链表
Node * insert(int number,Node *root){Node *current,*node;//给新结点分配空间node = (Node *)malloc(sizeof(Node));node->number = number;node->next = NULL;current = root;if(current == NULL){//如果空链表,新节点就是头结点root = node;}else{while(current->next != NULL){//不是最后一个节点,就继续遍历current = current->next;}current->next = node;}return root;
}//遍历链表
void display(Node *root){Node *current;current = root;if(current == NULL){printf("链表为空,无法遍历");return;}while(current != NULL){if(current->next == NULL){//如果是最后一个节点,那么将它的值输出并换行printf("%d\n",current->number);}else{printf("%d ->",current->number);}count++;current = current->next;//后移}
}

运行结果:
在这里插入图片描述

通过栈来实现的

2、通过栈来实现的,利用栈先进后出的特性,从而实现链表的逆序,这里通过数组来实现栈的压栈和出栈的相关操作。
下面是完整代码:

/*
通过栈先进后出的特点,从而实现链表的逆序
实现链表的方式有两种方式,通过数组和链表
1)通过链表的时候,只需要将新节点插入到头结点,从而实现了先进后出(上面就相当于通过
链表实现栈,从而实现逆序)
2)通过数组来实现栈的相关操作,从而实现链表的逆序
*/
#include<stdio.h>
#include<stdlib.h> //引入声明,从而可以使用malloc函数
typedef struct NODE{int number;struct NODE *next;
}Node;
Node * insert(int number,Node *root);//函数声明
Node * reverse(Node *a,Node *b);
void display(Node *root);
int count = 0; //定义一个全局变量,从而可以得到链表的节点个数
int main(){Node *a,*b;int i,number;a = b = NULL;//空链表for(i = 0; i < 5; i++){printf("输入第 %d 个节点:",i + 1);scanf("%d",&number);a = insert(number,a);}printf("链表a:");display(a);printf("链表a的逆序:");b = reverse(a,b);display(b);return 0;
}
//获取逆序的链表
Node * reverse(Node *a,Node *b){if(count == 0){printf("链表为空");return NULL;}Node *arr[count];//定义一个长度为count的节点数组int top = -1,i;//栈顶指针Node *current = a;/*遍历链表,将遍历得到的节点放到节点数组中,此时从下标为0开始遍历数组,得到的是顺序链表*/while(current != NULL){/*arr[++top] = (Node *)malloc(sizeof(Node));//给新结点分配空间arr[++top]->number = current->number;arr[++top]->next = NULL;注意不可以这样写的,因为本来是想给arr[top]这个节点赋值的,如果是上面那样的话,那么就和本来的要求相反了,并且还有可能会出现错误,因为没有给节点分配空间*/top++;arr[top] = (Node *)malloc(sizeof(Node));//给新结点分配空间arr[top]->number = current->number;arr[top]->next = NULL;current = current->next;}/*出栈,由于是从数组的最后开始遍历,所以遍历得到的是链表的逆序,然后只需要将遍历的节点插入到新链表的尾节点即可*/while(top >= 0){b = insert(arr[top--]->number,b);}return b;
}
//将节点插入到链表尾,从而创建链表
Node * insert(int number,Node *root){Node *current,*node;//给新结点分配空间node = (Node *)malloc(sizeof(Node));node->number = number;node->next = NULL;current = root;if(current == NULL){//如果空链表,新节点就是头结点root = node;}else{while(current->next != NULL){//不是最后一个节点,就继续遍历current = current->next;}current->next = node;}return root;
}//遍历链表
void display(Node *root){Node *current;current = root;if(current == NULL){printf("链表为空,无法遍历");return;}while(current != NULL){if(current->next == NULL){//如果是最后一个节点,那么将它的值输出并换行printf("%d\n",current->number);}else{printf("%d ->",current->number);}count++;current = current->next;//后移}
}

在这里插入图片描述

通过递归来实现

请看过程分析(这是基于java来分析的,如果是C语言的话,那么应该是head->next,head->next->next,而不是head.next,head.next.next(java的)):
在这里插入图片描述
完整代码:

#include<stdio.h>
#include<stdlib.h> //引入声明,从而可以使用malloc函数
typedef struct NODE{int number;struct NODE *next;
}Node;
Node * insert(int number,Node *root);//函数声明
Node * reverse2(Node *a);
int main(){Node *a,*b;int i,number;a = b = NULL;//空链表for(i = 0; i < 5; i++){printf("输入第 %d 个节点:",i + 1);scanf("%d",&number);a = insert(number,a);}printf("链表a:");display(a);printf("链表a的逆序:");b = reverse2(a);display(b);return 0;
}
/*
通过递归来实现链表的逆序
*/
Node * reverse2(Node *head){if(head == NULL || head->next == NULL)return head;//如果当前节点是NULL,表示是空链表,或者当前的节点是最后一个节点,那么直接返回这个节点Node *newHead = reverse2(head->next);//进入递归head->next->next = head;head->next = NULL;//这一步避免了链表存在环return newHead;
}
//将节点插入到链表尾,从而创建链表
Node * insert(int number,Node *root){Node *current,*node;//给新结点分配空间node = (Node *)malloc(sizeof(Node));node->number = number;node->next = NULL;current = root;if(current == NULL){//如果空链表,新节点就是头结点root = node;}else{while(current->next != NULL){//不是最后一个节点,就继续遍历current = current->next;}current->next = node;}return root;
}//遍历链表
void display(Node *root){Node *current;current = root;if(current == NULL){printf("链表为空,无法遍历");return;}while(current != NULL){if(current->next == NULL){//如果是最后一个节点,那么将它的值输出并换行printf("%d\n",current->number);}else{printf("%d ->",current->number);}count++;current = current->next;//后移}
}

运行结果:
在这里插入图片描述


http://chatgpt.dhexx.cn/article/oYjemNdB.shtml

相关文章

C语言单向链表的逆序输出

最近在学习链表&#xff0c;看到书上说可以采取每次在链表头部插入新增节点的方法&#xff0c;将链表逆序&#xff0c;也就是建立的链表节点内容与数据的输入顺序相反。我便来了兴趣&#xff0c;想着试试看&#xff0c;结果没搞懂&#xff0c;于是开始百度。看了几遍博客后终于…

样本方差与总体方差的区别

为什么80%的码农都做不了架构师&#xff1f;>>> 之前一直对于样本方差与总体方差的概念区分不清&#xff0c;对于前者不仅多了“样本”两个字&#xff0c;而且公式中除数是N-1&#xff0c;而不是N。现在写下这么写东西&#xff0c;以能彻底把他们的区别搞清楚。 总…

彻底理解样本方差为何除以n-1

设样本均值为&#xff0c;样本方差为&#xff0c;总体均值为&#xff0c;总体方差为&#xff0c;那么样本方差有如下公式&#xff1a; 很多人可能都会有疑问&#xff0c;为什么要除以n-1&#xff0c;而不是n&#xff0c;但是翻阅资料&#xff0c;发现很多都是交代到&#xff0c…

证明样本方差不是总体方差的无偏估计(1)

无偏估计是用样本统计量来估计总体参数时的一种无偏推断。估计量的数学期望等于被估计参数的真实值&#xff0c;则称此估计量为被估计参数的无偏估计&#xff0c;即具有无偏性&#xff0c;是一种用于评价估计量优良性的准则。无偏估计的意义是&#xff1a;在多次重复下&#xf…

总体方差与样本方差分母的小小区别,n还是n-1?

总体方差与样本方差分母的小小区别&#xff0c;n还是n-1&#xff1f; 引入方差概念方差计算无偏估计 样本方差公式相关参考链接 样本方差的自由度是n-1 引入 方差概念 方差是在概率论和统计方差衡量随机变量或一组数据时离散程度的度量&#xff0c;用来度量随机变量和其数学期…

样本方差的与方差

之前做模型拟合的时候需要计算样本的方差和均值,Matlab的std函数算出来就是不对经,一看才知道matlab的给定的标准差计算公式是: For a random variable vector A made up of N scalar observations, the standard deviation is defined as where μ is the mean of A: The s…

总体样本方差的无偏估计样本方差为什么除以n-1

总体样本方差的无偏估计样本方差为什么除以n-1 本文链接&#xff1a; https://blog.csdn.net/qq_16587307/article/details/81328773 我们先从最基本的一些概念入手。 如下图&#xff0c;脑子里要浮现出总体样本&#xff0c;还有一系列随机选取的样本。只要是样本&#xff0c;…

有偏样本方差、无偏样本方差

1.有偏样本方差、无偏样本方差 笔记来源&#xff1a;Why Dividing By N Underestimates the Variance 1.1 为什么样本方差总是小于总体方差&#xff1f; 由于总体量太大&#xff0c;我们要耗费大量人力物力财力才可以或者永远无法了解总体情况&#xff0c;所以我们只能用样本…

C# 及excel中【总体方差】、【样本方差】的计算公式

一、数学公式 二、C#中的数学包 using MathNet.Numerics.Statistics; 三、【总体方差】和【样本方差】的计算 using System; using System.Linq;var myList new List<float> {1,3,4,5,6,7};display("要计算的数据"); display(myList);var pv myList.Popula…

用Python计算样本方差,总体方差,比较

1.样本方差 #样本方差&#xff0c;考虑自由度def f_sigma(x):# 通过Python定义一个计算变量波动率的函数# x&#xff1a;代表变量的样本值&#xff0c;可以用列表的数据结构输入n len(x)u_mean sum(x)/n #计算变量样本值的均值z [] #生成一个空列表for t in range(n):z.…

总体方差与样本方差

今天在计算一类数据的协方差时遇到个问题。数据如下&#xff1a; x1(0,0,0)’ x2(1,0,0)’ x3(1,0,1)’ x4(1,1,0)’ 这本是一件很容易的事&#xff0c;但我手算后用Matlab的cov函数验算了一下&#xff0c;发现结果竟然不一样&#xff0c;于是按照协方差公式&#xff0c;一…

统计学---之样本方差与总体方差的区别

前段日子重新整理了一下这个问题的解答&#xff0c;跟大家分享一下&#xff0c;如果有什么错误的话希望大家能够提出来&#xff0c;我会及时改正的&#xff0c;话不多说进入正题&#xff1a; 首先&#xff0c;我们来看一下样本方差的计算公式&#xff1a; 刚开始接触这个公式的…

为什么样本方差里面要除以(n-1)而不是n?

前段日子重新整理了一下这个问题的解答&#xff0c;跟大家分享一下&#xff0c;如果有什么错误的话希望大家能够提出来&#xff0c;我会及时改正的&#xff0c;话不多说进入正题&#xff1a; 首先&#xff0c;我们来看一下样本方差的计算公式&#xff1a; 刚开始接触这个公式的…

ECS_FDS小议贝塞尔校正(Bessel‘s Correction)

在学习概率论与数理统计的相关知识时&#xff0c;大家肯定会听到”贝塞尔校正&#xff08;Bessels Correction&#xff09;“这个名词&#xff0c;这是德国天文学家&#xff0c;数学家Friedrich Bessel在进行天体测量学研究时提出的一个方法。可能大家看到一个以人名命名的概念…

样本方差与总体方差

样本方差与总体方差 一、方差&#xff08;variance)&#xff1a;衡量随机变量或一组数据时离散程度的度量。 概率论中方差用来度量随机变量和其数学期望&#xff08;即均值&#xff09;之间的偏离程度。 统计中的方差&#xff08;样本方差&am…

完整的Axios封装-单独API管理层、参数序列化、取消重复请求、Loading、状态码...

前言 Axios 相信对Vue熟悉的铁汁对它不会感到陌生了&#xff08;当然不熟悉Vue你也可以认识它&#xff09;&#xff0c;这简直就是前端近年来的一大杀器&#xff0c;自从Vue2开始之后&#xff0c;官方推荐使用axios来进行网络请求&#xff0c;后面基本大部分Vue项目都能瞧见它…

Ajax介绍和Axios基本使用

Ajax介绍 Ajax本身就是Asynchronous JavaScript And XML的缩写&#xff0c;直译为&#xff1a;异步的JavaScript和XML。 在实际应用中Ajax指的是&#xff1a;不刷新浏览器窗口&#xff0c;不做页面跳转&#xff0c;局部更新页面内容的技术。 『同步』和『异步』是一对相对的…

Axios使用详解

文章目录 一、Promise的使用1. 基本语法2. Promise三种状态3. Promise链式调用1. 基本写法2. 使用静态方法3. 直接返回 4. Promise.all5. Promise.race 二、Axios使用1. 安装并引入2. 发送请求3. config配置4. 响应结构5. 并发请求6. 全局配置6. 创建实例7. 实例方法8. 拦截器请…

Uncaught (in promise) Error: Network Error at e.exports (axios.js:8:6410) at d.onerror (axio

axios报错&#xff1a; 原因&#xff1a; 后端没有给跨域权限&#xff0c;后端需要设置允许跨域的响应头&#xff0c;即可解决。 解决&#xff1a; node端设置响应头&#xff0c;解决跨域问题 。