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

一、链表概述

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


二、单向链表

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.双向链表的创建,插入,删除,寻找,替换,销毁。

相关推荐
暮冬-  Gentle°4 小时前
C++中的命令模式实战
开发语言·c++·算法
卷福同学6 小时前
【养虾日记】Openclaw操作浏览器自动化发文
人工智能·后端·算法
春日见6 小时前
如何入门端到端自动驾驶?
linux·人工智能·算法·机器学习·自动驾驶
图图的点云库7 小时前
高斯滤波实现算法
c++·算法·最小二乘法
一叶落4387 小时前
题目:15. 三数之和
c语言·数据结构·算法·leetcode
y = xⁿ8 小时前
【LeetCodehot100】2:两数相加 19 删除链表倒数第n个节点
数据结构·链表
老鱼说AI8 小时前
CUDA架构与高性能程序设计:异构数据并行计算
开发语言·c++·人工智能·算法·架构·cuda
罗湖老棍子9 小时前
【例 1】数列操作(信息学奥赛一本通- P1535)
数据结构·算法·树状数组·单点修改 区间查询
big_rabbit05029 小时前
[算法][力扣222]完全二叉树的节点个数
数据结构·算法·leetcode
张李浩10 小时前
Leetcode 15三题之和
算法·leetcode·职场和发展