【数据结构基础_有[*pHead]和[*pEnd]的单向链表_(C语言实现)】

单向链表(含头尾指针)

本文主要是针对(含头尾指针)单向链表进行增、删、改、查操作

创建一个结构体当做节点
struct Node
{
	int iData;
	struct Node* pNext;
};

定义头尾指针

struct Node* pHead = NULL;
struct Node* pEnd = NULL;	

pEnd指针是为了增加尾添加的效率,否则就要循环找尾

增加:1.尾添加

无(*pEnd)指针的尾添加

void AddToEndNopEnd(struct Node** pHead, struct Node** pEnd, int iData)
{
	//创建节点
	struct Node* pTemp = (struct Node*)malloc(sizeof(struct Node));
	if (NULL != pTemp)
	{
		//节点成员赋值
		pTemp->iData = iData;
		pTemp->pNext = NULL;
		//链接
		if (NULL == *pHead)
		{
			*pHead = pTemp;
		}
		else
		{
			struct Node* pt = *pHead;
			while (pt->pNext != NULL)		//找尾巴
			{
				pt = pt->pNext;
			}
			pt->pNext = pTemp;
		}
	}
}

有(*pEnd)指针的尾添加

void AddToEnd(struct Node** pHead, struct Node** pEnd, int iData)
{
	//创建节点
	struct Node* pTemp = (struct Node*)malloc(sizeof(struct Node));
	if (NULL != pTemp)
	{
		//节点成员赋值
		pTemp->iData = iData;
		pTemp->pNext = NULL;
		//连接链表
		if (NULL == *pHead)//空链表
		{
			*pHead = pTemp;
			//*pEnd = pTemp;
		}
		else
		{
			(*pEnd)->pNext = pTemp;
			//*pEnd = pTemp;
		}
		*pEnd = pTemp;
	}
}
增加:2.头添加
void AddToHead(struct Node** pHead, struct Node** pEnd, int iData)
{
	//创建节点
	struct Node* pTemp = (struct Node*)malloc(sizeof(struct Node));
	if (NULL != pTemp)
	{
		//节点成员赋值
		pTemp->iData = iData;
		pTemp->pNext = NULL;
		//连接链表
		if (NULL == *pHead)//空链表
		{
			//*pHead = pTemp;
			*pEnd = pTemp;
		}
		else
		{
			pTemp->pNext = *pHead;
			//*pHead = pTemp;
		}
		*pHead = pTemp;
	}
}

此时咱们链表的最简单的两个添加,已经完成,我们可以遍历链表来查看,并且释放链表

遍历链表
void Look(struct Node* pHead)
{
	while (pHead != NULL)
	{
		printf("%d ", pHead->iData);
		pHead = pHead->pNext;
	}
	putchar('\n');
}
释放链表
void FreeList(struct Node** pHead, struct Node** pEnd)
{
	struct Node* pTemp = *pHead;
	while (pTemp != NULL)
	{
		struct Node* pt = pTemp;
		pTemp = pTemp->pNext;
		free(pt);
	}
	*pHead = NULL;
	*pEnd = NULL;
}

释放链表记得指针要置空哈

查找:1.根据下标查询
struct Node* FindNodeByIndex(struct Node* pHead, int iIndex)
{
	//参数合法性检测
	if (NULL == pHead || iIndex < 0)
	{
		printf("链表为空/位置输入有误,无法查节点\n");
		return NULL;
	}
	//循环链表
	int i = 0;
	while (pHead != NULL)
	{
		if (i == iIndex)
			return pHead;
		i++;
		pHead = pHead->pNext;
	}
	printf("位置输入过大,无法查节点\n");
	return NULL;
}
查找:2.根据数值查询
struct Node* FindNodeByData(struct Node* pHead, int iData)
{
	//参数合法性检测
	if (NULL == pHead || iData < 0)
	{
		printf("链表为空/位置输入有误,无法查节点\n");
		return NULL;
	}
	//循环链表
	while (pHead != NULL)
	{
		if (pHead->iData == iData)
			return pHead;
		pHead = pHead->pNext;
	}
	return NULL;
}
升级版添加:1.在指定下标节点处添加节点
void InsertNodeByIndex(struct Node** pHead, struct Node** pEnd, int iIndex, int iData)
{
	//参数合法性检测
	if (iIndex < 0)
	{
		printf("下标输入有误,请重新输入\n");
		return;
	}
	//下标为0,直接头添加
	if (0 == iIndex)
	{
		AddToHead(pHead, pEnd, iData);
	}
	else
	{
		//找位置
		struct Node* pTemp = FindNodeByIndex(*pHead, iIndex - 1);
		if (pTemp != NULL)
		{
			//申请节点空间
			struct Node* pT = (struct Node*)malloc(sizeof(struct Node));
			if (pT != NULL)
			{
				//节点成员赋值
				pT->iData = iData;
				pT->pNext = NULL;
				//链接 先连后断
				pT->pNext = pTemp->pNext;
				pTemp->pNext = pT;
			}
		}
		else
		{
			printf("查无此节点");
		}
	}
}
升级版添加:2.在指定数值添加多个节点

看着很高大上,其实你只需要调用头添加即可,此时的头非彼头;

void AddSomeNode(struct Node** pHead, struct Node** pEnd, unsigned int iCount, unsigned int iData)
{
	for (unsigned int i = 0; i < iCount; i++)
	{
		AddToEnd(pHead, pEnd, iData);
	}
}
修改:1.根据数值改变此节点数据
void ChengeData(struct Node* pHead, int iResData, int iDecData)
{
	struct Node* pRec = FindNodeByData(pHead, iResData);
	if (NULL == pRec)
	{
		printf("查无此节点\n");
	}
	else
	{
		pRec->iData = iDecData;
	}
}
修改:2.根据数值改变此节点所有相同数据(一)
void ChengeSomeData(struct Node* pHead, int iResData, int iDecData)
{
	//参数合法性检测
	if (NULL == pHead)
	{
		printf("查无此节点\n");
		return;
	}
	while (pHead != NULL)
	{
		if (pHead->iData == iResData)
			pHead->iData = iDecData;
		pHead = pHead->pNext;
	}
}
修改:2.根据数值改变此节点所有相同数据(一)
void ChengeSomeDataText(struct Node* pHead, int iResData, int iDecData)
{
	//参数合法性检测
	if (NULL == pHead)
	{
		printf("查无此节点\n");
		return;
	}
	while (FindNodeByData(pHead, iResData) != NULL)
	{
		ChengeData(pHead, iResData, iDecData);
	}
}
删除:1.删除头结点
void DeleteHead(struct Node** pHead, struct Node** pEnd)
{
	//参数合法性检测
	if (NULL == pHead)
		return;
	if (*pHead == *pEnd || (*pHead)->pNext == NULL)//只有一个节点
	{
		free(*pHead);
		*pHead = NULL;
		*pEnd = NULL;
	}
	else
	{
		struct Node* pT = *pHead;
		(*pHead) = (*pHead)->pNext;
		free(pT);
	}
}
删除:2.删除尾结点
void DeleteEnd(struct Node** pHead, struct Node** pEnd)
{
	//参数合法性检测
	if (NULL == pHead)
		return;
	if (*pHead == *pEnd || (*pHead)->pNext == NULL)//只有一个节点
	{
		free(*pHead);
		*pHead = NULL;
		*pEnd = NULL;
	}
	else
	{
		//找到结尾的前一个
		struct Node* pT = *pHead;
		while (pT->pNext != *pEnd)
		{
			pT = pT->pNext;
		}
		free(*pEnd);
		*pEnd = pT;
		(*pEnd)->pNext = NULL;
	}
}
删除:3.根据数值删除此节点(只删除一个哟)
struct Node* DeleteOneDataByData(struct Node** pHead, struct Node** pEnd, int iData)
{
	if (NULL == *pHead)
		return NULL;
	if ((*pHead)->iData == iData)
	{
		DeleteHead(pHead, pEnd);
		return *pHead;
	}
	else if ((*pEnd)->iData == iData)
	{
		DeleteEnd(pHead, pEnd);
		return *pHead;
	}
	else
	{
		//循环找节点
		struct Node* pTemp = *pHead;
		while (pTemp->pNext != NULL)
		{
			if (pTemp->pNext->iData == iData)
				break;
			pTemp = pTemp->pNext;
		}
		//判断
		if (pTemp->pNext != NULL)
		{
			struct Node* pT = pTemp->pNext; //记录
			pTemp->pNext = pT->pNext;
			free(pT);
			return *pHead;   //return pTemp->pNext  ----- pHead
		}
		else
		{
			printf("查无此节点\n");
			return NULL;
		}
	}
}

提醒一点哈,这里考虑到删除的节点可能是头结点,所有采用了返回值的方式

删除:4.根据数值删除此所有相同数据的节点
void DeleteAllDataByData(struct Node** pHead, struct Node** pEnd, int iData)
{
	if (NULL == *pHead)
		return;
	struct Node* pTemp = DeleteOneDataByData(pHead, pEnd, iData);
	while (pTemp != NULL)
	{
		pTemp = DeleteOneDataByData(pHead, pEnd, iData);
	}
}
删除:5.根据下标删除此节点
void DeleteDataByIndex(struct Node** pHead, struct Node** pEnd, int iIndex)
{
	if (NULL == *pHead)
		return;
	if (0 == iIndex)
		DeleteHead(pHead, pEnd);
	else
	{
		//找前一个节点
		struct Node* pTemp = FindNodeByIndex(*pHead, iIndex - 1);
		if (pTemp != NULL)
		{
			if (pTemp->pNext == *pEnd)
				DeleteEnd(pHead, pEnd);
			//记录被删除的节点
			struct Node* pT = pTemp->pNext;
			//链接
			pTemp->pNext = pT->pNext;
			//释放
			free(pT);
		}
		else
		{
			printf("查无此节点\n");
		}
	}
}

下面是一些其他简单操作方便大家更加熟悉链表

(1)获取节点个数
int GetListNodeCount(struct Node* pHead)
{
	int iCount = 0;
	while (NULL != pHead)
	{
		iCount++;
		pHead = pHead->pNext;
	}
	return iCount;
}
(2)根据数值交换两个节点的数据
void SwapByData(struct Node* pHead, int iData1, int iData2)
{
	if (NULL == pHead)
		return;
	//查找
	struct Node* pT1 = FindNodeByData(pHead, iData1);
	struct Node* pT2 = FindNodeByData(pHead, iData2);
	//都找到才能交换
	if (pT1 != NULL && pT2 != NULL)
	{
		//交换成员
		//struct Node p = *pT1;
		//*pT1 = *pT2;
		//*pT2 = p;
		//int temp = pT1->iData;
		//pT1->iData = pT2->iData;
		//pT2->iData = temp;
		struct Node p = *pT1;
		memcpy(pT1, pT2, 4);
		memcpy(pT2, &p, 4);
	}
}
(3)根据下标交换两个节点的数据
void SwapByIndex(struct Node* pHead, int iIndex1, int iIndex2)
{
	if (NULL == pHead)
		return;
	//查找
	struct Node* pT1 = FindNodeByIndex(pHead, iIndex1);
	struct Node* pT2 = FindNodeByIndex(pHead, iIndex2);
	//都找到才能交换
	if (pT1 != NULL && pT2 != NULL)
	{
		//交换成员
		//int temp = pT1->iData;
		//pT1->iData = pT2->iData;
		//pT2->iData = temp;
		struct Node p = *pT1;
		memcpy(pT1, pT2, 4);
		memcpy(pT2, &p, 4);
	}
}
(4)交换指针指向节点
void SwapByChangePoint(struct Node** pHead, struct Node** pEnd, int iIndex1, int iIndex2)
{
	//参数合法性检测
	if (NULL == *pHead || NULL == (*pHead)->pNext || iIndex1 == iIndex2 || iIndex1 < 0 || iIndex2 < 0)
	{
		return;
	}
	//确定大小关系
	int iMin = iIndex1;
	int iMax = iIndex2;
	if (iIndex1 > iIndex2)
	{
		iMin = iIndex2;
		iMax = iIndex1;
	}
	//根据下标找节点
	struct Node* pMin = FindNodeByIndex(*pHead, iMin);
	struct Node* pMax = FindNodeByIndex(*pHead, iMax);
	//找不到就结束
	if (NULL == pMin || NULL == pMax)
	{
		printf("查无此节点\n");
		return;
	}
	//头尾交换
	if (pMin == *pHead && pMax == *pEnd)
	{
		if ((*pHead)->pNext == *pEnd)  //两个节点
		{
			(*pEnd)->pNext = *pHead;
			(*pHead)->pNext = NULL;
			//交换指向
			*pHead = *pEnd;
			*pEnd = (*pHead)->pNext;
		}
		else //多个节点
		{
			//找到尾巴的前一个节点
			struct Node* pMaxPre = FindNodeByIndex(*pHead, iMax - 1);
			//尾巴变头
			(*pEnd)->pNext = (*pHead)->pNext;
			//头变尾巴
			pMaxPre->pNext = *pHead;
			//头下一个为NULL
			(*pHead)->pNext = NULL;
			//交换指向
			*pHead = *pEnd;
			*pEnd = pMaxPre->pNext;
		}
	}
	//头和中间交换
	else if (pMin == *pHead && pMax != *pEnd)
	{
		//相邻
		if (pMin->pNext == pMax)
		{
			pMin->pNext = pMax->pNext;
			pMax->pNext = pMin;
			*pHead = pMax;
		}
		//不相邻
		else  
		{
			//找pMax的前一个指针
			struct Node* pMaxPre = FindNodeByIndex(*pHead, iMax - 1);
			//记录头的下一个节点 即链表第二个节点
			struct Node* pHeadNext = pMin->pNext;
			//头节点连在max的位置上
			pMin->pNext = pMax->pNext;
			pMaxPre->pNext = pMin;
			//pMax挪到头
			pMax->pNext = pHeadNext;
			//改变新的头
			*pHead = pMax;
		}
	}
	//尾和中间交换
	else if (pMin != *pHead && pMax == *pEnd)
	{
		if (pMin->pNext == pMax)  //相邻
		{
			//找到pMin的前一个
			struct Node* pMinPre = FindNodeByIndex(*pHead, iMin - 1);
			//先把尾巴放中间
			pMinPre->pNext = pMax;
			pMax->pNext = pMin;
			//pMin next = NULL
			pMin->pNext = NULL;
			*pEnd = pMin;
		}
		//不相邻
		else
		{
			//找到pMin的前一个
			struct Node* pMinPre = FindNodeByIndex(*pHead, iMin - 1);
			//找pMax的前一个指针
			struct Node* pMaxPre = FindNodeByIndex(*pHead, iMax - 1);
			//pmax放中间去
			pMinPre->pNext = pMax;
			pMax->pNext = pMin->pNext;
			//pMin放尾巴上去
			pMaxPre->pNext = pMin;
			pMin->pNext = NULL;
			*pEnd = pMin;
		}
	}
	//中间两个相互交换
	else if (pMin != *pHead && pMax != *pEnd)
	{
		//相邻
		if (pMin->pNext == pMax)
		{
			//找到pMin的前一个节点
			struct Node* pMinPre = FindNodeByIndex(*pHead, iMin - 1);
			struct Node* pMaxNext = pMax->pNext;
			//将pMax装在pMin 与pMinPre中间
			pMinPre->pNext = pMax;
			pMax->pNext = pMin;
			//把pMax拿下来
			pMin->pNext = pMaxNext;
		}
		//不相邻
		else
		{
			//找到pMin的前一个
			struct Node* pMinPre = FindNodeByIndex(*pHead, iMin - 1);
			struct Node* pMinNext = pMin->pNext;
			//找pMax的前一个指针
			struct Node* pMaxPre = FindNodeByIndex(*pHead, iMax - 1);
			struct Node* pMaxNext = pMax->pNext;
			//将pMin撞到pMax的位置
			pMaxPre->pNext = pMin;
			pMin->pNext = pMaxNext;
			//将pMax撞到pMin的位置
			pMinPre->pNext = pMax;
			pMax->pNext = pMinNext;
		}
	}
}
(5)根据数据翻转链表
void ReverseByData(struct Node* pHead)
{
	//参数合法性检测
	if (NULL == pHead || NULL == pHead->pNext)
	{
		return;
	}
	int iCount = GetListNodeCount(pHead);
	int i = 0, j = iCount - 1;
	for (; i < j; i++, j--)
	{
		SwapByIndex(pHead, i, j);
	}
}
(6)根据下标翻转链表
void ReverseByPoint(struct Node** pHead, struct Node** pEnd)
{
	//参数合法性检测
	if (NULL == *pHead || NULL == (*pHead)->pNext)
	{
		return;
	}
	int iCount = GetListNodeCount(*pHead);
	int i = 0, j = iCount - 1;
	for (; i < j; i++, j--)
	{
		SwapByChangePoint(pHead, pEnd, i, j);
	}
}

此时单向链表基本操作已经完成咯~

  • 1
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

千北@

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值