数据结构之栈:从原理到实现

embedded/2024/11/27 15:34:50/

1. 什么是栈?

数据结构中,栈(Stack)是一种非常基础的线性数据结构,它的特点是后进先出(LIFO, Last In First Out)。想象一个装盘子的架子,你只能从最上面拿盘子或放盘子,先放进去的盘子需要最后才能被取出。这种操作方式使得栈在许多程序设计中都非常有用。

栈的操作通常有两个核心功能:

  • 入栈(Push):将一个元素压入栈中。
  • 出栈(Pop):将栈顶的元素取出。

栈还有一个操作是:

  • 查看栈顶元素(Peek或Top):获取栈顶元素但不移除它。
2. 栈的结构

栈是一种受限的线性表,所有操作都仅限于栈顶。栈的实现方式通常有两种:

  1. 顺序栈:使用数组实现,栈的容量固定。
  2. 链式栈:使用链表实现,栈的容量可以动态变化。
栈的结构特点:
  • 栈顶(Top):栈中最先被访问的位置。
  • 栈底(Bottom):栈中最先压入的元素,始终在栈的底部。
  • 栈只允许从栈顶插入或删除元素。
栈的核心属性:
  • 容量(Capacity):栈可以容纳的最大元素数量(顺序栈中需要指定容量,链式栈无需指定)。
  • 大小(Size):栈中当前的元素数量。

3. 栈的实现

我们将分别使用数组链表来实现栈,并分析它们的优缺点。

(1)顺序栈

顺序栈使用数组来实现,栈的容量固定。在操作时,我们需要一个变量top来记录当前栈顶的位置。

顺序栈的定义和基本操作
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>// 定义栈结构
typedef struct {int *data;      // 动态数组,存放栈元素int top;        // 栈顶索引int capacity;   // 栈的容量
} Stack;// 初始化栈
Stack* createStack(int capacity) {Stack* stack = (Stack*)malloc(sizeof(Stack));stack->data = (int*)malloc(sizeof(int) * capacity);stack->top = -1;         // 初始时,栈为空stack->capacity = capacity;return stack;
}// 判断栈是否为空
bool isEmpty(Stack* stack) {return stack->top == -1;
}// 判断栈是否已满
bool isFull(Stack* stack) {return stack->top == stack->capacity - 1;
}// 入栈操作
void push(Stack* stack, int value) {if (isFull(stack)) {printf("栈已满,无法插入元素。\n");return;}stack->data[++stack->top] = value; // 栈顶指针加1后插入元素
}// 出栈操作
int pop(Stack* stack) {if (isEmpty(stack)) {printf("栈为空,无法删除元素。\n");return -1;}return stack->data[stack->top--]; // 返回栈顶元素,并将栈顶指针减1
}// 查看栈顶元素
int peek(Stack* stack) {if (isEmpty(stack)) {printf("栈为空,无栈顶元素。\n");return -1;}return stack->data[stack->top];
}// 释放栈
void freeStack(Stack* stack) {free(stack->data);free(stack);
}
测试顺序栈
​
int main() {Stack* stack = createStack(5);push(stack, 10);push(stack, 20);push(stack, 30);printf("栈顶元素: %d\n", peek(stack));printf("出栈元素: %d\n", pop(stack));printf("栈顶元素: %d\n", peek(stack));freeStack(stack);return 0;
}​
(2)链式栈

链式栈使用链表实现,栈的容量可以动态扩展,每次入栈或出栈操作都会动态分配或释放内存。

链式栈的定义和基本操作
​
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>// 定义链表节点
typedef struct Node {int data;             // 数据域struct Node* next;    // 指向下一个节点
} Node;// 定义栈结构
typedef struct {Node* top;            // 栈顶指针
} Stack;// 初始化栈
Stack* createStack() {Stack* stack = (Stack*)malloc(sizeof(Stack));stack->top = NULL;return stack;
}// 判断栈是否为空
bool isEmpty(Stack* stack) {return stack->top == NULL;
}// 入栈操作
void push(Stack* stack, int value) {Node* newNode = (Node*)malloc(sizeof(Node));newNode->data = value;newNode->next = stack->top; // 新节点的next指向当前栈顶stack->top = newNode;       // 更新栈顶指针
}// 出栈操作
int pop(Stack* stack) {if (isEmpty(stack)) {printf("栈为空,无法删除元素。\n");return -1;}Node* temp = stack->top;int value = temp->data;stack->top = temp->next; // 更新栈顶指针free(temp);              // 释放内存return value;
}// 查看栈顶元素
int peek(Stack* stack) {if (isEmpty(stack)) {printf("栈为空,无栈顶元素。\n");return -1;}return stack->top->data;
}// 释放栈
void freeStack(Stack* stack) {while (!isEmpty(stack)) {pop(stack);}free(stack);
}​
测试链式栈
​
int main() {Stack* stack = createStack();push(stack, 10);push(stack, 20);push(stack, 30);printf("栈顶元素: %d\n", peek(stack));printf("出栈元素: %d\n", pop(stack));printf("栈顶元素: %d\n", peek(stack));freeStack(stack);return 0;
}​

4. 顺序栈和链式栈的对比
特性顺序栈链式栈
存储方式数组,固定大小链表,动态大小
动态扩展需要手动扩展容量内存动态分配,自动扩展
访问速度较快较慢
内存利用率容量可能超出实际需求精确分配
实现复杂度简单较复杂

5. 栈的应用场景

栈是一种非常实用的数据结构,常见的应用包括:

  1. 函数调用栈:用来保存函数调用过程中参数和局部变量的信息。
  2. 表达式求值:在中缀表达式转后缀表达式或计算后缀表达式时,栈是关键工具。
  3. 括号匹配:检查括号是否成对出现。
  4. 深度优先搜索(DFS):利用栈实现图的深度优先搜索算法。
  5. 浏览器的前进后退功能:使用两个栈分别保存前进和后退的历史记录。

最后

栈作为一种受限的线性表,以其独特的后进先出的操作方式,成为计算机科学中不可或缺的数据结构。希望通过这篇文章,你不仅能栈的结构和特点,还可以掌握顺序栈和链式栈的实现及其应用场景!


http://www.ppmy.cn/embedded/140935.html

相关文章

【H2O2|全栈】JS进阶知识(十一)axios入门

目录 前言 开篇语 准备工作 获取 介绍 使用 结束语 前言 开篇语 本系列博客主要分享JavaScript的进阶语法知识&#xff0c;本期主要对axios进行基本的了解。 与基础部分的语法相比&#xff0c;ES6的语法进行了一些更加严谨的约束和优化&#xff0c;因此&#xff0c;在…

leetcode hot100【LeetCode 215.数组中的第K个最大元素】java实现

LeetCode 215.数组中的第K个最大元素 题目描述 给定一个整数数组 nums 和一个整数 k&#xff0c;请返回数组中第 k 个最大的元素。 请注意&#xff0c;要求排名是从大到小的&#xff0c;因此第 k 个最大元素是排序后的第 k 个元素。你需要设计一个高效的算法来解决这个问题。…

基础入门-Web应用架构搭建域名源码站库分离MVC模型解析受限对应路径

知识点&#xff1a; 1、基础入门-Web应用-域名上的技术要点 2、基础入门-Web应用-源码上的技术要点 3、基础入门-Web应用-数据上的技术要点 4、基础入门-Web应用-解析上的技术要点 5、基础入门-Web应用-平台上的技术要点 一、演示案例-域名差异-主站&分站&端口站&…

SpringBoot - 优雅的实现【账号登录错误次数的限制和锁定】

文章目录 Pre需求实现步骤简易实现1. 添加依赖2. 配置文件3. 自定义注解4. AOP切面5. 使用自定义注解&#xff1a;6. 测试 附总结 Pre SpringBoot - 优雅的实现【流控】 需求 需求描述&#xff1a; 登录错误次数限制&#xff1a;在用户登录时&#xff0c;记录每个账号的登录错…

Conda命令速查

命令速查 命令Command新建Python环境conda create --name hello python3.10.14激活环境conda activate hello退出当前环境conda deactivate安装python包conda install < package name>安装python包pip install < package name>查看所有python环境conda env list

嵌入式系统与OpenCV

目录 一、OpenCV 简介 二、嵌入式 OpenCV 的安装方法 1. Ubuntu 系统下的安装 2. 嵌入式 ARM 系统中的安装 3. Windows10 和树莓派系统下的安装 三、嵌入式 OpenCV 的性能优化 1. 介绍嵌入式平台上对 OpenCV 进行优化的必要性。 2. 利用嵌入式开发工具&#xff0c;如优…

分布式查询处理优化之数据分片

基本的数据分布策略 数据分片 分片是将分布式数据库的全局数据逻辑划分为关系片段并且进行实际的物理分配的过程。不同的分布式系统有着不同的分片策略。 关系数据库主要通过数据分片技术对全局数据进行逻辑划分和实际的物理分配。考虑的主要因素&#xff1a;数据的模式特征…

Rust赋能前端: 纯血前端将 Table 导出 Excel

❝ 人的本事靠自己&#xff0c;人的成长靠网络 大家好&#xff0c;我是柒八九。一个专注于前端开发技术/Rust及AI应用知识分享的Coder ❝ 此篇文章所涉及到的技术有 Rust( Rust接收json对象并解析/Rust生成xml) WebAssembly 表格合并(静态/动态) React/Vue表格导出 excel Rspac…