目录
单链表介绍
与线性表在内存中的储存结构不同(线性表在内存中是一块连续的空间),链表的存储结构是组成链表的数据元素在内存中既可以是连续的,也可以是不连续的。链表中的每个元素都应该包含两个信息:一个是储存数据的信息,即数据域;另一个为储存与之相连的元素的位置信息,即指针域。
而单链表指的是链表中的每个节点中只包含一个指针域。整个链表的存取必须从头指针开始,头指针指示链表中的第一个节点的储存位置(头指针不用存放数据)。由于最后一个元素没有其后继元素,因此最后一个元素的指针域应为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] 指向尾节点时将节点块所指向的节点都释放即可。
为什么不能直接释放头节点?
若只释放头节点,可能会像下列代码一样造成内存溢出。
链表节点赋值函数
/**************************************************
函数名称:链表节点赋值函数
函数介绍:给链表的指定链节的数据域赋值
函数参数: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);
}
完整代码
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);
}