双向链表(含头尾指针)
双向链表呢,顾名思义,就是比单向多一个向前指向节点的一个指针
本文主要是针对(含头尾指针)双向链表进行增、删、改、查操作
创建一个结构体当做节点
struct Node
{
int iData;
struct Node* pNext; //记录后一个节点地址
struct Node* pPre; //记录前一个节点的地址
};
前面我用的是局部定义头尾指针(每次调用都需要传参),这次咱们使用全局的
定义头尾指针
struct Node* g_pHead = NULL;
struct Node* g_pEnd = NULL;
int g_iNodeCount = 0;
g_iNodeCount是记录节点数的
增加:1.尾添加
//尾添加
void AddToEnd(int iData)
{
//参数合法性检测
if (g_iNodeCount < 0)
return;
//申请节点
struct Node* pTemp = (struct Node*)malloc(sizeof(struct Node));
if (NULL == pTemp)
return;
//节点成员赋值
pTemp->iData = iData;
pTemp->pNext = NULL;
pTemp->pPre = NULL;
//连接链表
if (NULL == g_pHead) //无节点
{
g_pHead = pTemp;
//g_pEnd = pTemp;
}
else
{
g_pEnd->pNext = pTemp;
pTemp->pPre = g_pEnd;
//g_pEnd = pTemp;
}
g_pEnd = pTemp;
//节点数量++
g_iNodeCount++;
}
增加:2.头添加
void AddToHead(int iData)
{
//创建节点
struct Node* pTemp = (struct Node*)malloc(sizeof(struct Node));
if (pTemp == NULL)
return;
//节点赋值
pTemp->iData = iData;
pTemp->pNext = NULL;
pTemp->pPre = NULL;
//连接到链表上
if (NULL == g_pHead)
{
//g_pHead = pTemp;
g_pEnd = pTemp;
}
else
{
pTemp->pNext = g_pHead;
g_pHead->pPre = pTemp;
//g_pHead = pTemp;
}
g_pHead = pTemp;
g_iNodeCount++;
}
此时咱们链表的最简单的两个添加,已经完成,我们可以遍历链表来查看,并且释放链表
遍历链表
由于是双向链表所以既可以正向遍历,也可以反向遍历
void LookZheng()
{
if (NULL == g_pHead)
return;
//循环遍历
printf("共有 %d 个节点: ", g_iNodeCount);
struct Node* pTemp = g_pHead;
while (pTemp != NULL)
{
printf("%d ", pTemp->iData);
pTemp = pTemp->pNext;
}
putchar('\n');
}
void LookFan()
{
if (NULL == g_pEnd)
return;
//循环遍历
printf("共有 %d 个节点: ", g_iNodeCount);
struct Node* pTemp = g_pEnd;
while (pTemp != NULL)
{
printf("%d ", pTemp->iData);
pTemp = pTemp->pPre;
}
putchar('\n');
}
释放链表
void FreeList()
{
//参数合法性检测
if (NULL == g_pHead)
return;
//申请中间变量
struct Node* pTemp = g_pHead;
while (pTemp != NULL)
{
//记录要被释放的节点
struct Node* pT = pTemp;
//指向下一个节点
pTemp = pTemp->pNext;
//释放当前节点
free(pT);
}
g_pHead = NULL;
g_pEnd = NULL;
g_iNodeCount = 0;
}
释放链表记得指针要置空哈
并且计数器置零
查找:1.根据下标查询
struct Node* GetByIndex(int iIndex)
{
//参数合法性检测
if (NULL == g_pHead || iIndex < 0 || iIndex >= g_iNodeCount)
return NULL;
//循环遍历
struct Node* pTemp = g_pHead;
for (int i = 0; i < iIndex; i++)
pTemp = pTemp->pNext;
//返回
return pTemp;
}
查找:2.根据数值查询
struct Node* GetByData(int iData)
{
//参数合法性检测
if (NULL == g_pHead)
return NULL;
//循环遍历
struct Node* pTemp = g_pHead;
while (pTemp != NULL)
{
if (pTemp->iData == iData)
break;//return pTemp;
pTemp = pTemp->pNext;
}
//return NULL;
return pTemp;
}
升级版添加:1.指定的下标位置添加
void InsertNodeByIndex(int iIndex, int iCount, int iData)
{
//参数合法性检测
if (iIndex < 0 || iIndex>g_iNodeCount || iCount <= 0)
return;
//分类判断
if (0 == iIndex) //头添加
{
for (int i = 0; i < iCount; i++)
AddToHead(iData);
}
else if (iIndex == g_iNodeCount) //尾添加
{
for (int i = 0; i < iCount; i++)
AddToEnd(iData);
}
else //中间添加
{
//找位置
struct Node* pTemp = g_pHead;
for (int i = 0; i < iIndex; i++)
pTemp = pTemp->pNext;
//循环
for (int i = 0; i < iCount; i++)
{
//申请节点
struct Node* pNew = (struct Node*)malloc(sizeof(struct Node));
if (pNew == NULL)
return;
//节点赋值
pNew->iData = iData;
pNew->pNext = NULL;
pNew->pPre = NULL;
//链接
//指定位置前一个节点与新节点相连
pTemp->pPre->pNext = pNew;
pNew->pPre = pTemp->pPre;
//指定位置节点与新节点相连
pNew->pNext = pTemp;
pTemp->pPre = pNew;
}
//节点数量++
g_iNodeCount += iCount;
}
}
升级版添加:2.指定数据位置添加节点
void InsertNodeByData(int iValue, int iData)
{
//参数合法性检测
if (NULL == g_pHead)
return;
//找节点
struct Node* pTemp = g_pHead;
while (pTemp != NULL)
{
if (pTemp->iData == iValue)
break;
pTemp = pTemp->pNext;
}
//判断是否找到
if (pTemp != NULL)
{
if (pTemp == g_pHead)
AddToHead(iData);
else
{
//申请节点
struct Node* pNew = (struct Node*)malloc(sizeof(struct Node));
if (NULL == pNew)
return;
//节点成员赋值
pNew->iData = iData;
pNew->pNext = NULL;
pNew->pPre = NULL;
//链接
//指定位置前一个节点与新节点相连
pTemp->pPre->pNext = pNew;
pNew->pPre = pTemp->pPre;
//指定位置节点与新节点相连
pNew->pNext = pTemp;
pTemp->pPre = pNew;
//节点数量++
g_iNodeCount += 1;
}
}
}
修改:1.根据数值改变此节点数据
void ChangeByData(int iData,int iValue)
{
//参数合法性检测
if (NULL == g_pHead)
return;
//循环遍历
struct Node* pTemp = g_pHead;
while (pTemp != NULL)
{
if (pTemp->iData == iData)
pTemp->iData = iValue;
pTemp = pTemp->pNext;
}
}
修改:2.根据下标改变此节点数据
void ChangeByIndex(int iIndex,int iValue)
{
//参数合法性检测
if (NULL == g_pHead || iIndex < 0 || iIndex >= g_iNodeCount)
return;
struct Node* pTemp = GetByIndex(iIndex);
if (pTemp != NULL)
{
pTemp->iData = iValue;
}
}
删除:1.根据下标删除节点
void DeleteByIndex(int iIndex)
{
//查找节点
struct Node* pTemp = GetByIndex(iIndex);
//判断是否找到
if (pTemp != NULL)
{
//找到了删除节点
DeleteNode(pTemp);
}
}
删除:2.删除结点
void DeleteNode(struct Node* pTemp)
{
//是否为头结点
if (pTemp == g_pHead)
{
if (g_pHead == g_pEnd)
{
free(g_pHead);
g_pHead = NULL;
g_pEnd = NULL;
}
else
{
//方法1.
//记录头
//struct Node* pT = g_pHead;
头指向下一个
//g_pHead = g_pHead->pNext;
头前设置为NULL
//g_pHead->pPre = NULL;
释放
//free(pT)
方法2.记录头
头指向下一个
//g_pHead = g_pHead->pNext;
头前设置为NULL
//g_pHead->pPre = NULL;
释放
//free(pTemp);
//方法3.头指向下一个
g_pHead = g_pHead->pNext;
//释放头的前一个节点
free(g_pHead->pPre);
//头前置NULL
g_pHead->pPre = NULL;
}
}
//尾节点
else if (pTemp == g_pEnd)
{
g_pEnd = g_pEnd->pPre;
free(g_pEnd->pNext);
g_pEnd->pNext = NULL;
}
//中间节点
else
{
//让其前一个节点的pNext 指针指向其后一个节点
pTemp->pPre->pNext = pTemp->pNext;
//让其后一个节点的pPre 指针指向其前一个节点
pTemp->pNext->pPre = pTemp->pPre;
//释放此节点
free(pTemp);
}
g_iNodeCount--;
}
删除:3.根据数据删除所有与之相同的节点
void DeleteByData(int iValue)
{
//while (1)
//{
// //找
// struct Node* pTemp = GetByData(iValue);
// if (pTemp == NULL)
// break;
// DeleteNode(pTemp);
//}
struct Node* pTemp = NULL;
while (NULL != (pTemp = GetByData(iValue)))
DeleteNode(pTemp);
}
下面是合并有序双向链表简单操作方便大家更加熟悉双向链表
合并链表
void HeBing(struct Node** pHA, struct Node** pEA, int* pCA, struct Node** pHB, struct Node** pEB, int* pCB)
{
//参数合法性检测
if (NULL == pHA || NULL == *pHA || NULL == pHB || NULL == *pHB)
return;
//循环
while(1)
{
//获取头
struct Node* pTemp = GetHead(pHA, pEA, pCA);
if (pTemp == NULL)
return;
//比头小
if (pTemp->iData <= (*pHB)->iData)
{
//新节点下一个指向头
pTemp->pNext = (*pHB);
// 头前一个指向新节点
(*pHB)->pPre = pTemp;
// 头向前移
(*pHB) = pTemp;
//数量++
(*pCB)++;
}
// 比尾大
else if (pTemp->iData >= (*pEB)->iData)
{
//尾巴的pNext指向新节点
(*pEB)->pNext = pTemp;
//新节点的pre指向尾巴
pTemp->pPre = (*pEB);
//尾后移
(*pEB) = pTemp;
//尾巴的pNext置NULL
(*pEB)->pNext = NULL;
//数量++
(*pCB)++;
}
else // 中间
{
//在链表B中找到合适的位置
struct Node* pT = *pHB;
if (pT == NULL)
return;
while (pT->pNext != NULL)
{
if (pTemp->iData >= pT->iData && pTemp->iData <= pT->pNext->iData)
break;
pT = pT->pNext;
}
//中间添加
if (NULL == pT->pNext)
return;
//新节点的pNext 指向pT的下一个
pTemp->pNext = pT->pNext;
//pT的下一个的pPre指向新节点
pT->pNext->pPre = pTemp;
//pT和新节点相连
pT->pNext = pTemp;
pTemp->pPre = pT;
//数量++
(*pCB)++;
}
}
}
实现合并链表的完整代码
#include<stdio.h>
#include<stdlib.h>
struct Node
{
int iData;
struct Node* pNext; //记录后一个节点地址
struct Node* pPre; //记录前一个节点的地址
};
//尾添加
void AddToEnd(struct Node** pHead, struct Node** pEnd, int* pCount, int iData);
//释放链表
void FreeList(struct Node** pHead, struct Node** pEnd, int* pCount);
//遍历链表 1.正向 2.反向
void LookZheng(struct Node* pHead, int* pCount);
//得到链表头
struct Node* GetHead(struct Node** pHead, struct Node** pEnd, int* pCount);
//合并函数
void HeBing(struct Node** pHA, struct Node** pEA, int* pCA, struct Node** pHB, struct Node** pEB, int* pCB);
int main()
{
struct Node* g_pAH = NULL;
struct Node* g_pAE = NULL;
int g_iA = 0;
struct Node* g_pBH = NULL;
struct Node* g_pBE = NULL;
int g_iB = 0;
AddToEnd(&g_pAH, &g_pAE, &g_iA, 1);
AddToEnd(&g_pAH, &g_pAE, &g_iA, 5);
AddToEnd(&g_pAH, &g_pAE, &g_iA, 2);
AddToEnd(&g_pAH, &g_pAE, &g_iA, 8);
AddToEnd(&g_pAH, &g_pAE, &g_iA, 10);
AddToEnd(&g_pBH, &g_pBE, &g_iB, 2);
AddToEnd(&g_pBH, &g_pBE, &g_iB, 6);
AddToEnd(&g_pBH, &g_pBE, &g_iB, 9);
LookZheng(g_pAH, &g_iA);
LookZheng(g_pBH, &g_iB);
HeBing(&g_pAH, &g_pAE, &g_iA, &g_pBH, &g_pBE, &g_iB);
LookZheng(g_pBH, &g_iB);
FreeList(&g_pAH, &g_pAE, &g_iA);
FreeList(&g_pBH, &g_pBE, &g_iB);
return 0;
}
void HeBing(struct Node** pHA, struct Node** pEA, int* pCA, struct Node** pHB, struct Node** pEB, int* pCB)
{
//参数合法性检测
if (NULL == pHA || NULL == *pHA || NULL == pHB || NULL == *pHB)
return;
//循环
while(1)
{
//获取头
struct Node* pTemp = GetHead(pHA, pEA, pCA);
if (pTemp == NULL)
return;
//比头小
if (pTemp->iData <= (*pHB)->iData)
{
//新节点下一个指向头
pTemp->pNext = (*pHB);
// 头前一个指向新节点
(*pHB)->pPre = pTemp;
// 头向前移
(*pHB) = pTemp;
//数量++
(*pCB)++;
}
// 比尾大
else if (pTemp->iData >= (*pEB)->iData)
{
//尾巴的pNext指向新节点
(*pEB)->pNext = pTemp;
//新节点的pre指向尾巴
pTemp->pPre = (*pEB);
//尾后移
(*pEB) = pTemp;
//尾巴的pNext置NULL
(*pEB)->pNext = NULL;
//数量++
(*pCB)++;
}
else // 中间
{
//在链表B中找到合适的位置
struct Node* pT = *pHB;
if (pT == NULL)
return;
while (pT->pNext != NULL)
{
if (pTemp->iData >= pT->iData && pTemp->iData <= pT->pNext->iData)
break;
pT = pT->pNext;
}
//中间添加
if (NULL == pT->pNext)
return;
//新节点的pNext 指向pT的下一个
pTemp->pNext = pT->pNext;
//pT的下一个的pPre指向新节点
pT->pNext->pPre = pTemp;
//pT和新节点相连
pT->pNext = pTemp;
pTemp->pPre = pT;
//数量++
(*pCB)++;
}
}
}
struct Node* GetHead(struct Node** pHead, struct Node** pEnd, int* pCount)
{
//参数合法性检测
if (NULL == pHead || NULL == *pHead)
return NULL;
//摘头
struct Node* pT = *pHead; //记录头
if (*pHead == *pEnd) //只有一个节点
{
*pHead = NULL;
*pEnd = NULL;
}
else
{
*pHead = (*pHead)->pNext; //头往后走
(*pHead)->pPre = NULL;
}
(*pCount)--;
return pT;
}
void LookZheng(struct Node* pHead,int* pCount)
{
if (NULL == pHead)
return;
//循环遍历
printf("共有 %d 个节点: ", *pCount);
struct Node* pTemp = pHead;
while (pTemp != NULL)
{
printf("%d ", pTemp->iData);
pTemp = pTemp->pNext;
}
putchar('\n');
}
void FreeList(struct Node** pHead, struct Node** pEnd, int* pCount)
{
//参数合法性检测
if (NULL == *pHead)
return;
//申请中间变量
struct Node* pTemp = *pHead;
while (pTemp != NULL)
{
//记录要被释放的节点
struct Node* pT = pTemp;
//指向下一个节点
pTemp = pTemp->pNext;
//释放当前节点
free(pT);
}
*pHead = NULL;
*pEnd = NULL;
*pCount = 0;
}
void AddToEnd(struct Node** pHead, struct Node** pEnd, int* pCount, int iData)
{
//参数合法性检测
if (*pCount < 0)
return;
//申请节点
struct Node* pTemp = (struct Node*)malloc(sizeof(struct Node));
if (NULL == pTemp)
return;
//节点成员赋值
pTemp->iData = iData;
pTemp->pNext = NULL;
pTemp->pPre = NULL;
//连接链表
if (NULL == *pHead) //无节点
{
*pHead = pTemp;
}
else
{
(*pEnd)->pNext = pTemp;
pTemp->pPre = *pEnd;
}
*pEnd = pTemp;
//节点数量++
(*pCount)++;
}