顺序表与链表

news2024/11/28 0:35:06

思维导图:

 

顺序表与链表都是两种线性表,但是两者之间又有着许多的不同。顺序表是一种连续的空间,实际上就是数组。链表是不连续的空间,链表的空间是一块一块的开辟出来的。

两者的优点与缺点

顺序表:

优点:1.顺序表的空间是连续的,所以能够支持下标的随机访问。

缺点:2.顺序表的空间是连续的容易造成空间的浪费。

链表:

优点:1.空间不连续,要用时才申请所以不会造成空间的浪费。

缺点:2.空间不连续不能支持下标的随机访问。

一,顺序表的操作

1.顺序表的结构

顺序表的本质是数组,所以要定义一个有数组的结构体。并且,这个顺序表是动态的。所以我们又需要一个表示顺序表内元素个数的变量size和一个表示顺序表容量的变量capacity。结构体定义如下:

 代码:

typedef  int dataType;
typedef struct List
{
	dataType* a;//数组
	int size;//个数
	int capacity;//容量
}List;

2.顺序表的初始化

 顺序表的初始化是一个必要的操作,数组指针先初始化为NULL,size初始化为0,capacity初始化为0.代码如下:

void ListInit(List* list)
{
	assert(list);//防止传入NULL
	list->a = NULL;
	list->size = 0;
	list->capacity = 0;
}

 3.顺序表的前插操作

顺序表的插入操作都要先判断顺序表内的空间是否够用,所以在插入数据到顺序表之前得先对顺序表的容量是否已满进行判断。先写一个判断容量的函数,代码如下:

代码:

void checkCapacity(List* list)
{
	assert(list);
	if (list->size == list->capacity)
	{
		int newcapacity = list->capacity==0 ? 4: 2 * list->capacity;
		dataType* tmp = (dataType*)realloc(list->a, sizeof(dataType) * newcapacity);
		if (tmp == NULL)
		{
			perror("realloc fail!");
			return;
		}
		list->a = tmp;
		list->capacity = newcapacity;
	}
	
}

 然后便是对顺序表的头插操作,头插操作插入的位置都是数组下标为0的位置。为了实现这一操作,我们就必须在数组不为空的条件下对数据进行后移然后将下标为0的位置腾出来。代码如下:

代码:

void LishPushFront(List* list, dataType x)
{
	assert(list);
	checkCapacity(list);//判断容量是否已满
	if (list->size == 0)//当数组为空时直接插入
	{
		list->a[0] = x;
	}
	else//数组不为空时要将原有数据后移将下标为0的位置腾出
	{
		int end = list->size - 1;
		for (int i = end;i >= 0;i--)
		{
			list->a[i + 1] = list->a[i];
		}
		list->a[0] = x;
	}
	list->size++;//插入后数组元素个数增加
	
}

4,顺序表的尾插操作

顺序表的尾插操作也是一个插入操作,所以尾插操作的第一步便是对数组的容量进行检查。然后才是数据的尾插操作。尾插不需要数据的移动,只需要在size的位置上插入数据即可。

代码如下:

代码:

void ListPushBack(List* list, dataType x)
{
	assert(list);
	checkCapacity(list);
	list->a[list->size] = x;
	list->size++;
}

5.顺序表的头删

顺序表的头删操作是一个移动数据覆盖然后将size减1的过程。说是删除数据其实就是覆盖掉数组下标为0的位置的数据。注意要对数组是否为空进行判断,当数组为空时不能够对这个顺序表进行删除!!代码如下:

代码:

void ListPopFront(List* list)
{
	assert(list);
	assert(list->size > 0);//对顺序表是否为空进行判断
	for (int i = 1;i < list->size;i++)
	{
		list->a[i - 1] = list->a[i];
	}
	list->size--;
}

 6.顺序表的尾删

顺序表的尾删操作比起顺序表的头删操作就显得更加简单。顺序表的尾删操作不需要覆盖只需要让数组的最后一个数据访问不到便可,也就是将size减1。注意要对数组是否为空进行判断,当数组为空时不能够对这个顺序表进行删除操作!!代码如下:

代码:

void ListPopBack(List* list)
 {
	 assert(list);
	 assert(list->size > 0);
	 list->size--;
 }

 7.顺序表的中间插入

顺序表的中间插入操作实现的功能就是将要插入的数据插入到要插入的下标pos位置处。中间插入的操作能够被头插1和尾插复用进而实现头插与尾插。中间插入操作代码如下:

代码:

void ListInsert(List* list, int pos, dataType x)
 {
	 assert(list);
	 assert(0 <= pos && pos <= list->size);//对pos的值进行判断以免造成越界
	 checkCapacity(list);
	 for (int i = list->size;i >= pos;i--)
	 {
		 list->a[i] = list->a[i - 1];
	 }
	 list->a[pos] = x;
	 list->size++;
 }

 8.顺序表的中间删除操作

顺序表的中间删除操作就是将下标为pos的位置上的数据删除的操作。也能被其他两个删除操作进行复用从而实现头删和尾删。和前两个操作一样中间删除的操作的删除就是覆盖下标pos位置上的1数据或者让pos数据上的数据不可访问。中间删除操作代码如下:

代码:

void ListErase(List* list, int pos)
 {
	 assert(list);
	 assert(list->size>0);
	 assert(0 <= pos && pos < list->size);
	 for (int i = pos;i < list->size-1;i++)
	 {
		 list->a[i] = list->a[i + 1];
	 }
	 list->size--;
 }

9.顺序表内数据的寻找

在顺序表内寻找某个数据其实并不难,不过就是遍历顺序表内的数组然后看看有没有匹配的数据。如果有便返回数据的下标,没有便返回不存在的下标-1。代码如下:

 代码:

int ListFind(List* list, dataType x)
 {
	 assert(list);
	 for (int i = 0;i < list->size;i++)
	 {
		 if (list->a[i] == x)
			 return i;
	 }
	 return -1;
 }

10,顺序表数据的修改

 顺序表内数据的修改这一操作是和上一个查找操作配合着使用的。只有要查找的元素存在才能替换。否则便不能。代码如下:

代码:

void ListModify(List* list,int i,dataType x)
 {
	 assert(list);
	 if (i == -1)//i这个值是通过ListFind函数找到的下标
	 {
		 printf("要修改的值不存在!\n");
		 return;
	 }
	 else
	 {
		 list->a[i] = x;
	 }

 }

11.顺序表的销毁

太简单了,直接上代码:

代码:

 void ListDestory(List* list)
 {
	 assert(list);
	 free(list->a);
	 list->a = NULL;
	 list->size = list->capacity = 0;
 }

二,链表的创建与操作

1.链表的创建

链表是一种不连续的空间,但是又要将一个一个的节点联系起来并且链表里还要放置一些数据。所以链表的结构里就要有两个变量,一个是存下一个链表节点的指针,一个是存当前节点内数据的变量。链表节点的结构代码如下:

代码:

typedef int  dataType;
typedef struct listNode
{
	dataType val;//存放当前节点内的数据
	struct listNode* next;//存放下一个节点的地址
}listNode;

 2.链表的头插操作

链表的头插操作在执行时要分两种情况。第一种情况是链表为NULL时,你需要让链表的头节点头节点指向newnode。当链表不为NULL时,newnode指向的next就是原来的头节点,然后让头节点指向新的头节点。代码如下:

代码:

void SlistPushFront(SListNode** pphead, dataType x)
{
	assert(pphead);
	assert(pphead);//防止传入空指针
	SListNode* newnode = (SListNode*)malloc(sizeof(SListNode));
	if (newnode == NULL)
	{
		perror("malloc fail\n");
		return;
	}
	newnode->val = x;
	newnode->next = NULL;
	//当链表为NULL
	if (*pphead == NULL)
	{
		*pphead = newnode;
	}
	//当链表不为NULL时
	else
	{
		newnode->next = *pphead;
		*pphead = newnode;
	}
}

 3.链表的尾插操作

链表的尾插操作其实与头插操作差不多。最主要的是当链表不是NULL时需要去找到尾节点,然后让尾节点的next指向newnode。代码如下:

代码:

void SlistPushBack(SListNode** pphead, dataType x)
{
	assert(pphead);//防止传入空指针
	SListNode* newnode = (SListNode*)malloc(sizeof(SListNode));
	if (newnode == NULL)
	{
		perror("malloc fail\n");
		return;
	}
	newnode->val = x;
	newnode->next = NULL;
	//当链表为NULL
	if (*pphead == NULL)
	{
		*pphead = newnode;
	}
    //找尾插入值
	else
	{
		SListNode* tail = *pphead;
		while (tail->next)
		{
			tail = tail->next;
		}
		tail->next = newnode;
	}
}

以上两个代码能复用的地方:生成节点的地方

代码:

SListNode* BuyNode(dataType x)
{
	SListNode* node = (SListNode*)malloc(sizeof(SListNode));
	if (node == NULL)
	{
		perror("malloc fail\n");
		return;
	}
	node->val = x;
	node->next = NULL;
	return node;
}

改进后的代码:

头插:

void SlistPushFront(SListNode** pphead, dataType x)
{
	assert(pphead);
	assert(pphead);//防止传入空指针
	SListNode* newnode = BuyNode(x);
	if (newnode == NULL)
	{
		perror("malloc fail\n");
		return;
	}
	newnode->val = x;
	newnode->next = NULL;
	//当链表为NULL
	if (*pphead == NULL)
	{
		*pphead = newnode;
	}
	//当链表不为NULL时
	else
	{
		newnode->next = *pphead;
		*pphead = newnode;
	}
}

尾插:

void SlistPushBack(SListNode** pphead, dataType x)
{
	assert(pphead);//防止传入空指针
	SListNode* newnode = BuyNode(x);
	if (newnode == NULL)
	{
		perror("malloc fail\n");
		return;
	}
	newnode->val = x;
	newnode->next = NULL;
	//当链表为NULL
	if (*pphead == NULL)
	{
		*pphead = newnode;
	}
	else
	{
		SListNode* tail = *pphead;
		while (tail->next)
		{
			tail = tail->next;
		}
		tail->next = newnode;
	}
}

四,链表的头删操作

链表的头删操作也是一个简单的操作,这个操作就是将头节点给销毁掉然后再让头指针指向第二个节点。这也要分两种情况来删除,第一种情况是链表为NULL不能删,第二种情况便是当链表内只有一个头节点时直接删除这个节点然后置空便可以了。第三种情况是当链表内有多个节点时需要将第二个节点的地址保存下来然后再将头节点删掉并置空。代码如下:

代码:

void SListPopFront(SListNode** pphead)
{
	assert(pphead);
	//当链表为空时不能够继续删除
	assert(*pphead);
	//只有一个节点时
	if ((*pphead)->next == NULL)
	{
		free(*pphead);
		*pphead = NULL;
	}
	//有两个节点时
	else
	{
		SListNode* next = (*pphead)->next;
		free(*pphead);
		*pphead = next;
	}

}

五,链表的尾删

链表的尾删操作就是一个将链表的尾节点删除的操作。这个操作也得分三种情况:

1.链表为NULL不删   2.链表内只有一个节点,直接删除第一个节点  3.链表内有多个节点,找到最后一个节点删除置空。代码如下:

 代码:

void SListPopBack(SListNode** pphead)
{
	assert(pphead);
	assert(*pphead);
	//只有一个节点
	if ((*pphead)->next == NULL)
	{
		free(*pphead);
		*pphead = NULL;
	}
	else
	{
		SListNode* tail = *pphead;
		SListNode* prev = NULL;//记录尾节点的前一个节点
		while (tail->next)//找尾节点
		{
			prev = tail;
			tail = tail->next;
		}
		free(tail);//去尾
		tail = NULL;//将尾节点置空
		prev->next = NULL;//尾节点的前一个节点的next指向NULL,消除野指针问题
	}
}

六,链表的中间插入

链表的中间插入操作讲的是在链表的某个数据的位置处插入一个操作者想要插入的值。这个操作的关键点就在于找到要插入位置的前一个位置然后将这个位置的nex指向给改掉,改成指向我们想要插入的值。代码如下:

代码:

void SListInsert(SListNode** pphead,dataType target ,dataType x)
{
	assert(pphead);
	SListNode* cur = *pphead;//表示当前节点
	SListNode* prev = NULL;//表示当前节点的前一个节点
	while (cur)
	{
		SListNode* newnode = BuyNode(x);
		if (cur->val == target)
		{
			if (prev == NULL)//当第一个节点就是目标值所在节点时
			{
				newnode->next = *pphead;
				*pphead = newnode;
			}
			else//当其他节点才是目标值所在节点时
			{
				prev->next = newnode;
				newnode->next = cur;

			}
			return ;
	   }
		prev = cur;
		cur = cur->next;
	}
//当链表循环完以后链表内便没有数据为目标值的节点
	printf("链表内没有要找的目标值\n");
	return ;
}

七,链表的中间删除

链表的中间删除操作的作用是将目标值所在节点删除,释放掉。这个操作的代码和中间插入的代码有异曲同工之妙,都要找到要删除的节点的前一个节点然后再执行下列的操作。也有两种情况要讨论——1,当第一个节点为目标节点   2,当其它节点为目标节点。

void SListErase(SListNode** pphead, dataType target)
{
	assert(pphead);
	assert(*pphead);
	SListNode* cur = *pphead;
	SListNode* prev = NULL;
	while (cur)
	{
		SListNode* next = cur->next;
		if (cur->val == target)
		{
			if (prev == NULL)
			{
				free(cur);
				cur = next;
			}
			else
			{
				free(cur);
				prev->next = next;

			}
			return ;
		}
		prev = cur;
		cur = cur->next;

	}
	printf("链表内没有要删除的目标节点\n");
	return;
}

当然,中间删除与插入的代码的写法不止这一种,我们还可以通过寻找某个值所在的节点来对链表进行中间插入与删除。这里读者可以自己思考一下该如何写代码。 如果要写这样一个代码的话还需要写一个find函数来找到这个节点然后利用这个函数来与中间插入,中间删除函数配合着使用。

八,链表的销毁

 这个代码比较简单,但是一定要记得在删除掉当前节点的时候要记录一下之后的节点。代码如下:

代码:

void SListDestory(SListNode** pphead)
{
	SListNode* cur = *pphead;
	while (cur)//一个一个节点销毁
	{
		SListNode* next = cur->next;
		free(cur);
		cur = next;
	}
	*pphead = NULL;//最后将外面的list置空
}

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

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

相关文章

86、基于STM32的电动车 小车蓄电池锂电池充电桩系统设计(程序+原理图+PCB源文件+硬件资料+元器件清单等)

单片机主芯片选择方案 方案一&#xff1a;AT89C51是美国ATMEL公司生产的低电压&#xff0c;高性能CMOS型8位单片机&#xff0c;器件采用ATMEL公司的高密度、非易失性存储技术生产&#xff0c;兼容标准MCS-51指令系统&#xff0c;片内置通用8位中央处理器(CPU)和Flash存储单元&a…

云计算——虚拟化层架构

作者简介&#xff1a;一名云计算网络运维人员、每天分享网络与运维的技术与干货。 座右铭&#xff1a;低头赶路&#xff0c;敬事如仪 个人主页&#xff1a;网络豆的主页​​​​​ 前言 本章将会讲解云计算的虚拟化层架构&#xff0c;了解云计算虚拟化层都有哪些架构模式…

23. 销售额完成任务指标的商品

文章目录 题目需求存在的疑问实现一题目来源 题目需求 商家要求每个商品每个月需要售卖出一定的销售总额 假设1号商品销售总额大于21000&#xff0c;2号商品销售总额大于10000&#xff0c;其余商品没有要求 请写出SQL从订单详情表中&#xff08;order_detail&#xff09;查询连…

多元分类预测 | Matlab基于深度置信网络DBN的分类预测,多特征输入模型,DBN分类预测

文章目录 效果一览文章概述部分源码参考资料效果一览 文章概述

MySQL的match函数在sp中使用的BUG解析

一、问题发现 在一次开发中在sp中使用MySQL PREPARE以后&#xff0c;使用match AGAINST语句作为prepare stmt的参数后&#xff0c;发现执行第二遍call会导致数据库crash&#xff0c;于是开始动手调查问题发生的原因。 注&#xff1a;本次使用的 MySQL 数据库版本为最新的debug…

文件改名神器!简体中文一键翻译为繁体中文并智能保存到指定文件夹!

在这个简繁转换教程中&#xff0c;您将学习如何使用简单的方法将简体中文文件快速翻译为繁体中文&#xff0c;并将其保存至指定的文件夹中。无需复杂的翻译软件或多步操作&#xff0c;只需跟随以下步骤&#xff0c;您就能实现简繁转换的便捷保存。 首先第一步&#xff0c;我们…

机器学习一:线性回归

1 知识预警 1.1 线性代数 ( A T ) T A (A^\mathrm{T})^\mathrm{T}A (AT)TA$ ( A B ) T A T B T (AB)^\mathrm{T}A^\mathrm{T}B^\mathrm{T} (AB)TATBT ( λ A ) T λ A T (\lambda A)^\mathrm{T}\lambda A^\mathrm{T} (λA)TλAT ( A B ) T B T A T (AB)^\mathrm{T}B^…

C#加载 ToolBlock简单示例

可以用visionpro的VPPVersion.exe 工具查看文件格式。 在安装路径最后一个 官方示例文件路径 简单实例 using System; using System.Collections.Generic; using System.ComponentModel; using System.Data; using System.Drawing; using System.Linq; using System.Text; …

Spark学习(二)---Spark运行架构和核心概念

1.Spark运行架构 Spark框架的核心是一个计算引擎&#xff0c;它采用了master-slave的结构。 图形中的 Driver 表示 master&#xff0c; 负责管理整个集群中的作业任务调度。图形中的 Executor 则是 slave&#xff0c;负责实际执行任务。 1.1 核心组件 由此可以得出&#xf…

接口(Interface)

接口 基本介绍 接口就是给出一些没有实现的方法&#xff0c;封装到一起&#xff0c;到某个类要使用的时候&#xff0c;再根据具体情况把这些方法写出来。 class 类名 implements 接口{自己属性;自己方法;必须实现的接口的抽象方法; // 只需要重写抽象方法即可 }接口中的方法…

硬盘接口损坏换电路板

1.有一块西数1T的蓝盘&#xff0c;SATA接口L形塑料掰断了&#xff0c;顾换块板解决接触不良问题 2.买板子&#xff0c;看好板子上印刷的版号&#xff0c;2060-771829-005 REV A&#xff0c;tb上买一片通型号的&#xff0c;十几块 3.用烙铁焊下原来烂板的8个脚的BIOS芯片&…

java.lang.noclassdeffounderror: com/fasterxml/jackson/core/util/jacksonfeature

建议直接查看我的原博 1.问题 环境&#xff1a; springboot2.3.10.RELEASE jdk1.8 elasticsearch8.8.1 根据官网&#xff0c;使用es时pom文件需要引入json工具&#xff0c;这里使用了jackson-databind&#xff1a; <dependency><groupId>co.elastic.clients…

栈(单位数计算器)

方法&#xff1a; 判断优先级 判断字符还是数字 计算方法 查看栈顶元素 思路 个位数计算器的代码&#xff1a; package calculator;public class Calculator {public static void main(String[] args) {String exp "78*9-2";Stack num new Stack(10);Stack op…

【CSS】浮动

&#x1f4dd;个人主页&#xff1a;爱吃炫迈 &#x1f48c;系列专栏&#xff1a;HTMLCSS &#x1f9d1;‍&#x1f4bb;座右铭&#xff1a;道阻且长&#xff0c;行则将至&#x1f497; 文章目录 浮动浮动的规则浮动的案例浮动的清除 浮动 float属性可以指定一个元素应沿其容器的…

Linux中配置sudo用户访问权限

文章目录 一、如何在 Linux 中配置 sudo 的访问权限1.1、添加一个Linux普通用户有 sudo 权限1.2、测试普通用户的 sudo 权限1.3、添加多个Linux普通用户有 sudo 权限1.4、验证sudo 权限 一、如何在 Linux 中配置 sudo 的访问权限 1.1、添加一个Linux普通用户有 sudo 权限 [ro…

部分抓包测试

linux下使用tcpdump抓包&#xff0c;生成pcap格式文件&#xff0c;利用wireshark打开&#xff0c;进行数据包分析 tcpdump常用选项&#xff1a; -a&#xff1a;尝试将网络和广播地址转换成名称&#xff1b; -c<数据包数目>&#xff1a;收到指定的数据包数目后&#xff0…

又一款国产AI聊天工具360智脑

介绍 360智脑是一个基于深度学习技术的大型语言模型&#xff0c;能够进行自然语言理解和生成。它拥有海量的语料库和强大的计算能力&#xff0c;可以应用于智能客服、智能问答、机器翻译等多种场景&#xff0c;为用户提供高效准确的服务和支持。 功能测试 写代码 功能齐全 …

使用Flask Web创建一个调用ChatGPT API的网页--简单示例(Windows环境下)

前提&#xff1a;你应该要有一个能正常使用chatGPT的openAI账号&#xff1b;即你已经成功注册了chatGPT&#xff0c;并能正常使用。 文章目录 一、主要组成部分二、示例代码2.1 工程结构&#xff1a;2.2 说明2.3 依赖环境2.4 app.py代码2.5 index.html代码 三、搭建环境步骤 一…

Spark Sql 4/5

4. 用户自定义函数 通过spark.udf功能用户可以自定义函数。 4.1用户自定义UDF函数 Shellscala> val df spark.read.json("examples/src/main/resources/people.json")df: org.apache.spark.sql.DataFrame [age: bigint, name: string]​scala> df.show()--…

PSI算法经典论文算法概述

文章目录 什么是隐私求交PSIPSI协议分类PSI算法的分类基于哈希函数的PSI算法基于不经意传输&#xff08;OT&#xff09;的 PSI算法基于GC的PSI算法基于公钥加密的PSI算法基于DH的PSI算法基于RSA盲签名的PSI算法基于同态加密的PSI算法 基于差分隐私的PSI算法 总结参考文献 什么是…