C/C++ 实现链式栈

C/C++ 实现链式栈:带头结点与不带头结点的双方案讲解(附完整代码)

在这里插入图片描述

在数据结构中,栈(Stack)是一种“后进先出”(LIFO, Last In First Out)的线性表。相比顺序栈,链式栈具有不需要预分配固定大小、插入删除方便的优点,本文将详细介绍链式栈的概念,并对“带头结点”和“不带头结点”的两种实现方式进行分析和对比,帮助初学者掌握链式栈的实现思路。


一、链栈和链表的关系

链栈(LinkStack)是基于单链表实现的栈结构。也就是说,链栈其实就是用链表来模拟“栈”的行为,只不过我们只在链表头部进行插入(Push)和删除(Pop)操作。

  • 在链表中,节点的插入/删除可以发生在任意位置;
  • 而在链栈中,操作只能发生在栈顶(即链表头部)
  • 因此,链栈是链表的一种特殊使用方式。

二、链式栈的两种实现方式

我们可以使用两种方式来实现链式栈:

  1. 不带头结点:栈顶指针 L 直接指向链表的第一个元素;
  2. 带头结点:增加一个不存储数据的头结点,L 指向该头结点,而真正的数据从 L->next 开始。

三、不带头结点的链式栈实现(推荐用于理解栈本质)

✅ 特点:

  • 栈顶指针 L 指向第一个实际数据节点;
  • 插入新元素使用“头插法”;
  • 结构简单,易于理解。

💻 代码实现:

#include <iostream>
#include <stdio.h>
#include <stdlib.h>

typedef struct LNode {
    int data;
    struct LNode* next;
} LNode, *LinkStack;

// 初始化栈
void init(LinkStack& L) {
    L = NULL; // 栈为空
}

// 判断是否为空
bool Empty(LinkStack L) {
    return L == NULL;
}

// 入栈操作(头插法)
bool Push(LinkStack& L, int e) {
    LNode* s = (LNode*)malloc(sizeof(LNode));
    s->data = e;
    s->next = L;
    L = s;
    return true;
}

// 取栈顶元素
bool GetTop(LinkStack L, int& e) {
    if (L == NULL) return false;
    e = L->data;
    return true;
}

// 出栈操作
bool Pop(LinkStack& L, int& x) {
    if (L == NULL) return false;
    LNode* p = L;
    x = p->data;
    L = p->next;
    free(p);
    return true;
}

int main() {
    LinkStack L;
    init(L);

    int e;
    printf("请输入入栈数据,以 -1 结束:\n");
    while (scanf("%d", &e) && e != -1) {
        Push(L, e);
    }

    if (GetTop(L, e)) {
        printf("当前栈顶元素为:%d\n", e);
    }

    printf("依次出栈元素:\n");
    while (Pop(L, e)) {
        printf("%d 出栈成功\n", e);
    }

    return 0;
}

四、带头结点的链式栈实现(适合后续扩展)

✅ 特点:

  • 多了一个“哨兵”节点,不存数据,仅用于统一操作逻辑;
  • L->next 指向真正的数据栈顶;
  • 在某些情况下便于操作,特别是栈空与非空判断时更统一。

💻 修改关键函数:

// 初始化(带头结点)
void init(LinkStack& L) {
    L = (LNode*)malloc(sizeof(LNode));
    L->next = NULL;
}

// 入栈
bool Push(LinkStack& L, int e) {
    LNode* s = (LNode*)malloc(sizeof(LNode));
    s->data = e;
    s->next = L->next;
    L->next = s;
    return true;
}

// 获取栈顶元素
bool GetTop(LinkStack L, int& e) {
    if (L->next == NULL) return false;
    e = L->next->data;
    return true;
}

// 出栈
bool Pop(LinkStack& L, int& x) {
    if (L->next == NULL) return false;
    LNode* p = L->next;
    x = p->data;
    L->next = p->next;
    free(p);
    return true;
}

五、两种方式的对比与选择建议

方面不带头结点带头结点
内存开销少一个头结点多一个头结点
编程复杂度略高(边界判断多)相对简单,逻辑统一
推荐用途学习栈本质、栈顶即头结点后续扩展或统一风格

👉 初学者建议从不带头结点开始掌握栈的核心逻辑,然后再理解带头结点的好处。


六、总结

  • 链栈是通过链表实现的栈,具有动态分配、无栈满限制的优点;
  • 常见的两种链栈实现方式是“带头结点”和“不带头结点”;
  • 使用哪种方式可以根据需求和个人喜好选择;
  • 掌握链栈,是为后续理解表达式求值、括号匹配、递归调用等打好基础!

在这里插入图片描述

如果你觉得本文对你有帮助,欢迎点赞、收藏、评论支持 👍
如有问题也欢迎留言讨论交流~


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Yhame.

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值