数据结构:单向链表和双向链表

一、链表概述

链表是一种线性数据结构,通过节点之间的指针链接实现数据存储,属于链式存储结构。与顺序表(数组)相比,链表在动态内存分配和插入删除操作上具有明显优势,但随机访问效率较低。


二、单向链表

1. 结构特点

  • 每个节点包含数据域和指向下一个节点的指针。

  • 只能从头节点开始顺序访问,不支持反向遍历。

2. 基本操作

  • 创建与销毁:动态分配节点内存,遍历释放。

cpp 复制代码
note_t *creat(void)
{
	note_t *pnew = NULL;
	pnew = malloc(sizeof(note_t));
	if(NULL == pnew)
	{
		perror("malloc fail");
		return NULL;
	}
	pnew -> pnext = NULL;
	return pnew;
}
int insert(note_t *phead,DateType TmpData)
{
	note_t *pnew = NULL;

	pnew = malloc(sizeof(note_t));
	if(NULL == pnew)
	{
		perror("fail to malloc");
		return -1;
	}
	pnew->Date = TmpData;
	pnew->pnext = phead->pnext;
	phead->pnext = pnew;

	return 0;
}
  • 插入:可在头、尾或任意位置插入,时间复杂度为 O(1)O(1)(头插)或 O(n)O(n)(中间插入)。

  • 删除:需定位前驱节点,修改指针指向,释放节点内存。

cpp 复制代码
int delete(note_t *phead,DateType TmpData)
{
	note_t *ptmp = NULL;
	note_t *pter = NULL;
	int cnt = 0;
	pter = phead;
	ptmp = phead->pnext;
	while(ptmp != NULL)
	{
		if(ptmp->Date == TmpData)
		{
			pter->pnext = ptmp->pnext;
			free(ptmp);
			cnt++;
			ptmp = pter->pnext;
		}
		else
		{
			ptmp = ptmp->pnext;
			pter = pter->pnext;
		}
	}


	return cnt;
}
  • 遍历 :从头节点开始,依次访问直到指针为 NULL

3. 进阶问题:环检测与处理

  • 判断是否有环:使用快慢指针(Floyd 判圈算法),若相遇则有环。

  • 求环长:从相遇点出发,指针再次回到该点所走过的节点数即为环长。

  • 找环入口:两指针分别从头节点和相遇点同步前进,再次相遇点即为环入口。

cpp 复制代码
int IsCircleList(Node_t *pHead, int *plen, Node_t **ppTmpNode)
{
	Node_t *pFast = NULL;
	Node_t *pSlow = NULL;
	Node_t *pMeetNode = NULL;
	int len = 0;

	pSlow = pFast = pHead->pNext;
	while (1)
	{
		pFast = pFast->pNext;
		if (NULL == pFast)
		{
			return 0;
		}
		pFast = pFast->pNext;
		if (NULL == pFast)
		{
			return 0;
		}

		pSlow = pSlow->pNext;
		if (pSlow == pFast)
		{
			pMeetNode = pSlow;
			break;
		}
	}
	
	pSlow = pMeetNode->pNext;
	len++;
	while (pSlow != pMeetNode)
	{
		len++;
		pSlow = pSlow->pNext;
	}
	*plen = len;

	pFast = pMeetNode;
	pSlow = pHead->pNext;
	while (pFast != pSlow)
	{
		pFast = pFast->pNext;
		pSlow = pSlow->pNext;
	}

	*ppTmpNode = pSlow;

	return 1;
}

三、双向链表

1. 结构特点

  • 节点除数据域外,包含前驱指针 prev 和后继指针 next

  • 支持双向遍历,可从任意节点向前或向后访问。

2. 操作优势

  • 插入与删除:在已知节点位置时,时间复杂度为 O(1)O(1),因可直接修改前驱和后继指针。

  • 遍历:支持正向与反向遍历,灵活性更高。

3. 实现要点

  • 创建与销毁:需同时维护双向指针关系。

  • 头插与尾插 :注意更新头节点或尾节点的 prevnext

  • 删除操作:需处理被删除节点前后节点的指针指向,避免断链。


四、链式存储与顺序存储对比

特性 顺序存储(数组) 链式存储(链表)
访问效率 O(1)O(1) O(n)O(n)
插入删除效率 O(n)O(n) O(1)O(1)(已知位置)
空间利用 连续空间,可能浪费 动态分配,利用率高
内存开销 仅数据 数据 + 指针

今日练习

1.双向链表的创建,插入,删除,寻找,替换,销毁。

相关推荐
aramae4 小时前
MySQL复合查询(8)
java·c语言·开发语言·后端·算法
荆棘鸟智能4 小时前
城市感知设备怎么统一接入?从多协议网关到设备模型的中间件架构设计
人工智能·算法·边缘计算
无敌贵点大王4 小时前
RTThread学习记录11——RT-Thread 设备模型吃透:UART/ADC/PWM/PIN 到底有什么区别?
c语言·stm32·学习·链表
淡海水5 小时前
13-04-面试-源码级深度追问链
数据结构·unity·面试·c#·游戏引擎·源码·il2cpp
Logic1015 小时前
C语言/数据结构位运算题解:异或XOR找出时尚聚会中的“独特颜色“——只出现一次的数字
c语言·数据结构·数组·位运算·时间复杂度·算法题·异或性质
INGNIGHT8 小时前
270 · 电话号码的字母组合II(Trie)
linux·算法
2401_839080548 小时前
C++常见八股
数据结构·c++·算法
青山是哪个青山9 小时前
LeetCode 188:买卖股票的最佳时机 IV
算法
暮雨封夕9 小时前
跳表(Skip List)详解:原理、实现、使用场景与实际应用
数据结构
mikuyyds9 小时前
geo-toolbox 插件算法解析:RUSLE 与 MUSLE 的工程化落地
算法·rust·gis