栈是一种遵循**后进先出(LIFO)原则的线性数据结构,只允许在一端(栈顶)**进行插入(入栈)和删除(出栈)操作。本篇采用尾进尾出。
一、建立栈
结构体
struct Node
{
int a;
struct Node* pNext;
struct Node* pPre;
};
a是节点的数据域,用于存储实际数据。pNext是指向下一个节点的指针,若为链表末尾则指向NULL。pPre是指向前一个节点的指针,若为链表头部则指向NULL。
创建空头
struct Node* Stack()//创建空头
{
//申请节点
struct Node* pTemp = (struct Node*)malloc(sizeof(struct Node));
if (NULL == pTemp)
return pTemp;
//成员赋值
pTemp->a = -1;
pTemp->pNext = pTemp;
pTemp->pPre = pTemp;
return pTemp;
}
- 数据域
a被初始化为-1,通常用于标记头节点或无效值。 pNext和pPre指针均指向自身,形成循环结构。- 函数返回初始化后的头节点指针
二、进/出栈
1、尾进栈
代码:
void Push(struct Node* pStack, int a)//尾进栈
{
//参数合法性检测
if (NULL == pStack)
return;
//申请节点
struct Node* pTemp = (struct Node*)malloc(sizeof(struct Node));
if (NULL == pTemp)
return;
//成员赋值
pTemp->a = a;
pTemp->pNext = NULL;
pTemp->pPre = NULL;
//节点链接
pTemp->pPre = pStack->pPre;
pTemp->pNext = pStack;
pStack->pPre->pNext = pTemp;
pStack->pPre = pTemp;
}
实现了一个双向链表的尾进栈操作(Push)。头节点(pStack)的pNext指向第一个实际数据节点,pPre指向最后一个实际数据节点。
步骤:
参数合法性检测
检查传入的链表头节点指针是否为空。如果为空,直接返回,避免后续操作引发错误。
申请新节点
动态申请一个新节点内存空间。如果内存分配失败(返回NULL),直接返回,避免后续操作引发错误。
成员赋值
为新节点的数据成员a赋值为传入的参数a。初始化新节点的pNext和pPre指针为NULL。
节点链接
-
将新节点的pPre指向链表原来的最后一个节点(pStack->pPre)。
-
将新节点的pNext指向头节点(pStack)。
-
将原来的最后一个节点的pNext指向新节点(pStack->pPre->pNext = pTemp)。
-
更新头节点的pPre指向新节点(pStack->pPre = pTemp)。
2、判断是否为空栈
代码:
bool IsEmpty(struct Node* pStack)//判断是否为空栈
{
if (pStack->pNext == pStack)
return true;
return false;
}
定义了一个函数 IsEmpty,用于判断栈是否为空。函数接收一个指向栈顶节点(struct Node*)的指针 pStack,通过检查其 pNext 指针是否指向自身来判断栈是否为空。
3、出栈
代码:
void Pop(struct Node* pStack)//出栈
{
if (IsEmpty(pStack))
return NULL;
//记录被删除节点
struct Node* pT = pStack->pPre;
pT->pPre->pNext = pStack;
pStack->pPre = pT->pPre;
free(pT);
}
步骤:
-
检查栈是否为空
调用
IsEmpty(pStack)函数判断栈是否为空。如果为空,直接返回NULL,避免对空栈执行出栈操作。 -
记录待删除节点
栈顶元素是
pStack->pPre(即栈顶指针的前驱节点)。将其保存到临时指针pT中,以便后续操作和释放内存。 -
调整链表指针
将
pT的前驱节点(pT->pPre)与栈顶指针pStack连接起来,跳过待删除的节点pT:-
pT->pPre->pNext = pStack:将pT前驱节点的pNext指向pStack。 -
pStack->pPre = pT->pPre:将pStack的pPre指向pT的前驱节点。
-
-
释放内存
调用
free(pT)释放被删除节点的内存
三、释放
代码:
void FreeStack(struct Node** pStack)//释放链表
{
if (NULL == pStack)
return;
struct Node* pTemp = *pStack;
do
{
struct Node* pT = pTemp;
pTemp = pTemp->pNext;
free(pT);
} while (pTemp != *pStack);
*pStack = NULL;
}
步骤:
-
参数检查 :
函数首先检查传入的
pStack是否为NULL。如果是,直接返回,避免对空指针解引用。 -
临时指针初始化 :
pTemp初始化为链表的头节点*pStack,用于遍历链表。 -
循环释放节点 :
使用
do-while循环遍历链表,确保至少执行一次释放操作(即使链表只有一个节点)。-
在每次循环中,
pT保存当前节点的地址,pTemp移动到下一个节点。 -
调用
free(pT)释放当前节点的内存。 -
循环条件检查
pTemp是否回到头节点*pStack,确保循环链表的所有节点被释放。
-
-
头指针置空 :
循环结束后,将
*pStack设置为NULL,避免悬挂指针问题。