C C++最新线性表链式表示和实现(C语言)_c语言 链式法则,2024年最新秀出天际

img
img

网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。

需要这份系统化的资料的朋友,可以添加戳这里获取

一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!

在这里要强调一点:"->"是一个指针类型的运算符,它是用于指向结构体子数据的指针

3. 对线性表进行赋值

//对线性表进行赋值 
Status ValueList\_L(LinkList &L,ElemType e){
	LinkList s,p;
	p = L;  
	while(p->next){
		p = p->next;
	}
	s = (LinkList)malloc(sizeof(LNode));       //生成一个新结点 
	s->data = e;                 //将e赋值给新结点的数据域 
	s->next = p->next;           //将新结点与后一个结点的地址连接
	p->next = s;
	return OK; 
}

因为要实现构造一个单链表,所以在main函数中会循环调用ValueList_L方法,所以通过以下的循环是用来使p指向要赋值的位置

while(p->next){
p = p->next;
}

之后利用malloc()函数开辟新的结点来存放数据和下一个结点的位置即可,因为malloc()函数开辟空间之后返回的不是LinkList结点类型,所以在利用malloc()函数开辟新的结点时要将其通过强制类型转换使其转换成LinkList类型

4.对线性表进行销毁

//对线性表进行销毁
Status DistoryList\_L(LinkList &L) { 
	if(!L){            //如果线性表不存在,返回ERROR 
		printf("线性表不存在\n");
		return ERROR;
	}
	LinkList q = L->next;    //使q指向单链表的首元结点 
	while(q != NULL){     //当q结点不为空时一直进入循环 
		free(L);          //释放L结点 
		L = q;            //将q结点赋值给L结点 
		q = L->next;      //将q结点赋值给L结点以后使q结点指向L的下一个结点 
	} 
	free(L);    //此时q的值为NULL,L指向尾结点,将其释放
	L = NULL;   
	printf("线性表已销毁\n"); 
}

在对单链表进行销毁操作时,从头结点开始逐一释放,释放前使q指向开始释放的结点,当开始结点不为空时,执行释放过程,先释放头结点,然后将L,q都向后移,依次释放,因为q始终是L的后继,所以最后一定是L留到最后,最后释放L结点

L = NULL;

为什么在feel(L);之后还要将L赋值为空?

因为free函数只是将之前动态分配给L的内存归还给系统,但是指针类型的结点L仍然存在,为了防止之后发生野指针访问,将L赋值为NULL

5.对线性表进行重置

//对线性表进行重置
Status ClearList\_L(LinkList &L)
{
	if(!L->next){
		printf("线性表为空表,不需要重置\n");
		return ERROR;
	}
	LinkList p,q;
	p = L->next;          //将单链表的头结点赋值给p
	while(p){
		q = p->next;      //将单链表的首元结点赋值给q
		free(p);
		p = q;
	} 
	L->next = NULL;     //将头结点的指针域赋值为空 
	printf("线性表已重置\n");
	return OK;
} 

在对线性表进行重置前首先要判断线性表是为空表,当其不为空时构造两个LinkList类型的结点p和q,使p指向L的首元结点,当p不为空即单链表不为空时进入while循环,将p的下一个结点复制给q,将p释放后再将q赋值给p。p为空时说明此时单链表只剩下了头结点,将头结点的指针域设置为NULL,完成单链表的重置(因为LinkList为指针类型的数据,所以赋值的内容都是地址

图示:
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
6.判断线性表是否为空

//判断线性表是否为空
Status ListEmpty\_L(LinkList L)
{
	if(L){
		if(L->next == NULL)       //如果首元结点不存在 
	    printf("线性表是空表\n");
	    else
	    printf("线性表不是空表\n");
	}
	else{
	printf("线性表不存在,无法判断\n");	
	}
	return OK;
}

在判断线性表是否为空时,首先判断头结点是否存在,当头结点存在时看头结点的指针域是否为空,当指针域为空时说明首元结点不存在,单链表是空表当指针域不为空时说明存在首元结点,单链表不是空表。如果头结点不存在的话说明单链表不存在,无法判断是否为空表。

7.获取线性表的长度

//获取线性表的长度
Status ListLength\_L(LinkList L,int count)
{
	//L为带头结点的单链表的头指针,count为计数器
	LinkList p = L->next;    //定义p为单链表L的指针域 
	while(p){
		p = p->next;
		count++;
	}
	return count;
}

获取线性表长度的核心思路是遍历单链表,定义LinkList类型的变量p,将单链表的首元结点赋值给p。在该函数中count为计数器,形参count传来的值始终为1,count的值为1代表从首元结点开始计数,所以才将L->next赋值给p,目的是为了让count与存储数据元素的结点位置对应一致。(在单链表中有头结点的存在,所以这种方法计算出的长度最后的值要比线性表的实际长度大1)进入循环后每当p不断向后移动,每当p后移一次,计数器count的值就加1,直到p为空,此时count的位置就对应着最后一个存储着数据元素的结点位置。

8.获取线性表某一位置对应的元素

//获取线性表某一位置对应的元素
Status GetElem\_L(LinkList L,int index)
{
	LinkList p;
	p = L->next;       //使p指向L的首元结点
	int  count = 1;    //count为计数器 ,赋值等于1的原因是从首元结点开始计数 
	while(p && count < index){    //顺着指针向后查找,直到p指向第index个元素或p为空 
		p = p->next;
		count++;        //此时p一直指向第count个元素 
	}
	if(!p || count > index){
		printf("当前位置没有元素\n");
		return ERROR;
	}
	printf("第%d个元素的值是:%d\n",index,p->data);
	return OK;
}

与获取单链表的长度思路一样,获取单链表某一位置的元素也需要遍历单链表,只不过什么时候停止遍历由自己决定,可能不需要全部遍历。定义LinkList类型的变量p并且将首元结点的地址赋值给p。定义计数器count的初始值为1之后,进入while循环,循环判断有两个:

  1. p    因为p一直指向第count个结点,所以此循环判断条件的意思是当第count个结点存在时才能进入循环
  2. count < index   当count还不是我们想要获取的结点位置时继续循环

退出循环以后p指向的位置就是我们想要获取的结点位置,这个时候要先进行越界判断,!p的意思是如果在之前的循环中index的值大于单链表的长度,那么退出循环的原因就是p为NULL,那么!p就为true,满足if语句中的条件,返回ERROR,所以 !p的作用就是限制index不大于单链表的长度
count > index的目的是为了限制index的值小于1

9.在线性表某一位置插入元素

//在线性表某一位置插入元素
Status ListInsert\_L(LinkList &L,int index,ElemType e)
{
	LinkList p,q;
	p = L;      //将线性表的头结点赋值给p
	int count = 0;    //count为计数器 
	while(p && count < index - 1){      //寻找第index-1个结点 
		p = p->next;         //此时的p结点指向第index-1个结点 
		count++; 
	}
	if(!p || count > index -1){        //越界判断,index小于1或是index大于表长加1 
		printf("当前结点无法插入元素\n");
		return ERROR;
	}
	q = (LinkList)malloc(sizeof(LNode));
	q->data = e;            //将e赋值到q的数据域当中
	q->next = p->next;
	p->next = q;
	printf("元素插入成功\n");
	return OK; 
}

与寻找某个位置结点的值思路一致,需要先找到要插入结点的位置。但是这里不同的地方在于要插入结点的话,可以在单链表的表尾插入元素,也可以在头结点和首元结点间插入元素,所以计数器count的初值为0(为了保证从头结点开始遍历,count的值与实际的结点位置相匹配),所以判断条件变为index - 1。在结束循环和越界判断结束后p之后的位置就是要插入结点的位置,先构造出一个空结点并赋值给q,将p的下一个结点位置赋值给q的指针域,再将p的下一个结点位置赋值给q完成插入操作。

图示:
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
10.删除线性表某一位置的元素

//删除线性表某一位置的元素
Status DeleteList\_L(LinkList &L,int index)
{
	LinkList p,q;
	p = L;           //将线性表的头结点赋值给p
	int count = 0;   //计数器
	while(p->next && count < index - 1){
		p = p->next;
		count++;            //此时p一直指向第count个结点 
	}
	if(!(p->next) || count > index - 1){     //越界判断 
		printf("当前位置无法删除元素\n");
		return ERROR;
	}
	q = p->next;
	p->next = q->next;
	free(q);
	q = NULL;
	printf("当前位置元素已删除\n");
	return OK;
} 

删除某一结点的思路仍然是从头结点开始遍历找到要删除的结点的位置的前一个结点,此时p就是要删除结点位置的前一个结点。将p的后一个结点赋值给q,此时q就是要删除的结点,将q的下一个结点与p的下一个结点连接,释放q结点,完成删除操作。

11.求线性表某一元素的前驱

//求线性表某一元素的前驱 
Status PriorElem\_L(LinkList L,int index)
{
	LinkList p;
	int count = 0;       //count为计数器 
	p = L;
	while(p->next && count < index - 1){       //寻找第index-1个结点 
		p = p->next;            //p一直指向第count个结点 
		count++;
	}
	if(!(p->next) || count > index - 1){       //越界判断 
		printf("当前位置无法求该元素的前驱\n"); 
		return ERROR;
	}
	if(p != L)            //如果要获取第一个元素的前驱,就是获取头结点数据域的值 
	printf("该元素的前驱为:%d\n",p->data);
	else
	printf("该位置的前驱是头结点\n头结点的数据域中存储的值为:%d\n",p->data);
	return OK;
}

和删除结点的思路完全一致,只不过求前驱时不需要进行删除结点,在循环中控制循环条件使p在index - 1位置结束循环,此时p指向的就是第index的前驱,直接将p结点的数据域输出即可

12.求线性表某一元素的后继

//求线性表某一元素的后继 
Status NextElem\_L(LinkList L,int index)
{
	LinkList p;
	int count = 0;
	p = L->next;
	while(p && count < index){        //不断遍历寻找第index之后的结点 
		p = p->next;      //p一直指向index-1的后一个结点 
		count++;
	}
	//!p的目的是为了确保i不大于表长-1,count>index的目的是为了确保index不小于0 
	if(!p || count > index){          //越界判断
		printf("当前位置无法求该元素的后继\n"); 
		return ERROR;
	}
	printf("该元素的后继为:%d\n",p->data);
}

在声明LinkList类型变量p时将L的首元结点赋值给p,在循环中p一直指向第index的下一个结点,所以直接将p结点的数据域输出即可

13.打印线性表

//打印线性表
Status PrintList\_L(LinkList L)
{
	if(!L){            //如果线性表不存在,返回ERROR 
		printf("线性表不存在,无法打印\n");
		return ERROR;
	}
	LinkList p;
	p = L->next;    //将L的首元结点赋值给p ,为了不将头结点打印出来 
	while(p){
		printf(" %d",p->data);   //将p结点的数据域输出 
		p = p->next;    //结点不断的向后移动 
	}
	printf("\n");
	return OK;
}

打印单链表的思路也是进行对单链表的遍历,在遍历的过程中将每个结点的数据域中存储的值输出

运行结果演示:

为了方便演示,在这里为单链表一次赋值为1,2,3,4,5

构造一个空的头结点

在这里插入图片描述

赋值操作

在这里插入图片描述

判断此时线性表是否为空

在这里插入图片描述

获取单链表的长度

在这里插入图片描述

获取2号位置的元素

在这里插入图片描述

在3号位置插入520并打印单链表

在这里插入图片描述
在这里插入图片描述
获取此时单链表的长度

在这里插入图片描述
删除3号位置的元素并打印单链表

在这里插入图片描述
在这里插入图片描述
求3号位置元素的前驱和后继

在这里插入图片描述
在这里插入图片描述

重置单链表并获取长度以及判断是否为空表

在这里插入图片描述
在这里插入图片描述

在这里插入图片描述

销毁单链表并进行赋值和判断是否为空

在这里插入图片描述
在这里插入图片描述

在这里插入图片描述
以上便是线性表链式表示和实现,由于链表在空间的合理利用上和插入、删除是不需要移动等的优点,因此在很多场合下它是线性表的首选存储结构。然而,它也存在着实现某些基本操作的缺点,比如:求线性表长度时不如顺序存储结构…

源码:

#include<stdio.h>
#include<stdlib.h>
#include<malloc.h>
#include<windows.h> 

//函数结果状态代码 
#define TRUE 1 //代码中出现TRUE相当于出现了1 
#define FALSE 0 //出现FALSE相当于出现了0 
#define OK 1 //出现OK相当于出现了1 
#define ERROR 0 //出现ERROR相当于出现了0 
#define INFEASIBLE -1
#define OVERFLOW -2 

typedef int Status;    //定义函数的返回状态 
typedef int ElemType; 

typedef struct LNode{
	ElemType  data;               //数据域 
	struct LNode \* next;          //指针域,指向一整个结点(结构体,该结构体中包含数据域和指针域) 
}LNode , \* LinkList;        //\* LinkList是结点的类型,在之后的代码中出现了它就相当于出现了指向这个结点的指针 

//构造一个空的头结点 
Status InitList\_L(LinkList &L){     //之前因为没有输入"&"符号,没有使用引用传递也就意味着没有开辟了新的内存空间,所以在之后赋值的时候会出现无法赋值的情况 
	L = (LinkList)malloc(sizeof(LNode));      //产生头结点,并使L指向该头结点(L也称头指针)
	if(!L)  return ERROR;          //如果存储空间分配失败,返回ERROR
	L->next = NULL;                //将指针域赋值为NULL,在这里要强调一点:"->"是一个整体,它是用于指向结构体子数据的指针 
	printf("空的头结点创建成功\n");
	return OK; 
} 

//对线性表进行赋值 
Status ValueList\_L(LinkList &L,ElemType e){
	LinkList s,p;
	p = L;  
	while(p->next){
		p = p->next;
	}
	s = (LinkList)malloc(sizeof(LNode));       //生成一个新结点 
	s->data = e;                 //将e赋值给新结点的数据域 
	s->next = p->next;           //将新结点与后一个结点的地址连接
	p->next = s;
	return OK; 
} 

//对线性表进行销毁
Status DistoryList\_L(LinkList &L) {
/\*从头结点开始逐一释放,释放前使p指向头结点,q指向开始释放的结点
  当开始结点不为空时,执行释放过程
  先释放头结点,然后将p,q都向后移,依次释放
  因为q始终是p的后继,所以最后一定是p留到最后,最后释放p
\*/ 
	if(!L){            //如果线性表不存在,返回ERROR 
		printf("线性表不存在\n");
		return ERROR;
	}
	LinkList q = L->next;    //使q指向单链表的首元结点 
	while(q != NULL){     //当q结点不为空时一直进入循环 
		free(L);          //释放L结点 
		L = q;            //将q结点赋值给L结点 
		q = L->next;      //将q结点赋值给L结点以后使q结点指向L的下一个结点 
	} 
	free(L);    //此时q的值为NULL,L指向尾结点,将其释放
	L = NULL;   //free函数只是将之前动态分配给L的内存归还给系统,但是指针类型的结点L仍然存在
	//为了防止之后发生野指针访问,将L赋值为NULL 
	printf("线性表已销毁\n"); 
}

//对线性表进行重置
Status ClearList\_L(LinkList &L)
{
	if(!L->next){
		printf("线性表为空表,不需要重置\n");
		return ERROR;
	}
	LinkList p,q;
	p = L->next;          //将单链表的头结点赋值给p
	while(p){
		q = p->next;      //将单链表的首元结点赋值给q
		free(p);
		p = q;
	} 
	L->next = NULL;     //将头结点的指针域赋值为空 
	printf("线性表已重置\n");
	return OK;
} 
 
//判断线性表是否为空
Status ListEmpty\_L(LinkList L)
{
	if(L){
		if(L->next == NULL)       //如果首元结点不存在 
	    printf("线性表是空表\n");
	    else
	    printf("线性表不是空表\n");
	}
	else{
	printf("线性表不存在,无法判断\n");	
	}
	return OK;
} 

//获取线性表的长度
Status ListLength\_L(LinkList L,int count)


![img](https://img-blog.csdnimg.cn/img_convert/720506a7aef762181f12976d03c32afe.png)
![img](https://img-blog.csdnimg.cn/img_convert/d79727c6ee69390ba9e58b8410d77efb.png)

**既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,涵盖了95%以上C C++开发知识点,真正体系化!**

**由于文件比较多,这里只是将部分目录截图出来,全套包含大厂面经、学习笔记、源码讲义、实战项目、大纲路线、讲解视频,并且后续会持续更新**

**[如果你需要这些资料,可以戳这里获取](https://bbs.csdn.net/topics/618668825)**

s ListEmpty\_L(LinkList L)
{
	if(L){
		if(L->next == NULL)       //如果首元结点不存在 
	    printf("线性表是空表\n");
	    else
	    printf("线性表不是空表\n");
	}
	else{
	printf("线性表不存在,无法判断\n");	
	}
	return OK;
} 

//获取线性表的长度
Status ListLength\_L(LinkList L,int count)


[外链图片转存中...(img-xUnMhxrC-1715731963114)]
[外链图片转存中...(img-ocfQjM1u-1715731963115)]

**既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,涵盖了95%以上C C++开发知识点,真正体系化!**

**由于文件比较多,这里只是将部分目录截图出来,全套包含大厂面经、学习笔记、源码讲义、实战项目、大纲路线、讲解视频,并且后续会持续更新**

**[如果你需要这些资料,可以戳这里获取](https://bbs.csdn.net/topics/618668825)**

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值