数据结构——单向链表

ops/2024/12/22 20:14:20/

链表

1.空间可以不连续(理论上长度是无限的)

2.访问元素不方便

3.链表需要更大的空间存放数据和节点地址

4.链表的插入和删除效率很高O(1)

单向链表

无头单向链表,节点插入在头的话,每次头结点都会变,所以有了有头链表,头结点的pNext总是指向链表的第一个节点

1.创建空链表
//创建空链表
LinkNode *CreateLinkList(void)
{   LinkNode *pTmpNode = NULL;pTmpNode = malloc(sizeof(LinkNode));if (NULL == pTmpNode){return NULL;}pTmpNode->Data = 0;pTmpNode->pNext = NULL;return pTmpNode;
}
2.头插法
int HeadInsertLinkList(LinkNode *pHead, DataType TmpData)
{LinkNode *pTmpNode = NULL;//1.申请空间pTmpNode = malloc(sizeof(LinkNode));if (NULL == pTmpNode){return -1;}//2.将Data成员初始化pTmpNode->Data = TmpData;//3.将pNext成员初始化(和后面连起来)pTmpNode->pNext = pHead->pNext;//4.将头结点的pNext指向新申请节点(和前面连起来)pHead->pNext = pTmpNode;return 0;
}
3.遍历
int ShowLinkList(LinkNode *pHead)
{LinkNode *pTmpNode = NULL;pTmpNode = pHead->pNext;while (pTmpNode != NULL){printf("%d ", pTmpNode->Data);pTmpNode = pTmpNode->pNext;}printf("\n");return 0;
}
4.修改
int UpdateLinkList(LinkNode *pHead, DataType OldData, DataType NewData)
{LinkNode *pTmpNode = NULL;int cnt = 0;pTmpNode = pHead->pNext;while (pTmpNode != NULL){if (pTmpNode->Data == OldData){pTmpNode->Data = NewData;cnt++;}pTmpNode = pTmpNode->pNext;}return cnt;
}
5.查找
LinkNode *FindLinkList(LinkNode *pHead, DataType TmpData)
{LinkNode *pTmpNode = NULL;pTmpNode = pHead->pNext;while (pTmpNode != NULL){if (pTmpNode->Data == TmpData){return pTmpNode;}pTmpNode = pTmpNode->pNext;}return NULL;
}
6.尾插
int TailInsertLinkList(LinkNode *pHead, DataType TmpData)
{LinkNode *pNewNode = NULL;LinkNode *pTmpNode = NULL;pNewNode = malloc(sizeof(LinkNode));if (NULL == pNewNode){return -1;}pNewNode->Data = TmpData;pNewNode->pNext = NULL;pTmpNode = pHead;while (pTmpNode->pNext != NULL){pTmpNode = pTmpNode->pNext;}pTmpNode->pNext = pNewNode;return 0;
}
7.删除链表节点

删除链表节点,必须要知道要删节点的前一个结点,所以要定义两个指针来操作

int DeleteLinkList(LinkNode *pHead, DataType TmpData)
{LinkNode *pPreNode = NULL;LinkNode *pTmpNode = NULL;int cnt = 0;pPreNode = pHead;pTmpNode = pHead->pNext;while (pTmpNode != NULL){if (pTmpNode->Data == TmpData){//删除pPreNode->pNext = pTmpNode->pNext;free(pTmpNode);pTmpNode = pPreNode->pNext;cnt++;}else {pTmpNode = pTmpNode->pNext;pPreNode = pPreNode->pNext;}}return cnt;
}

注意:free掉pTmpNode后,它变成了野指针,要为它重新赋值

8.销毁
int DestroyLinkList(LinkNode **ppHead)
{LinkNode *pTmpNode = NULL;LinkNode *pFreeNode = NULL;pTmpNode = pFreeNode = *ppHead;while (pTmpNode != NULL){pTmpNode = pTmpNode->pNext;free(pFreeNode);pFreeNode = pTmpNode;}*ppHead = NULL;return 0;
}
复杂操作
 1.查找链表中间节点

算法:快指针pFast每次走2步,慢指针pSlow每次走1步,快指针到达末尾时,慢指针所在的位置即为中间位置

LinkNode *FindMidLinkNode(LinkNode *pHead)
{LinkNode *pFast = NULL;LinkNode *pSlow = NULL;pFast = pSlow = pHead->pNext;while (pFast != NULL){pFast = pFast->pNext;if (NULL == pFast){break;}pFast = pFast->pNext;if (NULL == pFast){break;}pSlow = pSlow->pNext;}return pSlow;
}

 2.查找链表倒数第k个节点

算法:快指针先走k步,慢指针和快指针每次走1步(快指针总是领先慢指针k步),当快指针走到末尾时,慢指针即指向链表倒数第k个节点

LinkNode *FindLastKthLinkNode(LinkNode *pHead, int k)
{LinkNode *pFast = pHead->pNext;LinkNode *pSlow = pHead->pNext;int i = 0;for (i = 0; i < k; i++){pFast = pFast->pNext;if (NULL == pFast){break;}}if (NULL == pFast){return NULL;}while (pFast != NULL){pSlow = pSlow->pNext;pFast = pFast->pNext;}return pSlow;
}
 3.链表的倒置(反转)

算法:例如:将第头节点与第一个节点断开连接,在断开前都要保存一下头节点的下一个节点,然后向头插法一样将第一个节点插入到链表中,依次类推

int ReversalLinkList(LinkNode *pHead)
{LinkNode *pTmpNode = NULL;LinkNode *pInsertNode = NULL;pTmpNode = pHead->pNext;pHead->pNext = NULL;pInsertNode = pTmpNode;while (pTmpNode != NULL){pTmpNode = pTmpNode->pNext;pInsertNode->pNext = pHead->pNext;pHead->pNext = pInsertNode;pInsertNode = pTmpNode;}return 0;
}

4.链表的排序(冒泡排序、选择排序)

冒泡排序

每轮比较之后pTmpNode1,都是下一轮的pEnd,直到pEnd = pHead->pNext->pNext;

//链表排序(冒泡排序法)
int BubbleSortLinkList(LinkNode *pHead)
{   LinkNode *pTmpNode1 = NULL;LinkNode *pTmpNode2 = NULL;LinkNode *pEnd = NULL;DataType TmpData;//如果链表没有节点或者只有1个节点返回0 if (NULL == pHead->pNext || NULL == pHead->pNext->pNext){return 0;}while (1){pTmpNode1 = pHead->pNext;pTmpNode2 = pHead->pNext->pNext;if (pTmpNode2 == pEnd){break;}while (pTmpNode2 != pEnd){if (pTmpNode1->Data > pTmpNode2->Data){TmpData = pTmpNode1->Data;pTmpNode1->Data = pTmpNode2->Data;pTmpNode2->Data = TmpData;}pTmpNode1 = pTmpNode1->pNext;pTmpNode2 = pTmpNode2->pNext;}pEnd = pTmpNode1;}return 0;
}

选择排序:

每次选出最小的,第二小的...最大的,


//选择排序
int SelectSortLinkList(LinkNode *pHead)
{LinkNode *pTmpNode = NULL;LinkNode *pMinNode = NULL;LinkNode *pSwapNode = NULL;DataType TmpData;if (NULL == pHead->pNext){return 0;}pSwapNode = pHead->pNext;while (pSwapNode->pNext != NULL){pMinNode = pSwapNode;pTmpNode = pSwapNode->pNext;while (pTmpNode != NULL){if (pTmpNode->Data < pMinNode->Data){pMinNode = pTmpNode;}pTmpNode = pTmpNode->pNext;}if (pMinNode != pSwapNode){TmpData = pMinNode->Data;pMinNode->Data = pSwapNode->Data;pSwapNode->Data = TmpData;}pSwapNode = pSwapNode->pNext;} return 0;
}

 5.已知链表中间某个节点地址,不知道头结点地址,如何删除该节点

算法:可以上中间这个的值变成下一个节点的值,并把它的pNext指向它的pNext->pNext,然后free掉空间


 6.如何判断一个链表是否有环?环长?环的入口位置?
        

是否有环:快指针每次走2步,慢指针每次走1步,快慢指针相遇则说明有环

因为快指针第一次比慢指针多1,第二次快指针比慢指针快2,依次类推,环长一定是一个自然数,所以一定能相遇

 如何计算环长:标记相遇的位置,让指针继续向后走,每走一步计算器自加,走回到标记位置,则计算器值即为环长
 如何计算环入口位置:将一个指针从第一个节点向后走,将一个指针从相遇点向后走,两个指针相遇的位置即为环入口的位置

//判断链表是否有环
//实现方式:
//     快指针每次走2步,慢指针每次走1步
//     两者能够相遇即为有环
LinkNode *IsHasCircle(LinkNode *pHead, int *pcnt)
{LinkNode *pFast = NULL;LinkNode *pSlow = NULL;LinkNode *pTmpNode = NULL;LinkNode *pNode1 = NULL;LinkNode *pNode2 = NULL;int ret = 0;int cnt = 1;pSlow = pFast = pHead->pNext;while (1){pFast = pFast->pNext;if (NULL == pFast){ret = 0;break;}pFast = pFast->pNext;if (NULL == pFast){ret = 0;break;}pSlow = pSlow->pNext;if (pSlow == pFast){ret = 1;break;}}if (1 == ret){//获得环长pTmpNode = pSlow->pNext;while (pTmpNode != pSlow){cnt++;pTmpNode = pTmpNode->pNext;}*pcnt = cnt;//获得环入口位置pNode1 = pSlow;pNode2 = pHead->pNext;while (1){pNode1 = pNode1->pNext;pNode2 = pNode2->pNext;if (pNode1 == pNode2){return pNode1;}}}return NULL;
}


http://www.ppmy.cn/ops/104207.html

相关文章

栈和队列的学习以及实现+双端队列的底层原理

在本次的博客当中我们来进行讲解栈和队列相关的内容。首先要认识一下栈和队列的使用方法。 栈和队列的使用 我们可以看到相比于我们之前使用的list&#xff0c;vector还有string类来说栈和队列就简单了很多&#xff0c;没有太多复杂的接口。 因为对于栈和队列来说输入和输出都有…

Python爬虫所需的技术及其原理(简单易懂)

导言 随着互联网的发展&#xff0c;大量的数据被存储在网络上&#xff0c;而我们需要从中获取有用的信息。Python作为一种功能强大且易于学习的编程语言&#xff0c;被广泛用于网络爬虫的开发。本文将详细介绍Python爬虫所需的技术及其原理&#xff0c;并提供相关的代码案例。…

django之ForeignKey、OneToOneField 和 ManyToManyField

在Django中&#xff0c;ForeignKey、OneToOneField 和 ManyToManyField 是用于定义模型之间关系的字段类型。 ForeignKey ForeignKey 用于定义多对一的关系。例如&#xff0c;一个Employee可以属于一个Department&#xff0c;一个Department可以有多个Employee。 from djang…

【ragflow】安装2:源码安装依赖

中文文档【ragflow】安装1: docker:失败官方说的成功 docker 安装的启动失败 重新来一遍,不会重新拉取: root@k8s-master-pfsrv:/home/zhangbin/perfwork/rag# cd ragflow/ root@k8s-master-pfsrv:/home/

揭开容器的面纱:容器技术全景概述

随着云计算的快速发展&#xff0c;容器技术已经成为IT行业的重要组成部分。Docker作为一种领先的容器化技术&#xff0c;为应用程序的开发、部署和运行带来了革命性的变化。本篇文章将详细介绍容器技术的概念、发展历程及其在现代计算中的应用。通过对Docker的深入了解&#xf…

docker实战基础一 (Docker基础命令)

一、docker安装 # step 1: 安装必要的一些系统工具 yum install -y yum-utils device-mapper-persistent-data lvm2 # Step 2: 添加软件源信息 yum-config-manager --add-repo http://mirrors.aliyun.com/docker-ce/linux/centos/docker-ce.repo # Step 3: 更新并安装 Doc…

深度学习项目实践——QQ聊天机器人(transformer)(三)功能实现的方法——NoneBot2插件结构与编写

深度学习项目实践——QQ聊天机器人&#xff08;transformer&#xff09;&#xff08;三&#xff09;功能实现的方法——NoneBot2插件结构与编写 在前两节中&#xff0c;我们详细讲解了QQ聊天的原理、QQ机器人的框架与环境配置的流程。本节将重点介绍NoneBot2的插件构成&#x…

完美解决node-sass@4.14.1 postinstall: `node scripts/build.js` 问题

node v14.16.0 安装node-sass4.14.1会出现报错 看日志排查发现设置的源国内的都有问题 直接梯子下载&#xff1a; https://github.com/sass/node-sass/releases/download/v4.14.1/win32-x64-83_binding.node 本地启动phpstudy&#xff0c;当然你也可以放在你服务器上&#xff0…