数据结构界的三大幻神----栈

简介: 数据结构界的三大幻神----栈

首先强调一下,操作系统中也有栈的概念,但那个栈是用来存放变量,涉及到函数栈帧的销毁,与数据结构中的栈是两个不同的概念

一.栈的概念

栈(Stack)是一种抽象数据类型,它遵循后进先出(Last-In, First-Out,LIFO)的原则。栈就像一摞盘子,你只能从最上面取盘子或把盘子放在最上面

 

栈的基本操作包括入栈(Push)和出栈(Pop)。入栈就是把元素添加到栈的顶部,出栈则是从栈的顶部取出元素。除此之外,常见的栈操作还有查看栈顶元素(Peek)和判断栈是否为空。

 

栈在很多场景中都有应用,比如函数调用栈、表达式求值、括号匹配、递归等。它的优势在于能够高效地进行入栈和出栈操作,而且不需要事先知道元素的数量。

 

例如,在编程中,当你调用一个函数时,系统会将函数的参数和返回地址压入栈中。当函数执行完毕后,再从栈中弹出返回地址,从而实现函数的正确返回。

二.栈与递归的关系

递归和栈的关系非常密切 递归是一种通过函数自身调用自身来解决问题的方法。在递归过程中,函数会不断地调用自己,直到达到某个终止条件。

 

当函数进行递归调用时,系统会自动使用栈来保存函数的调用信息,包括函数参数、局部变量等。每次递归调用都会在栈中创建一个新的帧(Frame),用于保存当前函数的状态。

 

当递归函数返回时,系统会从栈中弹出对应的帧,恢复函数的状态,并继续执行后续操作。通过栈的机制,递归可以实现函数在不同层次上的正确执行和状态恢复。

 

例如,计算阶乘的递归函数可以通过不断调用自身来累积阶乘的结果。在这个过程中,栈会记录每次递归调用的状态,确保正确地计算阶乘。

 

需要注意的是,递归虽然在表达逻辑上比较简洁,但由于栈的深度限制,递归可能会导致栈溢出等问题。在实际应用中,需要合理使用递归,并注意避免过度递归导致的问题。

三.手撕栈

#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
#include <stdbool.h>
typedef int StackData;
typedef struct Stack
{
    StackData* data;
    int top;
    int size;
}Stack;
//初始化栈
void initStack(Stack* st)
{
    assert(st);
    st->data = NULL;
    st->top = 0;
    st->size = 0;
}
//销毁栈
void destroyStack(Stack* st)
{
    assert(st);
    free(st->data);
    st->data = NULL;
    st->size = st->top = 0;
}
//栈是否为空
bool isEmpty(Stack* st)
{
    assert(st);
    return st->top == 0;
}
//出栈
void popStack(Stack* st)
{
    assert(st);
    assert(!isEmpty);
    st->top--;
}
//入栈
void pushStack(Stack* st, StackData data)
{
    assert(st);
    if (st->top == st->size)
    {
        int newsize = st->size == 0 ? 4 : 2 * st->size;
        StackData* temp = (StackData*)realloc(st->data, sizeof(StackData) * newsize);
        if (temp != NULL)
        {
            st->data = temp;
            st->size = newsize;
        }
    }
    st->data[st->top] = data;
    st->top++;
}
//取出栈顶元素
StackData topStack(Stack* st)
{
    assert(st);
    assert(!isEmpty);
    return st->data[st->top - 1];
}
//栈的大小
int sizeStack(Stack* st)
{
    assert(st);
    return st->top;
}
int main()
{
    return 0;
}
相关文章
|
5天前
|
算法 安全 测试技术
golang 栈数据结构的实现和应用
本文详细介绍了“栈”这一数据结构的特点,并用Golang实现栈。栈是一种FILO(First In Last Out,即先进后出或后进先出)的数据结构。文章展示了如何用slice和链表来实现栈,并通过golang benchmark测试了二者的性能差异。此外,还提供了几个使用栈结构解决的实际算法问题示例,如有效的括号匹配等。
golang 栈数据结构的实现和应用
01_设计一个有getMin功能的栈
01_设计一个有getMin功能的栈
|
5天前
|
前端开发
07_用队列实现栈
07_用队列实现栈
06_用栈来求解汉诺塔问题
06_用栈来求解汉诺塔问题
05_用一个栈实现另一个栈的排序
05_用一个栈实现另一个栈的排序
03_如何仅用递归函数和栈操作逆序一个栈
03_如何仅用递归函数和栈操作逆序一个栈
|
5天前
|
测试技术
02_由两个栈组成的队列
02_由两个栈组成的队列
|
9天前
|
存储
|
24天前
|
存储 人工智能 C语言
数据结构基础详解(C语言): 栈的括号匹配(实战)与栈的表达式求值&&特殊矩阵的压缩存储
本文首先介绍了栈的应用之一——括号匹配,利用栈的特性实现左右括号的匹配检测。接着详细描述了南京理工大学的一道编程题,要求判断输入字符串中的括号是否正确匹配,并给出了完整的代码示例。此外,还探讨了栈在表达式求值中的应用,包括中缀、后缀和前缀表达式的转换与计算方法。最后,文章介绍了矩阵的压缩存储技术,涵盖对称矩阵、三角矩阵及稀疏矩阵的不同压缩存储策略,提高存储效率。
|
26天前
|
存储 C语言
数据结构基础详解(C语言): 栈与队列的详解附完整代码
栈是一种仅允许在一端进行插入和删除操作的线性表,常用于解决括号匹配、函数调用等问题。栈分为顺序栈和链栈,顺序栈使用数组存储,链栈基于单链表实现。栈的主要操作包括初始化、销毁、入栈、出栈等。栈的应用广泛,如表达式求值、递归等场景。栈的顺序存储结构由数组和栈顶指针构成,链栈则基于单链表的头插法实现。
151 3