数据结构——栈(十六)

栈是一种遵循**后进先出(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,避免悬挂指针问题。

相关推荐
CSharp精选营3 天前
关系型 vs 非关系型:从原理到选型,一文搞定数据库核心分类
数据结构·nosql·关系型数据库·非关系型数据库·技术选型
刘马想放假6 天前
Modbus 全栈技术解析:TCP、RTU、ASCII、RTU over TCP
数据结构·网络协议
北域码匠7 天前
冒泡排序太慢?鸡尾酒排序双向优化,原生 C# 零第三方库完整代码
数据结构·排序算法·泛型·c# 算法·鸡尾酒排序·原生 c# 开发·冒泡排序优化·嵌入式算法
Darling噜啦啦14 天前
列表转树算法深度解析:从 Map 到 Reduce 的两种实现,面试高频考点
数据结构·算法·面试
小小工匠15 天前
Redis - 事务机制:能实现 ACID 属性吗
数据结构·redis·性能优化·并发·持久化
玖玥拾15 天前
C/C++ 数据结构(七)栈、容器适配器
c语言·数据结构·c++··容器适配器
Qres82115 天前
算法复键——树状数组
数据结构·算法
牛油果子哥q15 天前
并查集(DSU)超精讲,路径压缩、按秩合并、万能模板、连通性判定、最小生成树与刷题实战全解
数据结构·c++·最小生成树·并查集
凌波粒15 天前
LeetCode--491.递增子序列(回溯算法)
数据结构·算法·leetcode
WL学习笔记15 天前
单项不带头不循环链表
数据结构·链表