单链表实践

目的

学习单链表的基本结构及基本功能

代码内容

#include<stdio.h>
#include<malloc.h>
#define OK 1
#define ERROR 0
typedef int ElemType;
typedef int Status;

typedef struct LNode
{
	ElemType data;//数据域
	struct LNode* next;//指针域
}LNode, *LinkList;

Status LinkList_Create(LinkList& L)//用尾插法创建一个带头结点的单链表,以输入0结束
{
	ElemType node;
	L = (LinkList)malloc(sizeof(LNode));//先创建一个带头结点的空链表
	L->next = NULL;
	printf("输入单链表中各元素值:(以输入0结束)\n");
	scanf_s("%d", &node);//输入链表中第一个结点的数据值
	LinkList r = L;//引入一个尾指针,使其始终指向当前表的表尾
	while (node != 0)//如果输入的数据值为0,则结束循环
	{
		LinkList p = (LinkList)malloc(sizeof(LNode));//为新结点p分配空间
		if (!p)
			return ERROR;
		p->data = node;//将输入的数据值存入新结点的数据域
		r->next = p;//修改链,使新结点p插入到链表的表尾
		r = p;
		scanf_s("%d", &node);//输入链表中下一个结点的数据值
	}
	r->next = NULL;//最后尾结点的后继指针置空
	return OK;
}

Status LinkList_Insert(LinkList& L, int i, ElemType e)//在单链表L的第i个数据元素之前插入新的元素e,i的合法值是1<=i<=n+1
{
	LinkList p = L, s;//p指针指示链表中的头结点
	int j = 0;//j指示p指针所指的结点在表中的位序号
	while (p && j < i - 1)//找到第i个元素的前驱结点,并用p指针指向它
	{
		p = p->next;
		++j;
	}
	if (!p || j > i - 1)//i不合法(找不到前驱结点)
		return ERROR;
	s = (LinkList)malloc(sizeof(LNode));//产生新的结点
	s->data = e;
	s->next = p->next;//修改链指针,让新结点插入到第i-1个元素结点p的后面
	p->next = s;
	return OK;
}

Status LinkList_Delete(LinkList& L, int i, ElemType& e)//删除单链表L中的第i个数据元素,并通过e返回其值,i的合法值是1<=i<=n
{
	LinkList p = L, q;//p指针指示链表中的头结点
	int j = 0;//j指示p指针所指向的结点在表中的位序号
	while (p->next && j < i - 1)//找到被删除结点的前驱结点
	{
		p = p->next;
		++j;
	}
	if (!p->next || j > i - 1)//i不合法(找不到前驱结点)
		return ERROR;
	q = p->next;//q指向待删结点
	p->next = q->next;//修改链指针让待删结点从链中脱离出来
	e = q->data;//用e保存待删结点的数据元素值
	free(q);//释放待删结点空间
	return OK;
}

Status LinkList_LocateElem(LinkList L, int i, ElemType& e)//查找单链表中第i个数据元素,如果查找成功,则通过e返回其数据元素值,i的合法值是1<=i<=n
{
	LinkList p = L->next;//p指向链表中的首结点
	int j = 1;//j记录p结点在表中的位序号
	while (p && j < i)//沿着后继指针一个一个“点数”
	{
		p = p->next;
		j++;
	}
	if (!p || j > i)//i不合法
		return ERROR;
	e = p->data;//用e返回第i个元素的值
	return OK;
}

void LinkList_Output(LinkList L)//输出单链表L中各数据元素值
{
	LinkList p = L->next;//p指向链表的首结点
	printf("链表中的内容为:\n");
	while (p)
	{
		printf("%d ", p->data);//输出p指针所指结点的数据值
		p = p->next;//p指针后移
	}
	printf("\n");
	printf("\n");
}

int main()
{
	LinkList L;
	int i;
	ElemType e;
	LinkList_Create(L);//用尾插法创建一个带头结点的单链表,以输入0结束
	LinkList_Output(L);//输出单链表中各元素值

	printf("输入插入位置:\n");
	scanf_s("%d", &i);//输入插入位置
	printf("输入待插入的元素值:\n");
	scanf_s("%d", &e);//输入待插入的元素值
	if (LinkList_Insert(L, i, e))
		LinkList_Output(L);//输出插入操作后的单链表
	else
		printf("Insert failed!\n");

	printf("输入删除位置:\n");
	scanf_s("%d", &i);//输入删除位置
	if (LinkList_Delete(L, i, e))
	{
		LinkList_Output(L);//输出删除操作后的单链表
		printf("删除的元素是%d\n", e);
	}
	else
		printf("Deletion failed!\n");
	printf("输入待查找元素所在表中的位置:\n");
	scanf_s("%d", &i);//输入待查找元素所在表中的位置
	if (LinkList_LocateElem(L, i, e))
		printf("%d Search succeeded!\n", e);//输出查找找到的元素值及“Search succeeded!”
	else
		printf("Search failed!\n");//输出“Search failed!”
	return 0;
}

逻辑思路

编写基本框架

预定义

#include<stdio.h>
#include<malloc.h>
#define OK 1
#define ERROR 0
typedef ElemType;
typedef Status;

链表结点结构体定义

typedef struct LNode
{
	ElemType data;//数据域,用于存放数据
	struct LNode* next;//指针域,用于存储下一个结点的位置
}LNode, *LinkList;
代码解析
struct LNode* next;
  • struct LNode: 这里的 struct LNode 指的是一个结构体类型,名为 LNode。这个结构体代表链表的一个节点,包含了两个部分:数据部分 (data) 和指向下一个节点的指针部分 (next)。

  • *next: 这个 * 表示 next 是一个指针,指向类型为 struct LNode 的节点。换句话说,next 指向链表中下一个节点的内存地址。

tips:

可以将该结构体想象成一根铁链中的一个小圆环,小圆环由两部分组成:环内空隙、实体的铁环。

环内空隙用来存放东西,即数据域;实体铁环可以将两个环相扣,联系了两个空隙,使他们呈现线性关系,即指针(文字不好描述,可自行想象或用多个回形针串联参悟)

LNode, *LinkList;
  • LNode:

    • 这里的 LNode 是一个类型别名,它代表了结构体 LNode。通过 typedef,你可以直接使用 LNode 而不必每次都写 struct LNode。因此,LNode 类型可以new一个实际的链表节点。
  • *LinkList:

    • 这里的 *LinkList 是一个指针类型的别名。LinkList 被定义为指向 struct LNode 的指针。通过 LinkList,你可以方便地定义和使用链表指针,而不需要每次都写 struct LNode*

tips:

typedef struct LNode
{
}LNode, *LinkList;

可等同于

int A,B;

即typedef struct LNode(自定义类型LNode)等同于int

LNode, *LinkList等同于A,B

创建单链表(尾插法)

Status LinkList_Creat(LinkList& L)
{
	ElemType node;
	L = (LinkList)malloc(sizeof(LNode));
	L->next = NULL;
	printf("输入单链表中各元素值:(以输入0结束)\n");
	scanf_s("%d", &node);
	LinkList r = L;
	while (node != 0)
	{
		LinkList p = (LinkList)malloc(sizeof(LNode));
		if (!p)
			return ERROR;
		p->data = node;
		r->next = p;
		r = p;
		scanf_s("%d", &node);
	}
	r->next = NULL;
	return OK;
}
代码解析
L = (LinkList)malloc(sizeof(LNode));
  1. malloc(sizeof(LNode)):

    • malloc 是 C 语言中的一个函数,用于动态分配内存。
    • sizeof(LNode) 返回结构体 LNode 的大小(以字节为单位)。因此,malloc(sizeof(LNode)) 会分配足够的内存来存储一个 LNode 结构体的实例。
  2. (LinkList):

    • 这是一个类型转换,将 malloc 返回的 void* 指针转换为 LinkList 类型(即 struct LNode* 的指针类型)。
    • 这是一种将通用指针转为特定指针类型的方式,以便可以使用更具体的数据结构。
  3. L =:

    • 这里 L 是一个变量,其类型是 LinkList,即指向 struct LNode 的指针。
    • 这行代码的作用是将动态分配的内存地址(一个 LNode 的指针)赋值给 L

tips:

可与顺序表中的分配内存进行对比,可知双方在构成上基本没有区别

L.elem = (ElemType*)malloc(LIST_INIT_SIZE * sizeof(ElemType));

(各部分含义见优快云

L = (LinkList)malloc(sizeof(LNode));
L->next = NULL;

即创建一个空表

LinkList r = L;

新建一个辅助指针r,用于跟踪链表的尾结点

while (node != 0)
{
	LinkList p = (LinkList)malloc(sizeof(LNode));
	if (!p)
	return ERROR;
	p->data = node;
	r->next = p;
	r = p;
	scanf_s("%d", &node);
}

用while循环新建结点,并储值,r->next = p;连接结点,r = p;更新尾结点

r->next = NULL;

链表创建完成,将尾指针置空,表示无后续结点

编写输出模块

Status LinkList_Output(LinkList L)
{
	LinkList p = L->next;
	while (p)
	{
		printf("%d", p->data);
		p = p->next;
	}
	printf("\n");
}
代码解析
LinkList p = L->next;

新建指针p,并使它指向头结点的下一个结点(头结点不储存元素)

创建主函数

int main()
{

}

编写基础功能

插入

Status LinkList_Insert(LinkList& L, int i, ElemType e)//在单链表L的第i个数据元素之前插入新的元素e,i的合法值是1<=i<=n+1
{
	LinkList p = L, s;//p指针指示链表中的头结点
	int j = 0;//j指示p指针所指的结点在表中的位序号
	while (p && j < i - 1)//找到第i个元素的前驱结点,并用p指针指向它
	{
		p = p->next;
		++j;
	}
	if (!p || j > i - 1)//i不合法(找不到前驱结点)
		return ERROR;
	s = (LinkList)malloc(sizeof(LNode));//产生新的结点
	s->data = e;
	s->next = p->next;//修改链指针,让新结点插入到第i-1个元素结点p的后面
	p->next = s;
	return OK;
}
代码逻辑

找到第i个元素的前驱结点,修改链指针插入

删除

Status LinkList_Delete(LinkList& L, int i, ElemType& e)//删除单链表L中的第i个数据元素,并通过e返回其值,i的合法值是1<=i<=n
{
	LinkList p = L, q;//p指针指示链表中的头结点
	int j = 0;//j指示p指针所指向的结点在表中的位序号
	while (p->next && j < i - 1)//找到被删除结点的前驱结点
	{
		p = p->next;
		++j;
	}
	if (!p->next || j > i - 1)//i不合法(找不到前驱结点)
		return ERROR;
	q = p->next;//q指向待删结点
	p->next = q->next;//修改链指针让待删结点从链中脱离出来
	e = q->data;//用e保存待删结点的数据元素值
	free(q);//释放待删结点空间
	return OK;
}
代码逻辑

找到前驱结点,使用辅助指针q记录待删结点,修改链接,释放待删结点

查找

Status LinkList_LocateElem(LinkList L, int i, ElemType& e)//查找单链表中第i个数据元素,如果查找成功,则通过e返回其数据元素值,i的合法值是1<=i<=n
{
	LinkList p = L->next;//p指向链表中的首结点
	int j = 1;//j记录p结点在表中的位序号
	while (p && j < i)//沿着后继指针一个一个“点数”
	{
		p = p->next;
		j++;
	}
	if (!p || j > i)//i不合法
		return ERROR;
	e = p->data;//用e返回第i个元素的值
	return OK;
}

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值