数据结构——栈

引言

栈(Stack)是计算机科学中一种非常重要的数据结构,它遵循"后进先出"(LIFO, Last In First Out)的原则。栈的应用非常广泛,例如函数调用、表达式求值、括号匹配等。本文将详细介绍栈的基本概念、实现方式以及常见应用场景,帮助你更好地理解和应用这一数据结构。


一、什么是栈?

栈是一种线性数据结构,它只允许在一端(栈顶)进行插入和删除操作。栈的操作遵循以下规则:

  1. 入栈(Push):将元素添加到栈顶。

  2. 出栈(Pop):移除栈顶元素。

  3. 查看栈顶元素(Peek/Top):获取栈顶元素的值,但不移除它。

  4. 判断栈是否为空(IsEmpty):检查栈中是否有元素。

栈的特性使其非常适合处理需要"后进先出"的场景。


二、栈的实现方式

栈可以通过多种方式实现,常见的有数组实现链表实现。下面我们分别介绍这两种实现方式。

1. 数组实现

使用数组实现栈时,需要维护一个指针top,用于指示栈顶的位置。

代码示例
cpp 复制代码
#include <stdio.h>
#include <stdlib.h>

#define MAX_SIZE 100

typedef struct {
    int data[MAX_SIZE];
    int top;
} Stack;

void initStack(Stack *s) {
    s->top = -1;
}

int isFull(Stack *s) {
    return s->top == MAX_SIZE - 1;
}

int isEmpty(Stack *s) {
    return s->top == -1;
}

void push(Stack *s, int value) {
    if (isFull(s)) {
        printf("Stack is full!\n");
        return;
    }
    s->data[++s->top] = value;
}

int pop(Stack *s) {
    if (isEmpty(s)) {
        printf("Stack is empty!\n");
        return -1;
    }
    return s->data[s->top--];
}

int peek(Stack *s) {
    if (isEmpty(s)) {
        printf("Stack is empty!\n");
        return -1;
    }
    return s->data[s->top];
}

int main() {
    Stack s;
    initStack(&s);

    push(&s, 10);
    push(&s, 20);
    push(&s, 30);

    printf("Top element: %d\n", peek(&s)); // 输出 30

    printf("Popped: %d\n", pop(&s)); // 输出 30
    printf("Popped: %d\n", pop(&s)); // 输出 20

    printf("Top element: %d\n", peek(&s)); // 输出 10

    return 0;
}
优缺点
  • 优点:实现简单,访问速度快。

  • 缺点:数组大小固定,容易造成空间浪费。

2. 链表实现

使用链表实现栈时,可以动态分配内存,避免了数组实现的固定大小限制。链表的头部作为栈顶。

代码示例
cpp 复制代码
#include <stdio.h>
#include <stdlib.h>

typedef struct Node {
    int data;
    struct Node *next;
} Node;

typedef struct {
    Node *top;
} Stack;

void initStack(Stack *s) {
    s->top = NULL;
}

int isEmpty(Stack *s) {
    return s->top == NULL;
}

void push(Stack *s, int value) {
    Node *newNode = (Node *)malloc(sizeof(Node));
    newNode->data = value;
    newNode->next = s->top;
    s->top = newNode;
}

int pop(Stack *s) {
    if (isEmpty(s)) {
        printf("Stack is empty!\n");
        return -1;
    }
    Node *temp = s->top;
    int value = temp->data;
    s->top = s->top->next;
    free(temp);
    return value;
}

int peek(Stack *s) {
    if (isEmpty(s)) {
        printf("Stack is empty!\n");
        return -1;
    }
    return s->top->data;
}

int main() {
    Stack s;
    initStack(&s);

    push(&s, 10);
    push(&s, 20);
    push(&s, 30);

    printf("Top element: %d\n", peek(&s)); // 输出 30

    printf("Popped: %d\n", pop(&s)); // 输出 30
    printf("Popped: %d\n", pop(&s)); // 输出 20

    printf("Top element: %d\n", peek(&s)); // 输出 10

    return 0;
}
优缺点
  • 优点:动态分配内存,空间利用率高。

  • 缺点:实现稍复杂,访问速度略慢于数组。


三、栈的应用场景

栈在实际开发中有许多应用场景,以下是一些常见的例子:

  1. 函数调用:操作系统使用栈管理函数的调用顺序。

  2. 表达式求值:栈用于计算中缀表达式或后缀表达式。

  3. 括号匹配:栈用于检查代码中的括号是否匹配。

  4. 浏览器的前进后退功能:栈用于记录访问历史。


四、栈的变种

除了普通栈,还有一些常见的栈变种:

  1. 最小栈(Min Stack):在常数时间内获取栈中的最小元素。

  2. 双栈(Dual Stack):使用两个栈实现队列或其他功能。

  3. 共享栈(Shared Stack):使用一个数组实现两个栈,节省空间。


五、总结

栈是一种简单但功能强大的数据结构,广泛应用于各种场景。通过本文的学习,你应该已经掌握了栈的基本概念、实现方式以及常见应用场景。无论是数组实现还是链表实现,栈的核心思想都是"后进先出"。希望本文能帮助你更好地理解和应用栈这一数据结构。

参考资料

  1. 《算法导论》------ Thomas H. Cormen

  2. 《数据结构与算法分析》------ Mark Allen Weiss

  3. GeeksforGeeks: Stack Data Structure


如果你对栈还有其他疑问,或者想了解更多数据结构的知识,欢迎在评论区留言讨论!

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