单向链表(含头尾指针)
本文主要是针对(含头尾指针)单向链表进行增、删、改、查操作
创建一个结构体当做节点
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);
}
}
此时单向链表基本操作已经完成咯~