一文读懂栈

独立站原文

定义

栈是一种特殊的线性表,只可以在线性表的底部进行插入和删除操作.

线性表的头部被称为栈底,底部被成为栈顶,也就是说栈只可以在栈顶进行插入或者删除操作

插入操作被称为压栈(push)
在这里插入图片描述

删除操作被叫做弹栈(pop).
在这里插入图片描述

抽象数据类型

数据

/**
 * 栈抽象数据类型
 *
 * @param <T>
 */
public class ArrayStack<T> implements IStack<T> {
    /**
     * 栈是基于线性表来做的
     */
    protected Object[] list;
    /**
     * 栈顶
     */
    protected int top;
    /**
     * 方向 0 正向 1 负向
     */
    protected int direction;

    public ArrayStack() {
        this.init();
    }

    @Override
    public void init() {
        top = 0;
        list = new Object[100];
        direction = 0;
    }

    @Override
    public boolean destroyStack() {
        list = null;
        return true;
    }

    @Override
    public boolean clearStack() {
        if (direction == 0) {
            for (int i = 0; i <= top; i++) {
                list[i] = null;
            }
        } else if (direction == 1) {
            for (int i = list.length - 1; i >= top; i--) {
                list[i] = null;
            }
        }
        top = direction == 0 ? 0 : 99;
        return true;
    }

    @Override
    public boolean stackEmpty() {
        return list == null || (top == 0 && direction == 0) || (top == list.length - 1 && direction == 1);
    }

    @Override
    @SuppressWarnings("unchecked")
    public T getTop() {
        if (this.stackLength() == 0){
            return null;
        }
        return (T) list[direction == 0 ? top - 1 : top + 1];
    }

    @Override
    @SuppressWarnings("unchecked")
    public T pop() {
        int topIndex = direction == 0 ? top - 1 : top + 1;
        Object topObj = list[topIndex];
        list[topIndex] = null;
        if (direction == 0) {
            top--;
        } else {
            top++;
        }
        return (T) topObj;
    }

    @Override
    public void push(T e) {
        if (top + 1 > list.length || top < 0) {
            throw new OutOfRangeException("stack push error");
        }

        int next = direction == 0 ? top++ : top--;
        if (list[next] != null) {
            throw new OutOfRangeException("stack push error");
        }
        list[next] = e;
    }

    @Override
    public int stackLength() {
        return direction == 0 ? top : list.length - top - 1;
    }

    @Override
    @SuppressWarnings("unchecked")
    public List<T> getList() {
        if (null == list)return null;
        return top == 0 ? new ArrayList<>() : new ArrayList<>(Arrays.copyOf(list, top));
    }
}

行为

IStack

/**
 * 栈
 */
public interface IStack<T> {
    /**
     * 初始化
     */
    void init();

    /**
     * 销毁栈
     */
    boolean destroyStack();

    /**
     * 清空栈
     */
    boolean clearStack();

    /**
     * 栈是否为空
     *
     * @return true 空 false 非空
     */
    boolean stackEmpty();

    /**
     * 若栈非空,返回栈顶元素
     *
     * @return T
     */
    T getTop();

    /**
     * 弹栈
     *
     * @return 栈顶元素
     */
    T pop();

    /**
     * 压栈
     *
     * @param e 元素
     */
    void push(T e);

    /**
     * 返回栈长度
     *
     * @return int
     */
    int stackLength();

    /**
     * 获取栈存储list(非标准中包含该方法)
     * @return list
     */
    List<T> getList();
}

IShardedStack.java

public interface IShardedStack<T> extends IStack<T>{
    /**
     * 初始化
     */
    void init();

    /**
     * 销毁栈
     */
    void destroyStack(int num);

    /**
     * 清空栈
     */
    void clearStack(int num);

    /**
     * 栈是否为空
     *
     * @return true 空 false 非空
     */
    boolean stackEmpty(int num);

    /**
     * 若栈非空,返回栈顶元素
     *
     * @return T
     */
    T getTop(int num);

    /**
     * 弹栈
     *
     * @return 栈顶元素
     */
    T pop(int num);

    /**
     * 压栈
     *
     * @param e 元素
     */
    void push(T e,int num);

    /**
     * 返回栈长度
     *
     * @return int
     */
    int stackLength(int num);
}

栈的种类

数组栈和链表栈

因为栈是特殊的线性表,所以很自然的就想到存在数组和链表两种实现方式。
个人觉得数组这种方式特别合适,因为我们限定了所有操作都要在栈顶,而链表方式会多一些额外开销。

共享栈

共享栈就是两个栈分享一个数组/节点链表,栈顶由单个栈的时候的数组最大值,变成了两个栈顶"发生碰撞"
在这里插入图片描述

栈的应用

递归

递归的典型运用场景就是斐波那契数列

斐波那契数列: 前两项数的和 f(0) = 0; f(1) = 1; f(n) = f(n-1) + f(n-2);

普通的执行方法如果我们要算f(20),那么要从 f(0) f(1) f(2)一步步计算到f(20);

使用递归的化我们是从f(20)开始分解公式,f(20) = f(19) + f(18);再去调用f(18) 和 f(19) 依次往下自己调用自己。

在这个依次向下的调用过程我们就需要 “先将上层的公式存起来” 先存起来的公式后算,这和栈结构不谋而合,而在java中执行方法的压栈和出栈也确实是这样的。

四则运算法则

我们常见的表达式 9+(3-1)×3+10÷2 叫做中缀表达式,我们可以很轻松的依据优先级 “ 括号 大于 乘除 大于 加减” 来算出结果

但是计算机在处理中缀表达式的时候就很难办,于是 波兰的一位逻辑学家 想出来了一个后缀(逆波兰)表达式,
而上述中缀表达式转换后未 9 3 1 - 3 * + 10 2 / +

中缀表达式转后缀表达式

中缀表达式提取后缀表达的基本步骤:

  • 依次读取表达式中的每一项 符号和数字 ,读取到数字则放到后缀表达式中,读取到符号则放入计算符号栈中
  • 如果栈顶 无符号 或者 优先级小于 当前要放入的符号,则直接放入
  • 如果放入的符号 优先级大于当前符号则取出栈中的所有符号加入表达式 再放入符号
  • 如果放入的符号是) 则取出符号直到(为止 加入表达式 然后再放入符号
后缀表达式的计算逻辑
  • 依次将每个表达式的元素放入到计算栈中
  • 如果获取到计算符号则从栈中取出两个数进行计算,之后再放入到栈中
  • 最后计算结束取出栈中数据即可
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

小贾Coder

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值