数据结构之线性表(单链表的实现)

news2024/11/26 0:29:53

目录

一、单链表的原理

二、单链表的实现

1.单链表的定义

2.单链表的初始化

3.清空单链表

4.单链表是否为空

5.单链表的长度

6.获取指定位置 i 的元素

7.获取指定元素 e 的位置  

8.向链表中插入指定位置的元素

9.向链表中删除指定位置的元素

10.遍历链表中的元素

三、打印测试功能

1.测试

2.结果输出

3.总代码


一、单链表的原理

        本章节紧跟上一篇文章《顺序表的实现》来继续讲述线性表的一种:单链表。

        前面我们讲的线性表的顺序存储结构。它是有缺点的,最大的缺点就是插入和删除时需要移动大量元素,这显然就需要耗费时间。能不能想办法解决呢?

         要解决这个问题,我们就得考虑一下导致这个问题的原因。为什么当插入和删除时,就要移动大量元素,仔细分析后,发现原因就在于相邻两元素的存储位置也具有邻居关系。它们编号是1,2,3,…,n,它们在内存中的位置也是挨着的,中间没有空隙,当然就无法快速介入,而删除后,当中就会留出空隙,自然需要弥补。问题就出在这里。

        我们可以在第一个元素时,就知道第二个元素的位置(内存地址),而找到它;在第二个元素时,再找到第三个元素的位置(内存地址)。这样所有的元素我们就都可以通过遍历而找到,而这就是单链表的特点,如下图所示。

         而链表的结构则如下图所示,第一个节点的指针由头节点所指,第二个节点的指针由第一个节点所指,以此类推。

二、单链表的实现

1.单链表的定义

        定义一些功能常量和单链表的结构。

        这里需要注意的是,我给链表起别名的是一个指针,后面我们在实现链表功能的时候,我们传递函数参数的时候要传递的是一个双指针(LinkList* L)。 这是因为链表的结构使然,因为链表存储的是其他链表的指针,它是一个地址,后面我们在进行链表的增加和删除的时候,会经常涉及到指针的变化。

        如果函数的参数是本身一级指针,那么在函数内部对指针的修改将不会反映到函数外部,因为函数接收到的是指针的一个副本。使用指针的指针作为参数,则可以确保在函数内部对指针的修改能够反映到原始的指针上。

typedef  struct  Node*  LinkList;

#include <iostream>

#define MAX_SIZE 20
#define OK 1
#define ERROR 0
#define TRUE 1
#define FALSE 0
typedef int Status;
typedef int ElemType;

typedef struct Node
{
	ElemType data;
	struct Node* next;
}Node;

typedef struct Node* LinkList;
2.单链表的初始化

        链表的初始化主要是为头节点分配内存空间,使它的data 和 next 都指向为 NULL,方便我们后续的操作。

Status InitList(LinkList* L)
{
	*L = (LinkList)malloc(sizeof(Node));
	if (*L == NULL) return ERROR;
	(*L)->data = 0;
	(*L)->next = NULL;
	return OK;
}
3.清空单链表

        对链表进行清空操作,因为是用 malloc 进行分配的空间,对堆中的空间进行释放操作。

Status ClearList(LinkList* L)
{
	LinkList p, q;

	p = (*L)->next;
	while (p != NULL) 
	{	
		q = p->next;
		free(p);
		p = q;
	}
	(*L)->next = NULL;
	return OK;
}
4.单链表是否为空

        判断头节点指向的元素是否为空。即 next 是否为 NULL。

Status isListEmpty(LinkList L)
{
	if (L->next  == NULL) return TRUE;
	return FALSE;
}
5.单链表的长度

        求链表的长度,定义一个变量,然后用while循环遍历链表。

int ListLength(LinkList L)
{
	int num{ 0 };
	LinkList temp = L->next;
	while (temp != NULL)
	{
		temp = temp->next;
		num++;
	}
	return num;
}
6.获取指定位置 i 的元素

        定义一个变量,进行while操作,判断是否与指定位置 i 相同,获取其节点值。

Status GetElem(LinkList L, int i, ElemType* e)
{
	int num{ 1 };
	LinkList temp = L->next;
	while (temp != NULL && num < i)
	{
		temp = temp->next;
		num++;
	}	
	if (temp == NULL || num > i) return ERROR;
	*e = temp->data;
}
7.获取指定元素 e 的位置  

        和上述功能类似,返回值为 int 位置。

int LocateElem(LinkList L, ElemType* e)
{
	int num{ 0 };
	LinkList temp = L->next;
	while (temp != NULL)
	{
		num++;
		if (temp->data = *e)
		{
			return num;
		}
		temp = temp->next;
	}
	return 0;
}
8.向链表中插入指定位置的元素

          此功能为链表的难点所在,需要根据图像理解实现,如下图所示。 

     

Status LinkInsert(LinkList* L, int i, ElemType e)
{
	int j;
	LinkList p, s;
	p = *L;
	j = 1;

	while (p != NULL && j < i)
	{
		p = p->next;
		++j;
	}	
	if (!p || j > i) return ERROR;

	s = (LinkList)malloc(sizeof(Node));
	if (s == NULL) return ERROR;
	s->data = e;
	
	s->next = p->next;
	p->next = s;
	
	return OK;

}
9.向链表中删除指定位置的元素

        和上功能类似,如图所示。

Status LinkDelete(LinkList* L, int i, ElemType* e)
{
	int j;
	LinkList p, s;
	j = 1;
	p = (*L);
 	while (p != NULL && j < i)
	{
		p = p->next;
		++j;
	}
	if (!p || j > i) return ERROR;

	s = p->next;
	p->next = s->next;
	*e = s->data;
	free(s);
	return OK;
}
10.遍历链表中的元素

        功能比较简单,通过不断遍历打印其节点的data值。

Status visit(ElemType e)
{
	printf("%d->", e);
	return OK;
}
Status ListTraverse(LinkList L)
{
	LinkList p = L->next;
	while (p != NULL)
	{
		visit(p->data);
		p = p->next;
	} 
	printf("\n");
	return OK;
}

三、打印测试功能

1.测试

        测试主要为测试以上的功能是否可以实现。

int TestSingleList()
{
	LinkList L;
	ElemType e;
	Status ret;
	int j, k;
	// 初始化
	ret = InitList(&L);
	printf("初始化长度:%d\n", ListLength(L));

	for(j = 1; j <= 5; j++)
	{
		ret = LinkInsert(&L, 1, j);
	}
	printf("插入5个元素后:");
	ListTraverse(L);
	ret = isListEmpty(L);
	printf("是否为空,%d\n", ret);
	ret = ClearList(&L);
	printf("清空后的长度:%d\n", ListLength(L));
	printf("是否为空,%d\n", ret);
	for (j = 1; j <= 10; j++)
	{
		ret = LinkInsert(&L, j, j);
	}
	printf("插入10个元素后:");
	ListTraverse(L);

	LinkInsert(&L, 3, 100);
	printf("在第2个节点后面增加元素100:");
	ListTraverse(L);

	LinkDelete(&L, 2, &e);
	printf("删除第二个节点,节点元素为%d :", e);
	ListTraverse(L);

	ClearList(&L);

	return 0;
}
2.结果输出

           结果输出如图所示:

3.总代码

        总代码如下:



#include <iostream>

#define MAX_SIZE 20
#define OK 1
#define ERROR 0
#define TRUE 1
#define FALSE 0
typedef int Status;
typedef int ElemType;

typedef struct Node
{
	ElemType data;
	struct Node* next;
}Node;

typedef struct Node* LinkList;

Status visit(ElemType e)
{
	printf("%d->", e);
	return OK;
}

Status InitList(LinkList* L)
{
	*L = (LinkList)malloc(sizeof(Node));
	if (*L == NULL) return ERROR;
	(*L)->data = 0;
	(*L)->next = NULL;
	return OK;
}

Status ClearList(LinkList* L)
{
	LinkList p, q;

	p = (*L)->next;
	while (p != NULL) 
	{	
		q = p->next;
		free(p);
		p = q;
	}
	(*L)->next = NULL;
	return OK;
}

Status isListEmpty(LinkList L)
{
	if (L->next  == NULL) return TRUE;
	return FALSE;
}


int ListLength(LinkList L)
{
	int num{ 0 };
	LinkList temp = L->next;
	while (temp != NULL)
	{
		temp = temp->next;
		num++;
	}
	return num;
}

Status GetElem(LinkList L, int i, ElemType* e)
{
	int num{ 1 };
	LinkList temp = L->next;
	while (temp != NULL && num < i)
	{
		temp = temp->next;
		num++;
	}	
	if (temp == NULL || num > i) return ERROR;
	*e = temp->data;
}

int LocateElem(LinkList L, ElemType* e)
{
	int num{ 0 };
	LinkList temp = L->next;
	while (temp != NULL)
	{
		num++;
		if (temp->data = *e)
		{
			return num;
		}
		temp = temp->next;
	}
	return 0;
}

Status LinkInsert(LinkList* L, int i, ElemType e)
{
	int j;
	LinkList p, s;
	p = *L;
	j = 1;

	while (p != NULL && j < i)
	{
		p = p->next;
		++j;
	}	
	if (!p || j > i) return ERROR;

	s = (LinkList)malloc(sizeof(Node));
	if (s == NULL) return ERROR;
	s->data = e;
	
	s->next = p->next;
	p->next = s;
	
	return OK;

}

Status LinkDelete(LinkList* L, int i, ElemType* e)
{
	int j;
	LinkList p, s;
	j = 1;
	p = (*L);
 	while (p != NULL && j < i)
	{
		p = p->next;
		++j;
	}
	if (!p || j > i) return ERROR;

	s = p->next;
	p->next = s->next;
	*e = s->data;
	free(s);
	return OK;
}

Status ListTraverse(LinkList L)
{
	LinkList p = L->next;
	while (p != NULL)
	{
		visit(p->data);
		p = p->next;
	} 
	printf("\n");
	return OK;
}

Status CreateListHead(LinkList* L,int n)
{
	LinkList p;
	srand((unsigned)time(0));
	*L = (LinkList)malloc(sizeof(Node));
	if (*L == NULL) return ERROR;
	(*L)->next = NULL;
	for (int i = 0; i < n; i++)
	{
		p = (LinkList)malloc(sizeof(Node));
		if (p == NULL) return ERROR;
		p->data = rand() % 100 + 1;
		p->next = (*L)->next;
		(*L)->next = p;
	}
	return OK;
}

Status CreateListTail(LinkList* L,int n)
{
	LinkList p, r;
	srand((unsigned)time(0));
	*L = (LinkList)malloc(sizeof(Node));
	if (*L == NULL) return ERROR;
	(*L)->next = NULL;
	r = (*L);
	for (int i = 0; i < n; i++)
	{
		p = (LinkList)malloc(sizeof(Node));
		if (p == NULL) return ERROR;
		p->data = rand() % 100 + 1;
		r->next = p;
		r = p;
	}
	return OK;
}







int TestSingleList()
{
	LinkList L;
	ElemType e;
	Status ret;
	int j, k;
	// 初始化
	ret = InitList(&L);
	printf("初始化长度:%d\n", ListLength(L));

	for(j = 1; j <= 5; j++)
	{
		ret = LinkInsert(&L, 1, j);
	}
	printf("插入5个元素后:");
	ListTraverse(L);
	ret = isListEmpty(L);
	printf("是否为空,%d\n", ret);
	ret = ClearList(&L);
	printf("清空后的长度:%d\n", ListLength(L));
	printf("是否为空,%d\n", ret);
	for (j = 1; j <= 10; j++)
	{
		ret = LinkInsert(&L, j, j);
	}
	printf("插入10个元素后:");
	ListTraverse(L);

	LinkInsert(&L, 3, 100);
	printf("在第2个节点后面增加元素100:");
	ListTraverse(L);

	LinkDelete(&L, 2, &e);
	printf("删除第二个节点,节点元素为%d :", e);
	ListTraverse(L);

	ClearList(&L);

	return 0;
}

int main()
{
	return TestSingleList();
}


本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2035678.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

zdppy+vue3+onlyoffice开发文档管理系统实战 20240813登录功能中Python生成验证码校验的研究

遗留的问题 1、登录功能 5、设计登录的接口 6、前后端联调&#xff0c;实现登录功能 7、要记录登录的Token和用户名&#xff0c;跳转到首页 2、注册功能 3、用户管理 4、角色管理 5、权限管理 6、分享功能 tb_user拆成基本信息和详细信息两张表。 6、前后端联调&#xff0c…

设计模式-标识映射(Identity Map)

概念 通过在映射中保存每个已经加载的对象&#xff0c;确保每个对象只加载一次。当要访问对象的时候&#xff0c;通过映射来查找他们。 从数据库加载对象时&#xff0c;对象与其映射的一致性、重复加载&#xff0c;这些都是需要得到保证的。 标识映射记录在一个业务事务中从数…

2024年中国AI大模型场景应用趋势解读

引言 >> 近年来&#xff0c;随着人工智能技术的飞速发展&#xff0c;AI大模型逐渐成为推动各行业数字化转型的重要驱动力。本文从行业现状、应用痛点、发展趋势以及投资机会等方面进行详细解读&#xff0c;探讨AI大模型在未来的广泛应用前景。 一、AI大模型行业应用现状…

【LLM大模型】Llama3.1 部署本地知识库应用

一. 环境介绍 高性能应用服务 HAI 拥有丰富的预装应用&#xff0c;可以将开源社区的前沿模型快速转化为您专有的部署实践&#xff0c;一键拉起&#xff0c;即开即用。现已支持在HAI购买页的社区应用中&#xff0c;找到Llama 3.1等应用的入口&#xff0c;简单选型后&#xff0c…

【MySQL 05】数据类型

&#x1f308; 一、数据类型的作用 如果向 MySQL 特定的类型中插入不合法的数据&#xff0c;MySQL 一般会将本次操作直接拦截。反过来讲&#xff0c;能被成功插入到 MySQL 中的数据一定合法。在 MySQL 中&#xff0c;数据类型本身也是一种约束&#xff0c;这种约束约束的是使用…

一篇文章教会你如何使用Haproxy,内含大量实战案例

1. Haproxy 介绍 HAProxy是法国开发者 威利塔罗&#xff08;Willy Tarreau&#xff09; 使用C语言编写的自由及开放源代码软件&#xff0c;是一款具备高并发&#xff08;万级以上&#xff09;、高性能的TCP和HTTP应用程序代理. HAProxy运行在当前的硬件上&#xff0c;可以支持…

Ubuntu20源码安装Moveit 与 OMPL

文章目录 一、源码安装OMPL1.1 先检查是否安装二进制ompl1.2 若已经提前安装二进制&#xff0c;需要先行卸载1.3 OMPL官网安装教程 一、源码安装OMPL 1.1 先检查是否安装二进制ompl //进入到如下目录下 cd /opt/ros/noetic/// 查找 find ./ -name "libompl*" find .…

Linux用户ID和组ID

在 Linux 系统中&#xff0c;用户和组 ID 用于识别进程和文件的访问权限。每个进程都有与之关联的实际用户 ID、实际组 ID、有效用户 ID、有效组 ID 以及附属组 ID。 实际用户 ID (Real User ID, UID) 定义&#xff1a;实际用户 ID 是启动进程的用户的 ID。作用&#xff1a;标…

文件中找TopK问题 的详细讲解

一&#xff1a;问题&#xff1a; 从一个包含10000整数的文件中找出最大的前10个数。 二&#xff1a;方法&#xff1a; 1&#xff1a;先直接拿文件的前10个数&#xff0c;建造一个小堆 2&#xff1a;再依次读取文件中&#xff0c;剩下的数&#xff0c;比堆顶大&#xff0c;则…

【MATLAB源码-第246期】基于matlab的秃鹰搜索优化算法(BES)机器人栅格路径规划,输出做短路径图和适应度曲线

操作环境&#xff1a; MATLAB 2022a 1、算法描述 秃鹰搜索优化算法&#xff08;Bald Eagle Search, BES&#xff09;是一种新颖的群体智能优化算法&#xff0c;受自然界中秃鹰猎食行为的启发而设计。与其他群体智能算法类似&#xff0c;BES试图通过模拟自然界的某些行为来解…

SSM药房管理系统---附源码18275

目录 摘要 1 绪论 1.1 研究目的意义 1.2国内外研究现状 2 药房管理系统分析 2.1 可行性分析 2.1.1 技术可行性分析 2.1.2经济可行性分析 2.1.3社会可行性分析 2.1.4操作可行性分析 2.2 系统流程分析 2.2.1数据新增流程 2.2.2数据删除流程 2.3 药房管理系统 功能分…

idea过滤器 过滤所有页面除了登录页面 !(包括白名单简洁概括)

1、创建过滤器包&#xff0c;创建LoginFilter类 2.在LoginFilter类中写过滤代码 //白名单List<String> whitelist Arrays.asList("/login.jsp");//用来存放配置文件中Action节点的属性List<Action> actionList new ArrayList<>(); public void …

这些坑都没有踩过,还敢说你做过自动化测试?

在执行冒烟测试、回归测试或多浏览器兼容性测试时&#xff0c;利用web自动化测试可以显著节省人力成本&#xff0c;因此web自动化测试的价值非常大。然而&#xff0c;任何从事过web自动化测试的人都会有这样的体会:写自动化代码相对简单&#xff0c;但维护的成本却非常高。一日…

除了 EcoVadis 审核,企业还需要应对哪些可持续发展方面的挑战?

除了 EcoVadis 审核&#xff0c;企业在可持续发展方面还可能面临以下挑战&#xff1a; 对 ESG 的认识和理解不足&#xff1a;企业的领导者和各层级员工可能对 ESG&#xff08;环境、社会治理&#xff09;的内涵、要求和重要性缺乏清晰认知&#xff0c;这会影响企业在日常运营中…

Redis的基本数据结构整理以及实战运用案例

目录 一、背景介绍 二、Redis数据结构介绍 1、String(字符串) &#xff08;1&#xff09;缓存不常变化的信息&#xff0c;如用户信息、地址信息、配置信息等 &#xff08;2&#xff09;分布式锁的使用 &#xff08;3&#xff09;原子递增递减&#xff0c;可以做一些简单的…

重启人生计划-拒绝内耗

&#x1f973;&#x1f973;&#x1f973; 茫茫人海千千万万&#xff0c;感谢这一刻你看到了我的文章&#xff0c;感谢观赏&#xff0c;大家好呀&#xff0c;我是最爱吃鱼罐头&#xff0c;大家可以叫鱼罐头呦~&#x1f973;&#x1f973;&#x1f973; 如果你觉得这个【重启人生…

算法——动态规划:完全背包问题

文章目录 一、问题描述二、解决方案1. DP 状态的设计2. 状态转移方程3. 算法复杂度4. 举例5. 实现6. 滚动数组实现 三、总结 一、问题描述 问题的抽象&#xff1a;给定 n n n 种物品和一个背包&#xff0c;第 i i i 种物品的体积为 c i c_i ci​&#xff0c;价值为 w i w_…

Python项目——轻松实现动态网页爬虫|附详细源码

动态网页爬虫是专门设计用来爬取动态网页内容的自动化程序或工具。与静态网页爬虫不同&#xff0c;动态网页的内容不是预先存储在服务器上的HTML文件&#xff0c;而是根据用户的请求、交互、时间、数据库状态或其他外部因素动态生成的。这些动态内容通常通过JavaScript、AJAX&a…

2024年3个超好用的思维导图软件推荐

专业又经典的思维导图模板&#xff0c;全部拿去&#xff0c;可以直接套用的喔 1、TreeMind树图 推荐指数&#xff1a;☆☆☆☆☆ 点击直达官网>>https://shutu.cn/ TreeMind树图是一个在线思维导图工具&#xff0c;提供65万份思维导图模板&#xff0c;涵盖了各种使用场…

Java二十三种设计模式-观察者模式(15/23)

观察者模式&#xff1a;实现对象间的松耦合通知机制 引言 在当今的软件开发领域&#xff0c;设计模式已成为创建可维护、可扩展和可重用代码的基石。在众多设计模式中&#xff0c;观察者模式以其独特的能力&#xff0c;实现对象间的松耦合通信而脱颖而出。本文将深入探讨观察…