【数据结构】手动实现栈:使用链表轻松搞定

堆栈(Stack)是一种常用的数据结构,具有**后进先出(LIFO,Last In First Out)**的特性。在这篇博客中,我们将使用链表实现一个堆栈,深入剖析其代码结构与实现原理,帮助你掌握这一基础数据结构的动态实现方式。

本篇文章需要读者具有链表的基础知识,若不了解可参考以下文章:


一、堆栈的基本概念

1. 堆栈的特点

  • 后进先出(LIFO): 最后压入栈的数据最先弹出。
  • 两个主要操作:
    • push(入栈):将元素压入堆栈。
    • pop(出栈):弹出栈顶元素。

2. 使用链表实现堆栈的优点

  • 动态性: 链表可以动态分配内存,无需事先确定栈的大小。
  • 灵活性: 与数组实现不同,不会因为固定大小限制栈的容量。

二、链表实现堆栈的设计

我们将使用一个单链表(Singly Linked List)来实现堆栈,其中:

  • 每个节点(Node)包含一个值和指向下一个节点的指针。
  • 栈顶(top)是链表的第一个节点,表示当前堆栈的栈顶元素。

1. 节点结构(Node

每个节点包含一个值和指向下一个节点的指针:

struct Node {
    int value;      // 节点的值
    Node *next;     // 指向下一个节点的指针
    Node(int v) {   // 构造函数
        value = v;
        next = nullptr;
    }
};

2. 栈结构(Stack

  • 栈顶指针(top):指向链表的第一个节点。
  • 基本功能:
    • push(int x):将值 x 入栈。
    • pop():弹出栈顶元素。
    • isEmpty():判断栈是否为空。
    • 析构函数:释放所有动态分配的节点,防止内存泄漏。

完整结构如下:

struct Stack {
private:
    Node *top;  // 栈顶指针

public:
    Stack() {
        top = nullptr; // 初始化为空栈
    }

    ~Stack() {
        while (top) {
            Node *t = top;
            top = top->next;
            delete t;
        }
    }

    bool isEmpty() {
        return top == nullptr;
    }

    void push(int x) {
        Node *t = new Node(x);
        t->next = top;
        top = t;
    }

    int pop() {
        if (isEmpty()) {
            throw runtime_error("Stack is empty!");
        }
        Node *t = top;
        top = top->next;
        int res = t->value;
        delete t;
        return res;
    }
};

三、完整代码示例

以下是完整代码,包括堆栈的定义及其在主函数中的使用:

#include "bits/stdc++.h"

using namespace std;

struct Node {
    int value;
    Node *next;
    Node(int v) {
        value = v;
        next  = nullptr;
    }
};

struct Stack {
private:
    Node *top;

public:
    Stack() {
        top = nullptr;
    }

    ~Stack() {
        while (top) {
            Node *t = top;
            top = top->next;
            delete t;
        }
    }

    bool isEmpty() {
        return top == nullptr;
    }

    void push(int x) {
        Node *t = new Node(x);
        t->next = top;
        top = t;
    }

    int pop() {
        if (isEmpty()) {
            throw runtime_error("Stack is empty!");
        }
        Node *t = top;
        top = top->next;
        int res = t->value;
        delete t;
        return res;
    }
};

int main() {
    Stack *s = new Stack();
    s->push(1);
    s->push(2);
    s->push(3);

    cout << s->pop() << endl; // 输出 3
    cout << s->pop() << endl; // 输出 2
    cout << s->pop() << endl; // 输出 1

    delete s;
    return 0;
}

输出结果

3
2
1

四、关键点解析

1. 动态内存管理

  • push 操作中,动态分配内存用于存储新节点。
  • pop 操作中,释放已弹出节点的内存。

2. 异常处理

当栈为空时,pop 会抛出异常,防止非法操作:

if (isEmpty()) {
    throw runtime_error("Stack is empty!");
}

3. 析构函数

使用 ~Stack() 释放所有动态分配的节点,避免内存泄漏:

~Stack() {
    while (top) {
        Node *t = top;
        top = top->next;
        delete t;
    }
}

五、总结

通过这篇文章,我们实现了一个基于链表的堆栈,并学习了以下知识:

  1. 如何使用链表动态实现堆栈的基本操作。
  2. 如何合理管理内存,避免内存泄漏。
  3. 异常处理的重要性,以及如何通过析构函数清理资源。

这种动态实现方式非常适合需要灵活调整大小的场景,希望本文能帮助你更好地理解堆栈这一数据结构。

评论 13
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值