0
点赞
收藏
分享

微信扫一扫

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

我是小瘦子哟 2022-03-13 阅读 86

目录

单链表介绍

单链表的创建

链表结构体定义

 头插法创建链表

尾插法创建链表

单链表的简单操作

获取单链表长度

链表销毁函数

 链表节点赋值函数

链表节点查询函数

主函数以及测试结果

完整代码

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", Length(list1));printf("链表2的长度:%d", 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", dat1_write->dat_1);//读取链表2的值Dat* dat2_write = FindElem(list2, 3);printf("链表2的3号位:%d", 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", Length(list1));printf("链表2的长度:%d", 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", dat1_write->dat_1);//读取链表2的值Dat* dat2_write = FindElem(list2, 3);printf("链表2的3号位:%d", dat2_write->dat_1);//销毁链表Destroy(list1);Destroy(list2);}

 

 

 

 

 

举报

相关推荐

0 条评论