数据结构:栈

数据结构中的栈(Stack)

栈是一种重要的线性数据结构,遵循"后进先出"(Last In, First Out, LIFO)的原则。这意味着最后一个进入栈的元素将是第一个被弹出的元素。栈通常用于深度优先搜索、逆波兰表达式计算、函数调用栈等场景。

栈的基本定义

栈的基本操作包括:

  1. Push(压栈):将元素添加到栈的顶部。

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

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

  4. IsEmpty(判空):检查栈是否为空。

  5. IsFull(判满):检查栈是否已满(如果栈的大小有限)。

栈的实现方式

栈可以通过数组或链表来实现。以下是两种实现方式的详细介绍:

1. 数组实现栈

数组实现的栈是一种静态栈,其大小在初始化时确定。数组实现栈的好处是简单易用,适合小规模数据处理。

示例代码(数组实现栈):
cpp 复制代码
#include <stdio.h>
#include <stdbool.h>

#define MAX_SIZE 100

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

// 初始化栈
void initStack(Stack *s) {
    s->top = -1;
}

// 判断栈是否为空
bool isEmpty(Stack *s) {
    return s->top == -1;
}

// 判断栈是否已满
bool isFull(Stack *s) {
    return s->top == MAX_SIZE - 1;
}

// 压栈操作
void push(Stack *s, int value) {
    if (isFull(s)) {
        printf("栈已满,无法压栈\n");
        return;
    }
    s->top++;
    s->data[s->top] = value;
}

// 出栈操作
int pop(Stack *s) {
    if (isEmpty(s)) {
        printf("栈为空,无法出栈\n");
        return -1;  // 返回一个无效值表示错误
    }
    int value = s->data[s->top];
    s->top--;
    return value;
}

// 查看栈顶元素
int peek(Stack *s) {
    if (isEmpty(s)) {
        printf("栈为空,无法查看栈顶\n");
        return -1;  // 返回一个无效值表示错误
    }
    return s->data[s->top];
}

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

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

    printf("栈顶元素: %d\n", peek(&s));
    printf("出栈元素: %d\n", pop(&s));
    printf("栈顶元素: %d\n", peek(&s));

    return 0;
}
输出:
cpp 复制代码
栈顶元素: 30
出栈元素: 30
栈顶元素: 20
2. 链表实现栈

链表实现的栈是一种动态栈,其大小可以根据需要动态调整。链表实现栈的好处是可以灵活处理大规模数据,适合需要频繁插入和删除操作的场景。

示例代码(链表实现栈):
cpp 复制代码
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>

// 定义链表节点结构
struct Node {
    int data;
    struct Node* next;
};

typedef struct {
    struct Node* top;
} Stack;

// 初始化栈
void initStack(Stack *s) {
    s->top = NULL;
}

// 判断栈是否为空
bool isEmpty(Stack *s) {
    return s->top == NULL;
}

// 压栈操作
void push(Stack *s, int value) {
    struct Node* newNode = (struct Node*)malloc(sizeof(struct Node));
    newNode->data = value;
    newNode->next = s->top;
    s->top = newNode;
}

// 出栈操作
int pop(Stack *s) {
    if (isEmpty(s)) {
        printf("栈为空,无法出栈\n");
        return -1;  // 返回一个无效值表示错误
    }
    struct Node* temp = s->top;
    int value = temp->data;
    s->top = temp->next;
    free(temp);
    return value;
}

// 查看栈顶元素
int peek(Stack *s) {
    if (isEmpty(s)) {
        printf("栈为空,无法查看栈顶\n");
        return -1;  // 返回一个无效值表示错误
    }
    return s->top->data;
}

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

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

    printf("栈顶元素: %d\n", peek(&s));
    printf("出栈元素: %d\n", pop(&s));
    printf("栈顶元素: %d\n", peek(&s));

    return 0;
}
输出:
cpp 复制代码
栈顶元素: 30
出栈元素: 30
栈顶元素: 20
栈的应用案例
  1. 函数调用栈:在程序执行过程中,函数的调用和返回使用栈来管理。每次调用一个函数时,当前的执行状态(如返回地址、局部变量等)会被压入栈中,函数返回时再从栈中弹出。

  2. 括号匹配:在编译器中,栈可以用于检查括号是否匹配。例如,左括号压入栈,右括号出栈,最后栈为空则表示括号匹配。

  3. 逆波兰表达式计算:栈可以用于计算后缀表达式(逆波兰表达式)。操作数压入栈,遇到操作符时弹出栈顶的两个操作数进行计算,结果再压入栈。

  4. 浏览器历史记录:浏览器的前进和后退按钮可以使用栈来实现。后退时将当前页面地址出栈,前进时将之前出栈的页面地址重新压入栈。

总结

栈是一种简单但强大的数据结构,遵循LIFO原则,适合处理需要后进先出的场景。栈的基本操作包括压栈、出栈、查看栈顶和判断栈空/满。栈可以通过数组或链表实现,数组实现适合小规模数据处理,链表实现适合动态调整大小。栈在函数调用、括号匹配、表达式计算等场景中有广泛的应用。

相关推荐
im_AMBER1 小时前
Leetcode 99 删除排序链表中的重复元素 | 合并两个链表
数据结构·笔记·学习·算法·leetcode·链表
s砚山s2 小时前
代码随想录刷题——二叉树篇(十三)
数据结构·算法
ulias2122 小时前
AVL树的实现
开发语言·数据结构·c++·windows
黎雁·泠崖2 小时前
二叉树知识体系全梳理:从基础到进阶一站式通关
c语言·数据结构·leetcode
蜕变菜鸟3 小时前
JS的Object.keys()和sort()排序的用法
数据结构·算法
镜中人★3 小时前
408数据结构考纲知识点(更新中)
数据结构
杜子不疼.3 小时前
【LeetCode30_滑动窗口 + 哈希表】:三招搞定“串联所有单词的子串”
数据结构·算法·哈希算法
妹妹够啦3 小时前
1. 两数之和
数据结构·算法·leetcode
vyuvyucd3 小时前
Java数组与Arrays类实战指南
数据结构·算法
EXtreme353 小时前
【数据结构】彻底搞懂二叉树:四种遍历逻辑、经典OJ题与递归性能全解析
c语言·数据结构·算法·二叉树·递归