数据结构——栈(十六)

栈是一种遵循**后进先出(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,通常用于标记头节点或无效值。
  • pNextpPre指针均指向自身,形成循环结构。
  • 函数返回初始化后的头节点指针

二、进/出栈

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。

节点链接

  1. 将新节点的pPre指向链表原来的最后一个节点(pStack->pPre)。

  2. 将新节点的pNext指向头节点(pStack)。

  3. 将原来的最后一个节点的pNext指向新节点(pStack->pPre->pNext = pTemp)。

  4. 更新头节点的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);

}

步骤:
  1. 检查栈是否为空

    调用IsEmpty(pStack)函数判断栈是否为空。如果为空,直接返回NULL,避免对空栈执行出栈操作。

  2. 记录待删除节点

    栈顶元素是pStack->pPre(即栈顶指针的前驱节点)。将其保存到临时指针pT中,以便后续操作和释放内存。

  3. 调整链表指针

    pT的前驱节点(pT->pPre)与栈顶指针pStack连接起来,跳过待删除的节点pT

    • pT->pPre->pNext = pStack:将pT前驱节点的pNext指向pStack

    • pStack->pPre = pT->pPre:将pStackpPre指向pT的前驱节点。

  4. 释放内存

    调用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;

}

步骤:
  1. 参数检查

    函数首先检查传入的 pStack 是否为 NULL。如果是,直接返回,避免对空指针解引用。

  2. 临时指针初始化
    pTemp 初始化为链表的头节点 *pStack,用于遍历链表。

  3. 循环释放节点

    使用 do-while 循环遍历链表,确保至少执行一次释放操作(即使链表只有一个节点)。

    • 在每次循环中,pT 保存当前节点的地址,pTemp 移动到下一个节点。

    • 调用 free(pT) 释放当前节点的内存。

    • 循环条件检查 pTemp 是否回到头节点 *pStack,确保循环链表的所有节点被释放。

  4. 头指针置空

    循环结束后,将 *pStack 设置为 NULL,避免悬挂指针问题。

相关推荐
探序基因3 小时前
单细胞Seurat数据结构修改分群信息
数据结构
六义义3 小时前
java基础十二
java·数据结构·算法
张张努力变强6 小时前
C++ 类和对象(四):const成员函数、取地址运算符重载全精讲
开发语言·数据结构·c++·后端
历程里程碑8 小时前
双指针 --- 接雨水
java·数据结构·python·算法·leetcode·职场和发展·tornado
Snow_day.8 小时前
【补题记录】AT441,442
数据结构·算法·贪心算法·动态规划·图论
Tim_109 小时前
【算法专题训练】38、二分查找算法
数据结构·算法
weixin_461769409 小时前
判断是否为素数
数据结构·c++·算法·素数
玉树临风ives10 小时前
atcoder ABC442 题解
数据结构·c++·算法
橘颂TA10 小时前
【剑斩OFFER】算法的暴力美学——力扣 542 .01 题:矩阵
数据结构·c++·算法·leetcode·职场和发展·哈希算法·结构与算法