数据结构与算法(C语言版)之单链表

目录

单链表介绍

单链表的创建

链表结构体定义

 头插法创建链表

尾插法创建链表

单链表的简单操作

获取单链表长度

链表销毁函数

 链表节点赋值函数

链表节点查询函数

主函数以及测试结果

完整代码

LIST.c

LIST.h

main.c


单链表介绍

        与线性表在内存中的储存结构不同(线性表在内存中是一块连续的空间),链表的存储结构是组成链表的数据元素在内存中既可以是连续的,也可以是不连续的。链表中的每个元素都应该包含两个信息:一个是储存数据的信息,即数据域;另一个为储存与之相连的元素的位置信息,即指针域。

        而单链表指的是链表中的每个节点中只包含一个指针域。整个链表的存取必须从头指针开始,头指针指示链表中的第一个节点的储存位置(头指针不用存放数据)。由于最后一个元素没有其后继元素,因此最后一个元素的指针域应为NULL。

        *在接下来的示例中,我都会将主函数相关部分放在 main.c文件中,将链表相关部分放在LIST.c以及LIST.h文件中。

单链表的创建

        单链表的创建有头插法和尾插法两种方法,头插法在创建单链表时总是将新创建的节点置于头节点之后,而尾插法则将新创建的节点置于尾节点之后。

链表结构体定义

//单链表数据域结构体
typedef struct Dat {
	int dat_1;
}Dat;

//单链表结构体
typedef struct List {
	Dat dat; //数据域
	struct List* next; //指针域(下一节点指针)
}List;

 头插法创建链表

/********************************************
函数名称:单链表初始化函数(头插法)
函数介绍:使用头插法创建一个指定长度的单链表,
		  并返回其头指针
函数参数:int length -- 单链表指定生成长度
返回类型:List* listHead -- 单链表的头指针
********************************************/
List* InitList_Head(int length)
{
	List* head, * node, * tail;//头节点,中间节点,尾节点
	head = (List*)malloc(sizeof(List));//内存中开辟头节点空间
	if (length == 0)
	{
		head->next = NULL;
	}
	else
	{
		//连接节头节点与尾节点
		tail = (List*)malloc(sizeof(List));//内存中开辟尾节点空间
		head->next = tail;
		tail->next = NULL;
		//连接中间节点
		for (int i = 0; i < length - 1; i++)
		{
			node = (List*)malloc(sizeof(List));//开辟中间节点空间
			List* temp;
			temp = head->next;
			head->next = node;
			node->next = temp;
		}
	}
	return head; //返回头节点地址
}

        在创建非0长度链表时,先创建头节点与尾节点 ,此时链表长度为1(由于头节点不用储存数据,因此头节点的长度不计),此后再将新创建的中间节点连接在头节点之后。

尾插法创建链表

/********************************************
函数名称:单链表初始化函数(尾插法)
函数介绍:使用尾插法创建一个指定长度的单链表,
		  并返回其头指针
函数参数:int length -- 单链表指定生成长度
返回类型:List* listHead -- 单链表的头指针
********************************************/
List* InitList_Tail(int length)
{
	List* head, * node, * tail;//头节点,中间节点,尾节点
	head = (List*)malloc(sizeof(List));//内存中开辟头节点空间
	if (length == 0)
	{
		head->next = NULL;
	}
	else
	{
		tail = head;
		//让tail始终为尾节点
		for (int i = 0; i < length; i++)
		{
			node = (List*)malloc(sizeof(List));
			tail->next = node;
			tail = node;
		}
		tail->next = NULL;//尾节点的next为空
	}
	return head; //返回头节点地址
}

 始终让尾节点为新创建的节点,最后将尾节点的 next 指向NULL。

单链表的简单操作

获取单链表长度

/********************************************
函数名称:链表长度查询函数
函数介绍:查询指定链表的长度
函数参数:List* head -- 要查询长度的链表的头指针
返回类型:int length -- 该链表长度
*********************************************/
int Length(List* head)
{
	int length = 0;
	List* temp;
	temp = head;
	while (temp->next != NULL) {
		temp = temp->next;
		length++;
	}
	return length;
}

        创建临时节点,让临时节点不断指向尾节点并记录长度。

链表销毁函数

/********************************************
函数名称:链表销毁函数
函数介绍:销毁指定链表
函数参数:List* head -- 要销毁的链表的头指针
返回类型:无
*********************************************/
void Destroy(List* head) 
{
	List* temp[2];
	temp[0] = head->next;
	temp[1] = temp[0]->next;
	free(head);	//先释放头节点
	while (temp[1]->next != NULL) {
		free(temp[0]);
		temp[0] = temp[1];
		temp[1] = temp[0]->next;
	}
	free(temp[0]);
	free(temp[1]);
}

        在这里我先创建了一个节点块,节点块中 temp[1] 为 temp[0] 的后继元素,不断销毁 temp[0] 指向的空间,再让 temp[1] 找到下一个要销毁的节点的地址。最后当 temp[1] 指向尾节点时将节点块所指向的节点都释放即可。

        为什么不能直接释放头节点?

        若只释放头节点,可能会像下列代码一样造成内存溢出。

        watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBA6Zi_5pav5be05p-g5qqs6YW4,size_19,color_FFFFFF,t_70,g_se,x_16

 链表节点赋值函数

/**************************************************
函数名称:链表节点赋值函数
函数介绍:给链表的指定链节的数据域赋值
函数参数:List* head -- 要赋值的链表的头指针
		  int n -- 要赋值的链节的序号
		  Dat -- 要赋的值
返回类型:int类型 
		  1 -- 赋值成功 0 -- 错误的链节序号
**************************************************/
int InputElem(List* head, int node_number, Dat* dat)
{
	if (node_number <= 0 || node_number > Length(head))
	{
		return 0;
	}
	else
	{
		List* temp;
		temp = head->next;
		//找到赋值的链节的指针
		for (int i = 0; i < node_number; i++)
		{
			temp = temp->next;
		}
		//赋值
		temp->dat.dat_1 = dat->dat_1;
		return 1;
	}
}

链表节点查询函数

/**************************************************
函数名称:链表节点值查询函数
函数介绍:查询指定链节的值
函数参数:List* head -- 要查询的链表的头指针
		  int n -- 要查询的链节的序号
返回类型:Dat* dat -- 查询到的数据域的指针
**************************************************/
Dat* FindElem(List* head, int node_number)
{
	if (node_number <= 0 || node_number > Length(head))
	{
		return NULL;
	}
	else
	{
		List* temp;
		temp = head->next;
		//找到查询的链节的指针
		for (int i = 0; i < node_number - 1; i++)
		{
			temp = temp->next;
		}
		//返回
		return temp->next;
	}
}

主函数以及测试结果

#include <stdio.h>
#include "LIST.h"

void main()
{
	//创建链表(头插法)
	List* list1 = InitList_Head(6);
	//创建链表(尾插法)
	List* list2 = InitList_Tail(7);
	//输出链表长度
	printf("链表1的长度:%d\n", Length(list1));
	printf("链表2的长度:%d\n", Length(list2));
	Dat dat1 = { 55 };
	InputElem(list1, 2, &dat1);//为链表1赋值
	Dat dat2 = { 5 };
	InputElem(list2, 3, &dat2);//为链表2赋值
	//读取链表1的值
	Dat* dat1_write = FindElem(list1, 2);
	printf("链表1的2号位:%d\n", dat1_write->dat_1);
	//读取链表2的值
	Dat* dat2_write = FindElem(list2, 3);
	printf("链表2的3号位:%d\n", dat2_write->dat_1);
	//销毁链表
	Destroy(list1);
	Destroy(list2);
}

a63e17519e404118af6429224bde6962.png

完整代码

LIST.c

#include "LIST.h"

/********************************************
函数名称:单链表初始化函数(头插法)
函数介绍:使用头插法创建一个指定长度的单链表,
		  并返回其头指针
函数参数:int length -- 单链表指定生成长度
返回类型:List* listHead -- 单链表的头指针
********************************************/
List* InitList_Head(int length)
{
	List* head, * node, * tail;//头节点,中间节点,尾节点
	head = (List*)malloc(sizeof(List));//内存中开辟头节点空间
	if (length == 0)
	{
		head->next = NULL;
	}
	else
	{
		//连接节头节点与尾节点
		tail = (List*)malloc(sizeof(List));//内存中开辟尾节点空间
		head->next = tail;
		tail->next = NULL;
		//连接中间节点
		for (int i = 0; i < length - 1; i++)
		{
			node = (List*)malloc(sizeof(List));//开辟中间节点空间
			List* temp;
			temp = head->next;
			head->next = node;
			node->next = temp;
		}
	}
	return head; //返回头节点地址
}

/********************************************
函数名称:单链表初始化函数(尾插法)
函数介绍:使用尾插法创建一个指定长度的单链表,
		  并返回其头指针
函数参数:int length -- 单链表指定生成长度
返回类型:List* listHead -- 单链表的头指针
********************************************/
List* InitList_Tail(int length)
{
	List* head, * node, * tail;//头节点,中间节点,尾节点
	head = (List*)malloc(sizeof(List));//内存中开辟头节点空间
	if (length == 0)
	{
		head->next = NULL;
	}
	else
	{
		tail = head;
		//让tail始终为尾节点
		for (int i = 0; i < length; i++)
		{
			node = (List*)malloc(sizeof(List));
			tail->next = node;
			tail = node;
		}
		tail->next = NULL;//尾节点的next为空
	}
	return head; //返回头节点地址
}

/********************************************
函数名称:链表长度查询函数
函数介绍:查询指定链表的长度
函数参数:List* head -- 要查询长度的链表的头指针
返回类型:int length -- 该链表长度
*********************************************/
int Length(List* head)
{
	int length = 0;
	List* temp;
	temp = head;
	while (temp->next != NULL) {
		temp = temp->next;
		length++;
	}
	return length;
}

/********************************************
函数名称:链表销毁函数
函数介绍:销毁指定链表
函数参数:List* head -- 要销毁的链表的头指针
返回类型:无
*********************************************/
void Destroy(List* head) 
{
	List* temp[2];
	temp[0] = head->next;
	temp[1] = temp[0]->next;
	free(head);	//先释放头节点
	while (temp[1]->next != NULL) {
		free(temp[0]);
		temp[0] = temp[1];
		temp[1] = temp[0]->next;
	}
	free(temp[0]);
	free(temp[1]);
}

/**************************************************
函数名称:链表节点赋值函数
函数介绍:给链表的指定链节的数据域赋值
函数参数:List* head -- 要赋值的链表的头指针
		  int n -- 要赋值的链节的序号
		  Dat -- 要赋的值
返回类型:int类型 
		  1 -- 赋值成功 0 -- 错误的链节序号
**************************************************/
int InputElem(List* head, int node_number, Dat* dat)
{
	if (node_number <= 0 || node_number > Length(head))
	{
		return 0;
	}
	else
	{
		List* temp;
		temp = head->next;
		//找到赋值的链节的指针
		for (int i = 0; i < node_number; i++)
		{
			temp = temp->next;
		}
		//赋值
		temp->dat.dat_1 = dat->dat_1;
		return 1;
	}
}

/**************************************************
函数名称:链表节点值查询函数
函数介绍:查询指定链节的值
函数参数:List* head -- 要查询的链表的头指针
		  int n -- 要查询的链节的序号
返回类型:Dat* dat -- 查询到的数据域的指针
**************************************************/
Dat* FindElem(List* head, int node_number)
{
	if (node_number <= 0 || node_number > Length(head))
	{
		return NULL;
	}
	else
	{
		List* temp;
		temp = head->next;
		//找到查询的链节的指针
		for (int i = 0; i < node_number - 1; i++)
		{
			temp = temp->next;
		}
		//返回
		return temp->next;
	}
}

LIST.h

#include <stdlib.h>

//单链表数据域结构体
typedef struct Dat {
	int dat_1;
}Dat;

//单链表结构体
typedef struct List {
	Dat dat; //数据域
	struct List* next; //指针域(下一节点指针)
}List;

//函数声明
List* InitList_Head(int length); //单链表初始化函数(头插法)
List* InitList_Tail(int length); //单链表初始化函数(尾插法)
int Length(List* head); //链表长度查询函数
void Destroy(List* head); //链表销毁函数
int InputElem(List* head, int node_number, Dat* dat); //链表元素赋值函数
Dat* FindElem(List* head, int node_number); //链表值查询函数

main.c

#include <stdio.h>
#include "LIST.h"

void main()
{
	//创建链表(头插法)
	List* list1 = InitList_Head(6);
	//创建链表(尾插法)
	List* list2 = InitList_Tail(7);
	//输出链表长度
	printf("链表1的长度:%d\n", Length(list1));
	printf("链表2的长度:%d\n", Length(list2));
	Dat dat1 = { 55 };
	InputElem(list1, 2, &dat1);//为链表1赋值
	Dat dat2 = { 5 };
	InputElem(list2, 3, &dat2);//为链表2赋值
	//读取链表1的值
	Dat* dat1_write = FindElem(list1, 2);
	printf("链表1的2号位:%d\n", dat1_write->dat_1);
	//读取链表2的值
	Dat* dat2_write = FindElem(list2, 3);
	printf("链表2的3号位:%d\n", dat2_write->dat_1);
	//销毁链表
	Destroy(list1);
	Destroy(list2);
}

  • 4
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

阿斯巴柠檬酸

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

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

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

打赏作者

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

抵扣说明:

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

余额充值