文章目录
前言
例如:随着不段的学习深入,我们发现,线性表虽然可以随机存取表中任意一个元素,但是插入删除操作需要移动大量的元素,为此,引入了单链表。
提示:以下是本篇文章正文内容,下面案例可供参考
一、单链表是什么?
线性表的链式存储,即单链表。它通过一组任意的存储单元来存储线性表中的元素。
1.单链表结点结构
在单链表中,对于每一个结点,有着存放数据信息的data,和指向后继的null。
下面展示一些代码。
单链表结构特征
#include <stdio.h>
#include <stdlib.h>
#define ElemType int
struct LNode{ //定义单链表的结点的结构体
ElemType data; //数据域
struct LNode *next; //指针阈
};
二、单链表的基本操作
1.单链表初始化
方式1
int InitList1(){
struct LNode *L;
L=(struct LNode*)malloc(sizeof(LNode));
if (L==NULL){
return 0;}
L->next=NULL;
return 1;
}
方式2
//方式2
int Initlist2(struct LNode **L){
*L=(LNode*)malloc(sizeof(LNode));
if(*L==NULL){
return 0;
}
(*L)->next=NULL;
return 1;
}
方式3
在王道的书籍中,其对单链表的初始化操作为
//方式3
bool Initlist3(LinkList &L){ //带头结点单链表的初始化
L=(LNode*)malloc(sizeof(LNode)); //创建头结点
L->next=NULL; //头结点后暂时无元素结点
return ture;
}
但是在C语言中,不能够这样使用,上为C++的操作。因为在C语言中,没有bool类型,且C语言中形参不可用&,但是可以使用*操作来替代。上述代码中的LinkList实际上是对struct LNode *的简写即重命名。此操作需要在定义结构体时,使用typdef进行修饰。具体操作如下
typdef struct LNode{
Elemtype data;
struct LNode *next;
}LNode,*LinkList; //其中LNode表示结点。
//LinkList 是 struct LNode * 的别名,表示指向链表结点的指针。
//通常,LinkList 被用来表示整个链表的 头指针,即指向链表第一个结点的指针。
方式4
如果在C语言下实现单链表的初始化,则需要下面的操作,实际上就是将方式2中的struct LNode*替换成了LinkList,体现了代码的简洁性。
//方式4
int Initlist4(LinkList *L){//L为指向链表的指针的指针。选择*L作为参数,是为了访问并修改指向链表的指针
*L=(LNode*)malloc(sizeof(LNode)); //*L为指向结点的指针。
//在动态为结构体分配内存时,我们需要一个指向结构体的指针,该指针用于存储内存地址。
if(*L==NULL){
return 0;
}
(*L)->next=NULL;
return 1;
}
上述代码中出现了很多的指针,应该如何去理解呢?
对于一级指针*n
&n 自身地址
n 指向地址
*n 指向地址值
对于二级指针**n
&n 自身地址
n 一级指针地址
*n 一级指针指向地址
**n 一级指针指向地址值
如方式4中L指向指向链表的指针,即一级指针地址。
*L指向链表的第一个结点,即一级指针指向的地址。
2.单链表求表长
//求表长
int length(LinkList L) {
int len=0;
LNode *P=L;
while (P->next=NULL) {
len++;
}
return len;
}
3.单链表按序号查找结点
//按序号查找 结点,即寻找的是Lnode。找什么就设什么。
LNode* GetElem(LinkList L,int i) {
LNode*P=L; //指针P指向当前扫描到的结点。因为若按序号查找,若序号为0,即头结点,所以令指针指向头结点。
int j=0; // 记录当前结点的位序,头结点是第0个结点
while (P!=NULL&&j<i) {//循环找到第i个结点
P=P->next;
j++;
}
return P; //返回第i个结点的指针或Null。
}
4.单链表按值查找结点
//按值查找结点
LNode* LocateElem (LinkList L,ElemType e) {
LNode *P=L->next; //头结点一定不存储数据,于是便让指针从序号为1的结点开始
while (P!=NULL&&e!=L->data) {
P=P->next;
}
return P;
}
5.单链表插入结点操作
//插入结点操作
int Listinsert(LinkList *L,int i,ElemType e) { //L为被插入的结点,因为值发生改变,所以需要使用&。i为插入序号,e为插入数据。
LNode *P=*L; //指针P指向当前扫描到的结点。
int j=0;
while (P!=NULL&&j<i-1) {
P=P->next;
j++;
}//这一步的操作即按照序号查找结点。不过结点需要找的是结点i的前一个结点,新结点的前驱即P(i-1),后驱是曾经的P(i-1)->next。
if(P==NULL) {
return 0; //i值不合法
}
else {
LNode *S=(LNode*)malloc(sizeof(LNode));
S->data=e;
S->next=P->next;
P->next=S;
return 1;
}
}
6.单链表删除结点操作
//删除结点操作
int ListDelete(LinkList *L,int i,ElemType *e) { //e储存被删除结点的数据,可以加*也可以不加*,不影响结点的删除。
LNode*P=*L;
int j=0;
while (P!=NULL&&j<i-1) {
P=P->next;
j++;
}
if(P==NULL) {
return 0;
}else {
LNode*Q=P->next; //Q指向被删除结点
*e=Q->data; //将删除结点的 数据储存到e中
P->next=Q->next; //将删除结点断开
free(Q); //释放删除结点内存
return 1;
}
}
7.头插法建立单链表
//利用头插法建立单链表
LinkList List_HeadInsert(LinkList *L) { //逆项建立单链表
LNode*s;
int x; //设置元素类型为整型。
*L=(LNode*)malloc(sizeof(LNode));//创建头结点
if (*L == NULL) { // 检查内存分配是否成功
printf("Memory allocation failed.\n");
exit(1);
}
(*L)->next=NULL; //初始化为空链表。
scanf("%d",&x);
while (x!=9999) {
s=(LNode*)malloc(sizeof(LNode));
if (s == NULL) { // 检查内存分配是否成功
printf("Memory allocation failed.\n");
exit(1);
}
s->data=x;
s->next=(*L)->next;
(*L)->next=s;
scanf("%d",&x);
}
return *L;
}
8.尾插法建立单链表
LinkList List_TailInsert(LinkList *L) { //逆项建立单链表
int x; //设置元素类型为整型。
*L=(LNode*)malloc(sizeof(LNode));//创建头结点
if (*L == NULL) { // 检查内存分配是否成功
printf("Memory allocation failed.\n");
exit(1);
}
(*L)->next=NULL;
LNode *r=*L; //r为尾指针
printf("Enter elements (enter 9999 to stop):\n");
scanf("%d",&x);
while (x!=9999) {
LNode* s=(LNode*)malloc(sizeof(LNode));
if (s == NULL) { // 检查内存分配是否成功
printf("Memory allocation failed.\n");
exit(1);
}
s->data=x;
s->next=NULL;
r->next=s;
r=s;
scanf("%d",&x);
}
return *L;
}
三、总结
下为完整的代码部分。
#include <stdio.h>
#include <stdlib.h>
typedef int ElemType; // 定义 ElemType 为 int
typedef struct LNode {
ElemType data; // 数据域
struct LNode *next; // 指针域,指向下一个结点
} LNode, *LinkList; // LNode 表示单个结点,LinkList 表示指向链表头指针
// 1. 单链表初始化操作
int Initlist(LinkList *L) {
*L = (LNode *)malloc(sizeof(LNode)); // 分配内存给头结点
if (*L == NULL) {
return 0; // 内存分配失败
}
(*L)->next = NULL; // 初始化为空链表
return 1; // 初始化成功
}
// 2. 求单链表长度
int length(LinkList L) {
int len = 0;
LNode *P = L->next; // 从第一个数据结点开始遍历
while (P != NULL) {
len++;
P = P->next;
}
return len;
}
// 3. 按序号查找结点
LNode* GetElem(LinkList L, int i) {
int j = 0;
LNode *P = L; // 从头结点开始查找
while (P != NULL && j < i) {
P = P->next;
j++;
}
return P; // 返回第i个结点的指针或NULL
}
// 4. 按值查找结点
LNode* LocateElem(LinkList L, ElemType e) {
LNode *P = L->next; // 从第一个数据结点开始
while (P != NULL && P->data != e) {
P = P->next;
}
return P; // 返回存储数据e的结点指针或NULL
}
// 5. 单链表插入结点操作
int ListInsert(LinkList *L, int i, ElemType e) {
LNode *P = *L;
int j = 0;
while (P != NULL && j < i - 1) { // 找到第i-1个结点
P = P->next;
j++;
}
if (P == NULL) {
return 0; // i值不合法
}
LNode *S = (LNode *)malloc(sizeof(LNode)); // 分配新结点的内存
if (S == NULL) {
return 0; // 内存分配失败
}
S->data = e; // 插入数据
S->next = P->next; // 新结点指向原来的后继
P->next = S; // 第i-1个结点指向新结点
return 1;
}
// 6. 单链表删除结点操作
int ListDelete(LinkList *L, int i, ElemType *e) {
LNode *P = *L;
int j = 0;
while (P != NULL && j < i - 1) { // 找到第i-1个结点
P = P->next;
j++;
}
if (P == NULL || P->next == NULL) {
return 0; // i值不合法
}
LNode *Q = P->next; // 找到待删除结点
*e = Q->data; // 保存待删除结点数据
P->next = Q->next; // 将第i-1个结点指向第i+1个结点
free(Q); // 释放第i个结点
return 1;
}
// 7. 头插法建立单链表
LinkList List_HeadInsert(LinkList *L) {
LNode *s;
int x; // 设置元素类型为整型
*L = (LNode *)malloc(sizeof(LNode)); // 创建头结点
if (*L == NULL) { // 检查内存分配是否成功
printf("Memory allocation failed.\n");
exit(1);
}
(*L)->next = NULL; // 初始化为空链表
scanf("%d", &x);
while (x != 9999) {
s = (LNode *)malloc(sizeof(LNode));
if (s == NULL) { // 检查内存分配是否成功
printf("Memory allocation failed.\n");
exit(1);
}
s->data = x;
s->next = (*L)->next;
(*L)->next = s;
scanf("%d", &x);
}
return *L;
}
// 8. 尾插法建立单链表
LinkList List_TailInsert(LinkList *L) {
int x; // 设置元素类型为整型
*L = (LNode *)malloc(sizeof(LNode)); // 创建头结点
if (*L == NULL) { // 检查内存分配是否成功
printf("Memory allocation failed.\n");
exit(1);
}
(*L)->next = NULL;
LNode *r = *L; // 尾指针
scanf("%d", &x);
while (x != 9999) {
LNode *s = (LNode *)malloc(sizeof(LNode));
if (s == NULL) { // 检查内存分配是否成功
printf("Memory allocation failed.\n");
exit(1);
}
s->data = x;
s->next = NULL;
r->next = s;
r = s;
scanf("%d", &x);
}
return *L;
}
int main() {
LinkList L;
int init_result = Initlist(&L);
if (init_result) {
printf("单链表初始化成功!\n");
} else {
printf("单链表初始化失败!\n");
}
int len = length(L);
printf("单链表长度为:%d\n", len);
LNode* found_node = GetElem(L, 2);
if (found_node) {
printf("按序号查找成功,第 2 个节点的数据为:%d\n", found_node->data);
} else {
printf("按序号查找失败!\n");
}
found_node = LocateElem(L, 5);
if (found_node) {
printf("按值查找成功,值为 5 的节点找到。\n");
} else {
printf("按值查找失败!\n");
}
int insert_result = ListInsert(&L, 3, 10);
if (insert_result) {
printf("插入节点成功!\n");
} else {
printf("插入节点失败!\n");
}
int delete_result;
ElemType deleted_data;
delete_result = ListDelete(&L, 4, &deleted_data);
if (delete_result) {
printf("删除节点成功,删除的数据为:%d\n", deleted_data);
} else {
printf("删除节点失败!\n");
}
LinkList head_insert_list = List_HeadInsert(&L);
printf("头插法建立单链表结果:\n");
LNode* temp = head_insert_list->next;
while (temp) {
printf("%d ", temp->data);
temp = temp->next;
}
printf("\n");
LinkList tail_insert_list = List_TailInsert(&L);
printf("尾插法建立单链表结果:\n");
temp = tail_insert_list->next;
while (temp) {
printf("%d ", temp->data);
temp = temp->next;
}
printf("\n");
return 0;
}