数据结构 - 链表

2023-09-22 13:53:35

线性表的链式存储结构

概念

将线性表 L = (a0, a1, … , an-1)中各元素分布在存储器的不同存储块,成为结点,通过地址或指针建立元素之间的联系。
在这里插入图片描述
结点的 data 域存放数据元素 ai ,而 next 域是一个指针指向 ai 的直接后继 ai+1 所在的结点

  • 下图中的首元结点(头结点) A 的 data 不重要next 域指向链表的真正的第一个结点头结点的作用也是为了指明哪个结点是链表的第一个结点。

  • 最后一个结点的 next 域,也就是下图中最后一个结点的那个 ^ 代表的是 NULL

在这里插入图片描述

结点类型描述

typedef struct node
{
	data_t data; //结点的数据域
	struct node *next; //结点的后继指针域
}listnode, *linklist;

则有:

listnode A;
linklist p = &A;

结点声明

在这里插入图片描述
设 P 指向链表中结点 ai
在这里插入图片描述
获取 ai,写作:p->data
获取 ai+1,写作:p->next->data

若指针 p 的值为 NULL,则它不指向任何结点,此时 p->datap->next 是非法操作。

可调用 C 语言中 malloc() 函数向系统申请结点的存储空间:

linklist p;
p = (linklist)malloc(sizeof(listnode));

则创建一个类型为 linklist 的结点,且该结点的地址已存入指针变量 p 中。

建立单链表

  • 目标:建立单链表
  • 思路:
    1、给头结点分配空间然后判空
    2、初始化头结点(H->data,H->next)
    3、返回头结点
  • 代码:
linklist list_create()
{
	linklist H; //头结点

	//分配空间并判空
	if ((H = (linklist)malloc(sizeof(listnode))) == NULL)
	{
		printf("malloc H failed!\n");
		return NULL;
	}
	
	//初始化头结点
	H->data = 0;
	H->next = NULL;
	
	//返回头结点
	return H;
}

链表尾部插入节点

  • 目标:向单链表的尾部插入新的节点
  • 思路:
    1、建立新节点,给新节点分配空间并初始化
    2、定义指针指示插入节点的位置,这个位置的 next 为空,这样才能在 next 插入新节点,才能保证在链表的结尾插入节点 (从 H 开始,通过 q=q->next 遍历链表直到链表尾部
    3、在指针的 next 插入新节点
  • 代码:
int list_insert_tail(linklist H, data_t value)
{
	linklist p; //要插入的新节点 
	linklist q; //确定插入位置的指针

	//如果头结点为空那么以后的操作也没办法进行
	if (H == NULL)
	{
		printf("H is NULL!\n");
		return -1;
	}

	//给新节点分配空间并判空
	if ((p = (linklist)malloc(sizeof(listnode))) == NULL)
	{
		printf("malloc p failed!\n");
		return -1;
	}
	
	//初始化新节点
	p->data = 0;
	p->next = value;

	q = H;
	//确定插入节点的位置
	while (q->next)
    {
    	q = q->next;
    }

    //在链表尾部插入节点
    q->next = p;

	return 0;
}

根据下标查找节点

  • 目标:根据位置获取链表中的节点
  • 思路:
    1、判头结点是否为空,判下标是否合法;
    2、定义遍历链表的指针和 i 遍历链表直到找到指定位置(注意遍历完链表也没有找到指定节点的情况);
    3、返回要查找的节点或空节点(未找到的情况)
  • 代码:
linklist list_get(linklist H, int pos)
{
	linklist p; //遍历用指针
	int i; //遍历用标记
	
	if (H == NULL)
	{
		printf("H is NULL!\n");
		return NULL;
	}

	if (pos < 0)
	{
		printf("pos is invalid\n");
		return NULL;
	}

	p = H;
	i = -1;
	while (i < pos)
	{
		p = p->next; //指针p向下移动
		//已经遍历完链表但没找到这个位置则break
		if (p == NULL)
		{
			break;
		}
		i++; //i迭代
	}

	return p;
}

在链表指定位置插入新节点

  • 目标:给定链表、位置和新节点的值将这个新节点插入到链表的指定位置
  • 思路:
    1、判头节点是否为空,位置是否合法(初步)
    2、通过指针和 i 遍历链表找到这个要插入节点的前一个节点的位置,让指针指向这个节点
    3、通过改变指针指向插入这个新节点
  • 代码:
int list_insert_between(linklist H, data_t value, int pos)
{
	linklist p; //遍历用指针
	linklist q; //新节点
	int i;
	
	//如果头结点为空那么以后的操作也没办法进行
	if (H == NULL)
	{
		printf("H is NULL!\n");
		return -1;
	}

	//这种位置无效
	if (pos < -1)
	{
		printf("H is invalid!\n");
		return -1;
	}

	//给新节点分配空间并判空
	if ((q = (linklist)malloc(sizeof(listnode))) == NULL)
	{
		printf("malloc p failed!\n");
		return -1;
	}
 	
 	//初始化新节点
 	q->next = NULL;
 	q->data = value;

	//p从头结点开始遍历
	p = H;
	while (i < pos - 1) //注意这里是 pos - 1,一定要在循环结束时让指针指向指定位置的前一个节点
	{
		p = p->next;
		//i还没到pos指针指向节点就空了,证明给定的位置无效
		if (p == NULL)
	    {
	    	printf("the pos not found!\n");
	    	return -1;
	    }
	    i++;
	 }
		
	//此时指针指向指定位置的前一个节点,通过改变指针指向插入新节点
	q->next = p->next;
	p->next = q;

	return 0;
}	

打印链表

  • 目标:打印链表节点
  • 思路:通过指针遍历链表并打印
  • 代码:
int list_show(linklist H) {
    linklist p;
 
    if (H == NULL) {
        printf("H is NULL\n");
        return -1;
    }
 
    p = H;
 
    while (p->next != NULL) {
        printf("%d ", p->next->data);
        p = p->next;
    }
    puts("");
 
    return 0;
}
更多推荐

基于Matlab实现图像分割技术(附上源码+图像)

Matlab是一种功能强大的编程语言和开发环境,被广泛应用于图像处理和计算机视觉领域。图像分割是图像处理中的重要技术之一,它将图像分割成若干个具有相似特征的区域,以便更好地理解和处理图像。在Matlab中,实现图像分割可以使用多种方法和函数。下面将介绍几种常用的图像分割技术及其在Matlab中的实现。基于阈值的分割:基

LeetCode 2591. 将钱分给最多的儿童

【LetMeFly】2591.将钱分给最多的儿童力扣题目链接:https://leetcode.cn/problems/distribute-money-to-maximum-children/给你一个整数money,表示你总共有的钱数(单位为美元)和另一个整数children,表示你要将钱分配给多少个儿童。你需要按照

【AI+医疗】AI在医疗影像设备工作周期中的应用探索

导读随着人工智能技术的飞速发展,越来越多的领域开始与人工智能技术深度融合,产生了一种新型的技术模式——AI+。AI+是指将人工智能技术与其他领域的技术或应用进行结合,在提高效率、精度和创新能力的同时,也为人工智能技术的发展提供了更多的应用场景和数据支持。其中,AI+医疗是人工智能技术与医疗领域的深度融合,通过结合人工智

代码随想录算法训练营第56天 | ● 583. 两个字符串的删除操作 ● 72. 编辑距离 ● 动态规划之编辑距离总结篇

文章目录前言一、583.两个字符串的删除操作二、72.编辑距离三、动态规划之编辑距离总结篇总结前言一、583.两个字符串的删除操作两种思路:1.直接动态规划,求两个字符串需要删除的最小次数2.采用子序列的和-最长公共子序列。思路一分析如下:动规五部曲,分析如下:确定dp数组(dptable)以及下标的含义dp[i][j

频频刷屏朋友圈,白酒如何越来越年轻化?来聊聊白酒企业数字化

最近,某白酒品牌频频吸引大众眼球,白酒与咖啡、巧克力等联名衍生品一经推出便掀起热潮。某商品由于太过火爆,甚至一度售罄下架。不得不说,我国拥有超大规模内需市场,消费潜力巨大。当前,创新消费场景加上数字化融合转型,成为酒企品牌开疆扩土、逆势增长的重要途径。如今越来越多的酒企开始拥抱数字化,建立涵盖白酒品系开发、酒体设计、基

如何用CRM软件系统管理企业客户

客户对企业的重要程度不言而喻。可以说,客户是企业收益的来源和可持续发展的基础,客户的转化率和保留率也与企业的发展息息相关。企业想要成功转化客户,需要经历客户跟踪、挖掘、维护等一系列过程。下面我们来说说,CRM客户管理系统如何管理企业客户?1、CRM系统能从不同渠道获取客户CRM系统可以实现客户信息获取和访问、高效转化和

Js中一些数组常用API总结

前端面试题库(面试必备)推荐:★★★★★地址:前端面试题库【国庆头像】-国庆爱国程序员头像!总有一款适合你!前言Js中数组是一个重要的数据结构,它相比于字符串有更多的方法,在一些算法题中我们经常需要将字符串转化为数组,使用数组里面的API进行操作。本篇文章总结了一些数组中常用的API,我们把它们分成两类,一类是会改变原

数据信息会有哪些风险,云数据库如何保护?

数据信息安风险是多种多样的,那么,云数据库如何规避并保护数据信息安全?今天安策带大家具体来了解数据信息会有哪些风险,云数据库将如何保护:数据泄露:不管是内部人员疏忽,还是恶意攻击、系统漏洞等等原因,数据泄露会导致敏感信息的暴露,损害其品牌的声誉和利益。数据损坏:损坏原因主要由自然灾害、人为或恶意攻击构成,从而导致无法预

容器核心技术之Namespace与Cgroup

容器是一种流行的虚拟化技术,它允许我们在同一台计算机上与其他进程在独立环境中运行进程。那么容器是如何做到这一点的呢?为此,容器是从Linux内核的一些新功能构建的,其中两个主要功能是“namespace”和“cgroup”。1.Namespace1.1Namespace简介Namespace(命名空间)技术是一种内核级

Nvme Spec 第一章节学习

@NvmeExpressBaseSpecification第一章简介1.1概述NVMExpressTM(NVMeTM)接口允许主机软件与非易失性存储器子系统通信。此接口针对企业和客户端固态驱动器进行了优化,通常作为寄存器级接口连接到PCIExpress接口。注:在开发过程中,本规范被称为企业NVMHCI。然而,在完成之

YOLOv8『小目标』检测指南

前言目前博主课题组在进行物体部件的异常检测项目,项目中需要先使用YOLOv8进行目标检测,然后进行图像切割,最后采用WinCLIP模型进行部件异常检测但是在实际操作过程中出现问题,YOLOv8模型目标检测在大目标精确度不错,但是在小目标检测中效果极差我们之前的解决方案是扩大异常部件的目标检测范围,易于检测。但是缺点是会

热文推荐