椋鸟数据结构笔记#3:链表

萌新的学习笔记,写错了恳请斧正。


目录

链表的定义

链表的分类

方向(单向还是双向)

头节点(哨兵节点)的有无

循环或不循环

8种分类

不带头单向不循环链表的实现

带头单向循环链表的实现

链表与顺序表的差异


链表的定义

链表是一种基于指针的,物理结构不连续而逻辑结构连续的数据结构。大概就是说,把一个一个数据存放在一个一个节点中,每一个节点可能创建在内存的不同区域,但是每个节点都包括了至少一个指向下一个节点的指针。这样,我们就可以通过指针来按顺序找到整个链表的数据。

我们一般创建一个结构体类型作为链表的节点,结构体的成员包括若干数据和若干指针,其中数据部分称为链表的数据域,指针部分称为链表的指针域。(有些指针属于数据,要具体分析其功能

链表的分类

方向(单向还是双向)

链表可以是单向的也可以是双向的。

单向代表链表每个节点的指针域指向其下一个节点,可以顺着指针一路向后寻找而不能反过来向前寻找;而双向链表的节点一般有两个指针,一个指向后一个节点,另一个指向前一个节点。

头节点(哨兵节点)的有无

头结点本身不存储任何实际的数据元素,而是作为链表的第一个节点存在,其主要作用是指向链表的第一个实际存储数据的节点。使用头结点的好处在于可以使链表的插入和删除操作更加统一和简洁,因为即使是在链表的开头进行操作,也不需要对链表进行特殊处理,从而避免了一些边界条件的检查

在不带头结点的链表中,插入新的第一个元素或删除现有的第一个元素时,需要特别处理链表的头指针,这可能会使代码变得复杂且容易出错。而在带有头结点的链表中,无论插入或删除操作发生在链表的哪个位置,操作流程都是统一的,因为头结点为链表提供了一个统一的入口

循环或不循环

如果链表的最后一个节点再次指向整个链表的第一个节点(哨兵节点或数据节点),那么这个链表就是循环的。循环的结构可以为实现很多比较复杂的数据处理功能带来便利。

8种分类

所以从上面3个角度考虑,链表一共可以分为8种(排列组合),但是最常用的有两种:不带头单向不循环链表(简称单链表)、带头双向循环链表

这两种链表中,单链表结构最简单,但是一般不会直接使用,而是作为一些更复杂的数据结构的子结果,也频繁出现在试题中。

而带头双向循环链表则是实际作为链表使用最频繁的,在实现诸多功能上都存在明显优势。

下面是8种链表的示意图:

不带头单向不循环链表的实现

头文件SList.h

//不带头单向不循环链表(single linked list)#pragma once#include <stdio.h>
#include <stdlib.h>
#include <assert.h>//单链表节点的值类型
typedef int SLDataType;//单链表节点
typedef struct SListNode
{SLDataType data;struct SListNode* next;
} SListNode;//单链表的初始化
#define SListInit(pList) SListNode* pList = NULL//单链表的销毁
void SListDestory(SListNode** ppList);//单链表的头插
void SListPushFront(SListNode** ppList, SLDataType x);
//单链表的尾插
void SListPushBack(SListNode** ppList, SLDataType x);//单链表指定位置后插入
void SListInsertAfter(SListNode* pos, SLDataType x);
//单链表指定位置前插入
void SListInsertBefore(SListNode** ppList, SListNode* pos, SLDataType x);//单链表的头删
void SListPopFront(SListNode** ppList);
//单链表的尾删
void SListPopBack(SListNode** ppList);//单链表删除指定位置的节点
void SListErase(SListNode** ppList, SListNode* pos);
//单链表指定位置后删除
void SListEraseAfter(SListNode* pos);
//单链表指定位置前删除
void SListEraseBefore(SListNode** ppList, SListNode* pos);//单链表删除所有指定值的节点
void SListRemove(SListNode** ppList, SLDataType x);//单链表查找
SListNode* SListFind(SListNode* pList, SLDataType x);
//单链表中间节点
SListNode* SListFindMidNode(SListNode* pList);//单链表的长度
int SListSize(SListNode* pList);
//单链表的判空
int SListEmpty(SListNode* pList);//单链表打印
void SListPrint(SListNode* pList);//单链表逆序
void SListReverse(SListNode** ppList);//单链表冒泡排序
void SListBubbleSort(SListNode** pList);//合并两个有序链表
SListNode* SListMerge(SListNode* pList1, SListNode* pList2);

源文件SList.c

#include "SList.h"//单链表的销毁
void SListDestory(SListNode** ppList)
{//断言assert(ppList);SListNode* cur = *ppList;while (cur){SListNode* next = cur->next;free(cur);cur = next;}*ppList = NULL;
}//新节点申请
SListNode* SListBuyNode(SLDataType x)
{SListNode* newNode = (SListNode*)malloc(sizeof(SListNode));if (newNode == NULL){perror("malloc");exit(EXIT_FAILURE);}newNode->data = x;newNode->next = NULL;return newNode;
}//单链表的头插
void SListPushFront(SListNode** ppList, SLDataType x)
{//断言assert(ppList);SListNode* newNode = SListBuyNode(x);newNode->next = *ppList;*ppList = newNode;
}
//单链表的尾插
void SListPushBack(SListNode** ppList, SLDataType x)
{//断言assert(ppList);SListNode* newNode = SListBuyNode(x);if (*ppList == NULL){*ppList = newNode;}else{SListNode* cur = *ppList;while (cur->next){cur = cur->next;}cur->next = newNode;}
}//单链表指定位置后插入
void SListInsertAfter(SListNode* pos, SLDataType x)
{//断言assert(pos);SListNode* newNode = SListBuyNode(x);newNode->next = pos->next;pos->next = newNode;
}
//单链表指定位置前插入
void SListInsertBefore(SListNode** ppList, SListNode* pos, SLDataType x)
{//断言assert(ppList&&pos);if (*ppList == NULL || *ppList == pos)	//空链表或者pos为头节点{SListPushFront(ppList, x);}else{SListNode* cur = *ppList;while (cur->next != pos){cur = cur->next;}SListNode* newNode = SListBuyNode(x);newNode->next = cur->next;cur->next = newNode;}
}//单链表的头删
void SListPopFront(SListNode** ppList)
{//断言assert(ppList);if (*ppList == NULL){return;}else{SListNode* next = (*ppList)->next;free(*ppList);*ppList = next;}
}
//单链表的尾删
void SListPopBack(SListNode** ppList)
{//断言assert(ppList);if (*ppList == NULL){return;}else if ((*ppList)->next == NULL){free(*ppList);*ppList = NULL;}else{SListNode* cur = *ppList;while (cur->next->next){cur = cur->next;}free(cur->next);cur->next = NULL;}
}//单链表删除指定位置的节点
void SListErase(SListNode** ppList, SListNode* pos)
{//断言assert(ppList&&pos);if (*ppList == NULL){return;}else if (*ppList == pos){SListPopFront(ppList);}else{SListNode* cur = *ppList;while (cur->next != pos){cur = cur->next;}cur->next = pos->next;free(pos);}
}
//单链表指定位置后删除
void SListEraseAfter(SListNode* pos)
{//断言assert(pos);if (pos->next == NULL){return;}else{SListNode* next = pos->next->next;free(pos->next);pos->next = next;}
}
//单链表指定位置前删除
void SListEraseBefore(SListNode** ppList, SListNode* pos)
{//断言assert(ppList&&pos);if (*ppList == NULL || *ppList == pos)	//空链表或者pos为头节点{return;}else if ((*ppList)->next == pos)	//pos为第二个节点{SListPopFront(ppList);}else{SListNode* cur = *ppList;while (cur->next->next != pos){cur = cur->next;}free(cur->next);cur->next = pos;}
}//单链表删除所有指定值的节点
void SListRemove(SListNode** ppList, SLDataType x)
{//断言assert(ppList);if (*ppList == NULL){return;}else{SListNode* cur = *ppList;while (cur->next){if (cur->next->data == x){SListNode* next = cur->next->next;free(cur->next);cur->next = next;}else{cur = cur->next;}}if ((*ppList)->data == x){SListPopFront(ppList);}}
}//单链表查找
SListNode* SListFind(SListNode* pList, SLDataType x)
{//断言assert(pList);SListNode* cur = pList;while (cur){if (cur->data == x){return cur;}cur = cur->next;}return NULL;
}
//单链表中间节点
SListNode* SListFindMidNode(SListNode* pList)
{//断言assert(pList);SListNode* fast = pList;SListNode* slow = pList;while (fast && fast->next){fast = fast->next->next;slow = slow->next;}return slow;
}//单链表的长度
int SListSize(SListNode* pList)
{//断言assert(pList);int count = 0;SListNode* cur = pList;while (cur){count++;cur = cur->next;}return count;
}
//单链表的判空
int SListEmpty(SListNode* pList)
{return pList == NULL ? 1 : 0;
}//单链表打印
void SListPrint(SListNode* pList)
{//断言assert(pList);SListNode* cur = pList;while (cur){printf("%d->", cur->data);cur = cur->next;}printf("NULL\n");
}//单链表逆序
void SListReverse(SListNode** ppList)
{//断言assert(ppList);if (*ppList == NULL || (*ppList)->next == NULL){return;}else{SListNode* cur = *ppList;SListNode* next = cur->next;SListNode* nextNext = next->next;while (next){next->next = cur;cur = next;next = nextNext;if (nextNext){nextNext = nextNext->next;}}(*ppList)->next = NULL;*ppList = cur;}
}//单链表冒泡排序
void SListBubbleSort(SListNode** pList)
{//断言assert(pList);if (*pList == NULL || (*pList)->next == NULL){return;}else{SListNode* tail = NULL;	//尾节点while (tail != *pList)	//只有一个节点时,tail==*pList{SListNode* cur = *pList;	//当前节点SListNode* next = cur->next;	//下一个节点SListNode* prev = NULL;	//当前节点的前一个节点while (next != tail)	//只有两个节点时,next==tail{if (cur->data > next->data)	//交换{if (cur == *pList)	//头节点交换{cur->next = next->next;next->next = cur;*pList = next;prev = next;next = cur->next;}else{cur->next = next->next;next->next = cur;prev->next = next;prev = next;next = cur->next;}}else	//不交换{prev = cur;cur = next;next = next->next;}}tail = cur;}}
}//单链表合并
SListNode* SListMerge(SListNode* pList1, SListNode* pList2)
{if (!pList1){return pList2;}if (!pList2){return pList1;}SListNode* newList = NULL;SListNode* cur1 = pList1;SListNode* cur2 = pList2;SListNode* cur = NULL;while (cur1 && cur2){if (cur1->data < cur2->data){if (!newList){newList = cur = cur1;}else{cur->next = cur1;cur = cur->next;}cur1 = cur1->next;}else{if (!newList){newList = cur = cur2;}else{cur->next = cur2;cur = cur->next;}cur2 = cur2->next;}}if (cur1){cur->next = cur1;}else{cur->next = cur2;}return newList;
}

带头单向循环链表的实现

头文件List.h

//带头双向不循环链表(List)#pragma once#include <stdio.h>
#include <stdlib.h>
#include <assert.h>//定义数据类型
typedef int LTDataType;//定义链表节点
typedef struct ListNode
{LTDataType data;struct ListNode* next;struct ListNode* prev;
} ListNode;//申请节点
ListNode* BuyListNode(LTDataType x);//初始化链表
ListNode* ListInit(void);
//销毁链表
void ListDestory(ListNode** pphead);//打印
void ListPrint(ListNode* phead);//尾插
void ListPushBack(ListNode* phead, LTDataType x);
//头插
void ListPushFront(ListNode* phead, LTDataType x);//尾删
void ListPopBack(ListNode* phead);
//头删
void ListPopFront(ListNode* phead);//查找
ListNode* ListFind(ListNode* phead, LTDataType x);//在pos位置之后插入x
void ListInsert(ListNode* pos, LTDataType x);
//删除pos位置的节点
void ListErase(ListNode* pos);

源文件List.c

#include "List.h"//申请节点
ListNode* BuyListNode(LTDataType x)
{ListNode* newnode = (ListNode*)malloc(sizeof(ListNode));if (newnode == NULL){perror("malloc");exit(EXIT_FAILURE);}newnode->data = x;newnode->next = newnode->prev = NULL;return newnode;
}//初始化链表
ListNode* ListInit(void)
{ListNode* phead = BuyListNode((LTDataType)0);phead->next = phead->prev = phead;return phead;
}
//销毁链表
void ListDestory(ListNode** pphead)
{//断言assert(pphead);if (*pphead == NULL){return;}ListNode* phead = *pphead;ListNode* cur = phead->next;while (cur != phead){ListNode* next = cur->next;free(cur);cur = next;}free(phead);*pphead = NULL;
} //打印
void ListPrint(ListNode* phead)
{//断言assert(phead);ListNode* cur = phead->next;printf("head->");while (cur != phead){printf("%d->", cur->data);cur = cur->next;}printf("tail\n");
}//尾插
void ListPushBack(ListNode * phead, LTDataType x)
{//断言assert(phead);ListNode* tail = phead->prev;ListNode* newnode = BuyListNode(x);newnode->next = phead;newnode->prev = tail;tail->next = newnode;phead->prev = newnode;
}
//头插
void ListPushFront(ListNode* phead, LTDataType x)
{//断言ListNode* newnode = BuyListNode(x);newnode->next = phead->next;newnode->prev = phead;phead->next->prev = newnode;phead->next = newnode;
}//尾删
void ListPopBack(ListNode* phead)
{//断言assert(phead);ListNode* tail = phead->prev;ListNode* prev = tail->prev;prev->next = phead;phead->prev = prev;free(tail);
}
//头删
void ListPopFront(ListNode* phead)
{//断言assert(phead);ListNode* first = phead->next;ListNode* second = first->next;phead->next = second;second->prev = phead;free(first);
}//查找
ListNode* ListFind(ListNode* phead, LTDataType x)
{//断言assert(phead);ListNode* cur = phead->next;while (cur != phead){if (cur->data == x){return cur;}cur = cur->next;}return NULL;
}//在pos位置之后插入x
void ListInsert(ListNode* pos, LTDataType x)
{//断言assert(pos);ListNode* newnode = BuyListNode(x);newnode->next = pos->next;newnode->prev = pos;pos->next->prev = newnode;pos->next = newnode;
}
//删除pos位置的节点
void ListErase(ListNode* pos)
{//断言assert(pos);ListNode* prev = pos->prev;ListNode* next = pos->next;prev->next = next;next->prev = prev;free(pos);
}

链表与顺序表的差异

区别链表顺序表
物理结构不连续连续
随机访问(通过下标任意的访问节点)不支持,O(N)支持,O(1)
任意位置插入删除只需要修改指针的方向即可可能需要大量挪移数据,效率低下
容量没有容量的概念需要管理容量,扩容
应用场景频繁发生任意位置插入时需要高效频繁访问各节点元素时
缓存命中(意味着CPU访问内存的效率)

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

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

相关文章

FPGA高端项目:解码索尼IMX390 MIPI相机转HDMI输出,提供FPGA开发板+2套工程源码+技术支持

目录 1、前言2、相关方案推荐本博主所有FPGA工程项目-->汇总目录我这里已有的 MIPI 编解码方案 3、本 MIPI CSI-RX IP 介绍4、个人 FPGA高端图像处理开发板简介5、详细设计方案设计原理框图IMX390 及其配置MIPI CSI RX图像 ISP 处理图像缓存HDMI输出工程源码架构 6、工程源码…

Mistral 7B v0.2 基础模型开源,大模型微调实践来了

Mistral AI在3月24日突然发布并开源了 Mistral 7B v0.2模型&#xff0c;有如下几个特点&#xff1a; 和上一代Mistral v0.1版本相比&#xff0c;上下文窗口长度从8k提升到32k&#xff0c;上下文窗口&#xff08;context window&#xff09;是指语言模型在进行预测或生成文本时&…

暴雨服务器X7740赋能大模型训练

数字经济浪潮愈演愈烈,大模型训练对服务器的要求也越来越高。在此背景下,暴雨信息发布专门为大规模模型训练而设计的全新旗舰GPU服务器—X7740,以卓越的计算性能、高速网络通信能力以及创新的能效表现,有效赋能大模型训练。 X7740 搭载了暴雨信息最新一代的英特尔至强可扩展处理…

银河麒麟安装回退至GCC5.4.0 添加镜像源地址并设置其优先级。问题已解决

问题&#xff1a; 因为软件依赖低版本的gcc&#xff0c;而新版银河麒麟gcc默认时高版本&#xff0c;考虑到软件适配应该考虑最低版本的麒麟系统&#xff0c;需要将gcc降级。 过程中遇到问题&#xff0c;无法直接找到gcc5.4.0 这是没有软件源&#xff0c;需要添加其他软件源&…

mysqldump备份数据库提示ERROR 1064 (42000): You have an error in your SQL syntax

在dos下备份数据库的时候提示上面的错误信息 1 错误详情 ERROR 1064 (42000): You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near mysql-v at line 1错误示例图 2 解决办法 通过查资料…

Codeforces Round 934 (Div. 2) ---- counting is fun (Easy and Hard Version)

目录 F1. Counting Is Fun (Easy Version): 题目大意&#xff1a; 思路解析&#xff1a; 代码实现&#xff1a; D2. Counting Is Fun (Hard Version)&#xff1a; 思路解析&#xff1a; 代码实现&#xff1a; F1. Counting Is Fun (Easy Version): 题目大意&#xff1a…

DAY16 二叉树最大深度最小深度完全二叉树节点个数

9.二叉树的最大深度 递归法 后序遍历 本题可以使用前序&#xff08;中左右&#xff09;&#xff0c;也可以使用后序遍历&#xff08;左右中&#xff09;&#xff0c;使用前序求的就是深度&#xff0c;使用后序求的是高度。 二叉树节点的深度&#xff1a;指从根节点到该节点…

力扣刷题Days28-66.加一(js)

目录 本题重在思路的学习与思路和代码转换实现关系的学习&#xff01; 1&#xff0c;题目 2&#xff0c;代码 3&#xff0c;学习与总结 思路回顾 1&#xff0c;题目 给定一个由 整数 组成的 非空 数组所表示的非负整数&#xff0c;在该数的基础上加一。最高位数字存放在数…

Java知识点重点拓展

1.2 Java语言的特点 Java语言的广泛应用主要得益于它的核心特点&#xff0c;主要包括跨平台性、面向对象特性、安全性等。 特性描述跨平台性Java能够在多种操作系统上运行&#xff0c;这得益于JVM&#xff08;Java虚拟机&#xff09;。只要设备安装了对应的JVM&#xff0c;Jav…

MySQL Server 8.3.0 重要变更解析

MySQL Server 8.3.0 Innovation 版本是 MySQL 8.x 系列最后一个创新版本&#xff0c;下个月即将迎来 MySQL 8.4.0 LTS 长期支持版本。 关于发版模型变更&#xff0c;在之前的文章 重磅&#xff01;MySQL 8.1.0 已来&#xff01; 中已有所介绍。 这里补充一点&#xff0c;对于 M…

Java反序列化JDK动态代理的关系

Java代理模式 为什么要学习代理模式&#xff1f;了解开发原理&#xff0c;才能明白漏洞的产生。这不仅仅是SpringAOP的底层&#xff01; [SpringAOP 和 SpringMVC] 代理模式的分类&#xff1a; 静态代理动态代理 静态代理 角色分析&#xff1a; 抽象角色&#xff1a;一般会…

A Simple Problem with Integers(线段树)

目录 描述 输入 输出 样例输入 样例输出 思路 建树 第一次错误解法&#xff08;正确解法在下面&#xff0c;可跳过这一步&#xff09; 正确解法 code 描述 You have N integers, A1, A2, ... , AN. You need to deal with two kinds of operations. One type of …

java中的单例模式

一、描述 单例模式就是程序中一个类只能有一个对象实例 举个例子: //引出单例模式&#xff0c;一个类中只能由一个对象实例 public class Singleton1 {private static Singleton1 instance new Singleton1();//通过这个方法来获取实例public static Singleton1 getInstance…

【文献分享】通过形态扫描仪阐明自组装肽聚集:蛋白质-肽结构表征的新工具

题目&#xff1a;Elucidating Self‐Assembling Peptide Aggregation via Morphoscanner: A New Tool for Protein‐Peptide Structural Characterization 通过形态扫描仪阐明自组装肽聚集&#xff1a;蛋白质-肽结构表征的新工具 自组装和分子折叠在自然界中无处不在&#xff…

你该选择哪个职业呢?数据科学家、数据分析师和数据工程师

每周跟踪AI热点新闻动向和震撼发展 想要探索生成式人工智能的前沿进展吗&#xff1f;订阅我们的简报&#xff0c;深入解析最新的技术突破、实际应用案例和未来的趋势。与全球数同行一同&#xff0c;从行业内部的深度分析和实用指南中受益。不要错过这个机会&#xff0c;成为AI领…

CQ 社区版2.10.0 | 新增 SQL 审核、全新英文版上线…

三月中旬&#xff0c;我们预告了 CloudQuery 社区版即将上线的「SQL 审核」功能。现在&#xff0c;它来了&#xff01; 本次社区版 v2.10.0&#xff0c;除了 SQL 审核功能&#xff0c;我们还在手动授权、连接分组等模块做了新功能和优化。 新增功能 新增 SQL 审核功能 支持…

路径规划——搜索算法详解(二):Floyd算法详解与MATLAB代码

上次总结了Dijkstra算法的案例原理与代码&#xff0c;本文分享第二种比较基础且易懂的方法为Floyd算法&#xff0c;该算法可以有效正确地处理有向图的最短路径问题&#xff0c;与Dijkstra算法不同&#xff0c;Floyd算法是一种动态规划算法&#xff0c;对于稠密图效果显著。原理…

安达发|APS计划排产系统帮助纺织业实现企业数字化管理

APS&#xff08;高级计划排产系统&#xff09;是一种基于供应链管理和约束理论的计划排产工具&#xff0c;它通过模拟和优化企业的生产、物流等运作过程&#xff0c;帮助企业实现精细化管理。在纺织业中&#xff0c;APS的应用可以极大地推动企业数字化管理的进程&#xff0c;具…

武汉星起航:各大企业力挺亚马逊,共筑跨境电商新生态

亚马逊电商平台作为全球领先的跨境电商巨头&#xff0c;一直以来都备受各大企业的青睐与支持。这些企业通过与亚马逊合作&#xff0c;共同拓展市场、提升品牌影响力&#xff0c;实现了互利共赢的局面。武汉星起航将浅析各大企业对亚马逊电商平台的支持&#xff0c;展现其共筑强…

Linux之时间子系统(四): tick 层模块(broadcast )

一、前言 在内核中&#xff0c;有cpuidle framework可以控制cpu的节电&#xff1a;当没有进程调度到该cpu上执行的时候&#xff0c;swapper进程粉墨登场&#xff0c;将该cpu会被推入到idle状态。当然CPU的idle状态有深有浅&#xff0c;当CPU睡的比较深入的时候&#xff0c;有可…