线性表链式表示和实现(C语言)

本文详细介绍了如何使用C语言实现线性表的链式存储结构,包括构造空头结点、赋值、销毁、重置等核心功能,以及如何进行元素的查询、插入、删除等操作。

线性表链式表示和实现(C语言)

线性表的顺序表示指的是用一组地址连续的存储单元依次存储线性表的数据元素,而线性表的链式存储特点则是用一组任意的存储单元存储线性表的数据元素。这组存储单元既可以是连续的,也可以是不连续的。

对于链式存储的每个数据元素而言,除了存储其本身的信息之外,还需要存储一个指示其直接后继的信息,即直接后继的存储位置。这两部分信息组成了数据元素的存储映像,称为结点

链式存储的结构包含两个域:一个用于存储数据元素的信息,另一个用于存储直接后继的存储位置;存储数据元素信息的域称为数据域存储直接后继存储位置的域称为指针域

图示:
在这里插入图片描述
数据结构中,在单链表的开始结点之前一般要附设一个类型相同的结点,称之为头结点头结点的数据域可以不存储任何信息,头结点的指针域存储指向开始结点的指针,即第一个元素结点的存储位置。

附设头结点的作用:

  1. 防止单链表是空的而设的。当链表为空的时候,带头结点的头指针就指向头结点,如果当链表为空的时候,单链表没有带头结点,那么它的头指针就为NULL
  2. 方便单链表的特殊操作,插入在表头或者删除第一个结点,加入头结点的话就可以保持单链表操作的统一性
  3. 当单链表加上头结点之后,无论单链表是否为空,头指针始终指向头结点,因此空表和非空表的处理也就统一了,方便了单链表的操作,也减少了程序的复杂性和出现bug的机会

链式表示要实现的功能:

实现工具:Dev C++

  1. 构造一个空的头结点
  2. 对线性表进行赋值
  3. 对线性表进行销毁
  4. 对线性表进行重置
  5. 判断线性表是否为空
  6. 获取线性表的长度
  7. 获取线性表某一位置对应的元素
  8. 在线性表某一位置插入元素
  9. 删除线性表某一位置的元素
  10. 求线性表某一元素的前驱
  11. 求线性表某一元素的后继
  12. 打印线性表
  13. 退出

准备工作:

打开Dev C++后新建一个Source File文件即可
File>new>Source File
在这里插入图片描述
代码实现:
在实现线性表的链式存储时导入的头文件有

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

在这里我调用windows头文件是为了在后面的代码中修改控制台的名称,在实现线性表的链式存储时真正用到的只有前三个头文件

在写各种函数之前先对一些表示结果状态的字符进行预定义

//函数结果状态代码 
#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定义了Status和ElemType为int类型,也就是说之后的代码当中如果出现了Status和ElemType,它们与int作用相同

1. 单链表的结点构造

typedef struct LNode{
   
   
	ElemType  data;               //数据域 
	struct LNode * next;          //指针域,指向一整个结点(结构体,该结构体中包含数据域和指针域) 
}LNode , * LinkList;        

代码中的* LinkList指的是结点的类型,在之后的代码中出现了它就相当于出现了指向这个结点的指针

2. 构造一个空的头结点

//构造一个空的头结点
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; 
} 

在该函数传参时一定要在L之前加"&“符号,表示引用传递,保证形参和实参同时改变。之前在写代码时因为没有输入”&"符号,没有使用引用传递也就意味着没有开辟了新的内存空间,所以在之后赋值的时候会出现无法赋值的情况
在这里要强调一点:"->"是一个指针类型的运算符,它是用于指向结构体子数据的指针

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++
评论 7
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Dream_飞翔

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值