数据结构与算法---栈

主要解决最近相关性问题

题目

  1. https://leetcode-cn.com/problems/valid-parentheses/
  2. https://leetcode-cn.com/problems/min-stack/
  3. https://leetcode-cn.com/problems/largest-rectangle-in-histogram
  4. https://leetcode-cn.com/problems/sliding-window-maximum
  5. https://leetcode.com/problems/design-circular-deque
  6. https://leetcode.com/problems/trapping-rain-water/
  7. 分析 Queue 和 Priority Queue 的源码
  8. 用 add first 或 add last 这套新的 API 改写 Deque 的代码

如何理解栈

后进者先出,先进者后出,这就是典型的“栈”结构。
栈是一种“操作受限”的线性表,只允许在一端插入和删除数据
从功能上来说,数组或链表确实可以替代栈,但你要知道,特定的数据结构是对特定场景的抽象,而且,数组或链表暴露了太多的操作接口,操作上的确灵活自由,但使用时就比较不可控,自然也就更容易出错。
当某个数据集合只涉及在一端插入和删除数据,并且满足后进先出、先进后出的特性,我们就应该首选“栈”这种数据结构。

如何实现栈

主要:
------ 入栈 —>栈顶插入一个数据
------ 出栈 ---->栈顶删除一个数据

数组实现----->顺序栈
链表实现----->链式栈


// 基于数组实现的顺序栈
public class ArrayStack {
  private String[] items;  // 数组
  private int count;       // 栈中元素个数
  private int n;           //栈的大小

  // 初始化数组,申请一个大小为n的数组空间
  public ArrayStack(int n) {
    this.items = new String[n];
    this.n = n;
    this.count = 0;
  }

  // 入栈操作
  public boolean push(String item) {
    // 数组空间不够了,直接返回false,入栈失败。
    if (count == n) return false;
    // 将item放到下标为count的位置,并且count加一
    items[count] = item;
    ++count;
    return true;
  }
  
  // 出栈操作
  public String pop() {
    // 栈为空,则直接返回null
    if (count == 0) return null;
    // 返回下标为count-1的数组元素,并且栈中元素个数count减一
    String tmp = items[count-1];
    --count;
    return tmp;
  }
}
支持动态扩容的顺序栈

像支持动态扩容的容器一样,当栈满了以后,申请一个更大的数组,将原来的数据拷贝到新的数组中
那么支持动态扩容的栈入栈和出栈的时间复杂度是多少呢
在这里插入图片描述

入栈 ----(直接插入)最好时间复杂度O(1) -----(栈满了,扩容,拷贝)最差时间复杂度O(n)
-----均摊时间复杂度O(1)

栈在函数调用中的应用

操作系统中给线程分配内存空间,叫做栈,用来储存函数调用时的临时变量,函数调用的过程中不断压栈(调用)和出栈(return),参考递归函数调用 。

栈在表达式求值中的应用

编译器如何利用栈来实现表达式求值
编译器通过两个栈来实现,一个报错操作数的栈,另一个保存运算符的栈。
例子:3+5*8-6

遇到数字 ---->直接压入操作数栈
 遇到操作符----->就与操作符栈的栈顶操作符比较---->如果比栈顶元素优先级高---->就压栈
 									 ----->如果比栈顶元素优先级低或相同--
 									 ----->从运算符栈中取栈顶运算符,从操作数的栈顶取两个操作数
 									 ----->然后计算,再把计算完的结果压入操作数栈

在这里插入图片描述

栈在括号匹配中的应用

假设表达式中只包含三种括号,圆括号 ()、方括号[]和花括号{},并且它们可以任意嵌套。比如,{[] ()[{}]}或[{()}([])]等都为合法格式,而{[}()]或[({)]为不合法的格式。现在给你一个包含三种括号的表达式字符串,如何检查它是否合法呢?
我们用栈来保存未匹配的左括号,从左到右依次扫描字符串。当扫描到左括号时,则将其压入栈中;当扫描到右括号时,从栈顶取出一个左括号。如果能够匹配,比如“(”跟“)”匹配,“[”跟“]”匹配,“{”跟“}”匹配,则继续扫描剩下的字符串。如果扫描的过程中,遇到不能配对的右括号,或者栈中没有数据,则说明为非法格式。当所有的括号都扫描完成之后,如果栈为空,则说明字符串为合法格式;否则,说明有未匹配的左括号,为非法格式。

栈实现浏览器前进后退

我们使用两个栈,X 和 Y,我们把首次浏览的页面依次压入栈 X,当点击后退按钮时,再依次从栈 X 中出栈,并将出栈的数据依次放入栈 Y。当我们点击前进按钮时,我们依次从栈 Y 中取出数据,放入栈 X 中。当栈 X 中没有数据时,那就说明没有页面可以继续后退浏览了。当栈 Y 中没有数据,那就说明没有页面可以点击前进按钮浏览了

C++实现栈
typedef struct
{   
    StackDataType * elem;
    int top;
    int size;
    int increment;
}SqStack;

//初始化栈
Status initStack(SqStack &s, int size, int inc );
//销毁栈
Status destoryStack(SqStack &s);
//判断栈是否为空
Status emptyStack(SqStack &s);
//清空栈
Status clearStack(SqStack &s);
//将元素e压栈
Status pushStack(SqStack &s, StackDataType e);
//取出栈顶元素,用e返回
Status getTopStack(SqStack &s, StackDataType &e);
//出栈
Status popStack(SqStack &s, StackDataType &e);
//初始化栈
Status initStack(SqStack &S, int size, int inc )
{
    S.elem = (StackDataType *)malloc(size * sizeof(StackDataType));
    if(NULL == S.elem)
        return OVERFLOW;
    S.top = 0;
    S.size = size;
    S.increment = inc;
    return OK;
}
//销毁栈
Status destoryStack(SqStack &S)
{
    free(S.elem);
    S.elem = NULL;
    return OK;
}
//判断栈是否为空
Status emptyStack(SqStack &S)
{
    if(0 == S.top)
        return TRUE;
    return FALSE;
}
//清空栈
Status clearStack(SqStack &S)
{
    if(0 != S.top)
    {
        S.size = 0;
        S.top = 0;
    } 
    return OK;
}
//将元素e压栈
Status pushStack(SqStack &S, StackDataType e)
{
    StackDataType * newBase;
    if(S.top >= S.size)
    {
        newBase = (StackDataType *)realloc(S.elem, (S.size + S.increment));
        if(NULL == newBase) return OVERFLOW;
        S.elem = newBase;
        S.size += S.increment;
    }
    S.elem[S.top++] = e;
    return OK;
}
//取出栈顶元素,用e返回
Status getTopStack(SqStack &S, StackDataType &e)
{
    if(0 == S.top) return OVERFLOW;
    e = S.elem[S.top - 1];
    return OK;
}
//出栈
Status popStack(SqStack &S, StackDataType &e)
{
    if(0 == S.top) return OVERFLOW;
    e = S.elem[S.top - 1];
    S.top--;
    return OK;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值