数据结构:链表常见的操作方法!!

之前我们讲了单向有头链表的创建、插入(头插和尾插)、遍历、删除,除了这些操作方法,还有修改、查找(单个节点、中间节点、倒数第k个节点)、倒置、排序,此外也会讲到链表是否有环的判断方法。

cpp 复制代码
typedef int DataType;

typedef struct node 
{
	DataType Data;
	struct node *pNext;
}Node_t

一、修改

先定义一个指针指向链表第一个有效节点;

遍历查找要修改的节点,将旧的数据替换为新的数据。

关键代码示例:

cpp 复制代码
    Node_t *pTmpNode = NULL;
	int Cnt = 0;

	pTmpNode = pHead->pNext;
	while (pTmpNode != NULL)
	{
		if (pTmpNode->Data == OldData)
		{
			pTmpNode->Data = NewData;
			Cnt++;
		}
		pTmpNode = pTmpNode->pNext;
	}

二、查找单个节点的地址

定义一个指针,指向链表第一个有效节点;

遍历,当指针指向的节点的值为我们要查找的值,返回该节点的地址。

关键代码示例:

cpp 复制代码
    Node_t *pTmpNode = NULL;
	
	pTmpNode = pHead->pNext;
	while (pTmpNode != NULL)
	{
		if (pTmpNode->Data == TmpData)
		{
			return pTmpNode;
		}
		pTmpNode = pTmpNode->pNext;
	}

	return NULL;

三、查找中间节点的地址

定义两个指针,一个快指针,一个慢指针,都从链表第一个有效节点开始;

遍历,让快指针走两步(先让快指针走一步后判断,若快指针指向了空,则直接返回慢指针的地址,若不为空则让快指针再走一步),慢指针走一步,当快指针走到NULL时,慢指针的地址即为中间节点的地址。

关键代码示例:

cpp 复制代码
    Node_t *pFast = NULL;
	Node_t *pSlow = NULL;

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

四、查找倒数第k个节点地址

定义两个指针,一个快指针,一个慢指针,都从头指针开始;

先让快指针走K步(当快指针不指向NULL时,for循环走k步,循环次数小于k步),若快指针指向了空,则直接返回空(说明没有倒数第k个节点),遍历让快指针和慢指针同时走,当快指针指向空时,慢指针的地址即为中间节点的地址。

关键代码示例:

cpp 复制代码
    Node_t *pFast = NULL;
	Node_t *pSlow = NULL;
	int i = 0;

	pFast = pSlow = pHead;
	for (i = 0; pFast != NULL && i < Nth; i++)
	{
		pFast = pFast->pNext;
	}

	if (NULL == pFast)
	{
		return NULL;
	}

	while (pFast != NULL)
	{
		pFast = pFast->pNext;
		pSlow = pSlow->pNext;
	}
	
	return pSlow;

五、销毁

定义两个指针ptmp和pfree,都先指向头指针;

遍历让ptmp指向下一个,释放pfree的空间,再让pfree赋值为ptmp

注意最后要将释放后的指针置空,避免野指针,即传入二级指针,将**pHead置空。

关键代码示例:

cpp 复制代码
Node_t *pTmpNode = NULL;
	Node_t *pFreeNode = NULL;

	pTmpNode = *ppHead;
	pFreeNode = *ppHead;
	while (pTmpNode != NULL)
	{
		pTmpNode = pTmpNode->pNext;
		free(pFreeNode);
		pFreeNode = pTmpNode;
	}
	*ppHead = NULL;

	return 0;

六、倒置

定义两个指针ptmp和pinsert,都指向第一个有效节点;

将头指针的pNext置空,也就是将头节点与第一个有效节点断开;

遍历让ptmp向后走,用头插法插入pinsert。

关键代码示例:

cpp 复制代码
    Node_t *pTmpNode = NULL;
	Node_t *pInsertNode = NULL;

    pTmpNode = pInsertNode = pHead->pNext;
    pHead->pNext = NULL;

    while(pTmpNode != NULL)
    {
        pTmpNode = pTmpNode->pNext;
        pInsertNode->pNext = pHead->pNext;
        pHead->pNext = pInsertNode;
        pInsertNode = pTmpNode;
    }

        return 0;

七、排序

1.冒泡排序

    1. 边界检查
    • 先判断链表是否为空,或只有一个有效节点。

    • 如果是,直接返回,无需排序。

    1. 初始化指针
    • pTmpNode1pTmpNode2:用于遍历并比较相邻的两个节点。

    • pEnd:标记已排序区间的尾部,初始为 NULL,表示还没有任何节点完成排序。

    • TmpData:临时变量,用于交换节点的值。

    1. 外层循环:控制排序轮次
    • 使用 while(1) 作为无限循环,通过内部条件判断来终止。

    • 每一轮结束后,最大的未排序值会被 "冒泡" 到当前未排序区间的末尾,因此 pEnd 会向前移动。

    1. 内层循环:相邻节点比较与交换
    • 每轮开始时,重置 pTmpNode1pTmpNode2 到链表的起始有效节点。

    • 只要 pTmpNode2 不等于 pEnd(即还没遍历到已排序区间),就持续比较:

      • 如果 pTmpNode1->Data > pTmpNode2->Data,则交换两者的值。

      • 然后将 pTmpNode1pTmpNode2 同时向后移动一个节点。

    1. 推进已排序区间
    • 当一轮内层循环结束后,当前未排序区间的最大值已经被移动到末尾。

    • pEnd 更新为 pTmpNode1,表示该节点及其之后的部分已经有序。

    1. 终止条件
    • pEnd 等于 pTmpNode2 时,说明未排序区间只剩一个节点,链表已经完全有序,退出外层循环。
    1. 结束返回
    • 排序完成后,函数返回 0。

关键代码示例:

cpp 复制代码
Node_t *pTmpNode1 = NULL;
	Node_t *pTmpNode2 = NULL;
	Node_t *pEnd = NULL;
	DataType TmpData;

	if (NULL == pHead->pNext || NULL == pHead->pNext->pNext)
	{
		return 0;
	}

	while (1)
	{
		pTmpNode1 = pHead->pNext;
		pTmpNode2 = pHead->pNext->pNext;
		
		if (pEnd == pTmpNode2)
		{
			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;

2.选择排序

    1. 边界检查
    • 先判断链表是否为空,或只有一个有效节点。
    • 如果是,直接返回,无需排序。
    1. 初始化排序指针
    • 定义 pSwapNode,指向当前待排序区间的起始节点,初始化为链表第一个有效节点。
    • 定义 pMinNode 用来记录当前区间的最小值节点。
    • 定义 pTmpNode 用来遍历当前区间寻找最小值。
    1. 外层循环:确定待排序区间
    • pSwapNode 开始,直到链表末尾(pSwapNode->pNext != NULL)。
    • 每次循环确定一个位置的最终值。
    1. 内层循环:寻找最小值节点
    • pMinNode 初始化为当前 pSwapNode
    • pTmpNodepSwapNode->pNext 开始遍历剩余节点。
    • 比较每个节点的数据值,更新 pMinNode 指向值更小的节点。
    1. 交换节点值
    • 如果找到的最小值节点 pMinNode 不是当前的 pSwapNode
    • 交换两者的 Data 字段,把最小值放到当前 pSwapNode 的位置。
    1. 推进排序区间
    • pSwapNode 移动到下一个节点,进入下一轮排序。
    • 重复步骤 3--5,直到整个链表有序。
    1. 结束返回
    • 排序完成后,函数返回 0。

关键代码示例:

cpp 复制代码
Node_t *pSwapNode = NULL;
	Node_t *pTmpNode = NULL;
	Node_t *pMinNode = NULL;
	DataType TmpData;

	if (NULL == pHead->pNext || NULL == pHead->pNext->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 (pSwapNode != pMinNode)
		{
			TmpData = pSwapNode->Data;
			pSwapNode->Data = pMinNode->Data;
			pMinNode->Data = TmpData;
		}
		pSwapNode = pSwapNode->pNext;
	}

	return 0;

八、判断链表是否有环(了解)

相关推荐
舟舟亢亢2 小时前
算法总结——二叉树【hot100】(上)
java·开发语言·算法
weixin_477271693 小时前
根象:树根。基石。基于马王堆帛书《周易》原文及甲骨文还原周朝生活活动现象(《函谷门》原创)
算法·图搜索算法
普通网友3 小时前
多协议网络库设计
开发语言·c++·算法
努力努力再努力wz3 小时前
【Linux网络系列】:TCP 的秩序与策略:揭秘传输层如何从不可靠的网络中构建绝对可靠的通信信道
java·linux·开发语言·数据结构·c++·python·算法
daxi1503 小时前
C语言从入门到进阶——第9讲:函数递归
c语言·开发语言·c++·算法·蓝桥杯
持续学习的程序员+14 小时前
强化学习Q-chunking算法
算法
Polaris北5 小时前
第二十七天打卡
开发语言·c++·算法
风吹乱了我的头发~5 小时前
Day30:2026年2月20日打卡
算法
blackicexs5 小时前
第五周第五天
算法
不吃橘子的橘猫6 小时前
《集成电路设计》复习资料2(设计基础与方法)
学习·算法·fpga开发·集成电路·仿真·半导体