stack(栈)数据结构详解

栈(stack)的那些事

1.1栈的简介

栈作为一种限制性访问数据结构,提供了push()压入和pop()弹出的方法,具有后进先出的特点,使得程序不容易出错。 关于栈的实现,输入的入栈和出栈的时间复杂度为O(1)。操作时间不依赖总体栈中的个数,因此操作时间非常快。

1.2 栈的Java实现

这里用底层数组用Object主要为了能存储不同对象,默认初始化数组大小为10,当push时候会先判断是否需要扩大数组容量。pop()时候会先取出数据,之后再删除。

public class ArrayStack {
    //存储元素的数组,声明为Object类型能存储任意类型的数据
    private Object[] elementData;
    //指向栈顶的指针
    private int top;
    //栈的总容量
    private int size;
     
     
    //默认构造一个容量为10的栈
    public ArrayStack(){
        this.elementData = new Object[10];
        this.top = -1;
        this.size = 10;
    }
     
    public ArrayStack(int initialCapacity){
        if(initialCapacity < 0){
            throw new IllegalArgumentException("栈初始容量不能小于0: "+initialCapacity);
        }
        this.elementData = new Object[initialCapacity];
        this.top = -1;
        this.size = initialCapacity;
    }
     
     
    //压入元素
    public Object push(Object item){
        //是否需要扩容
        isGrow(top+1);
        elementData[++top] = item;
        return item;
    }
     
    //弹出栈顶元素
    public Object pop(){
        Object obj = peek();
        remove(top);
        return obj;
    }
     
    //获取栈顶元素
    public Object peek(){
        if(top == -1){
            throw new EmptyStackException();
        }
        return elementData[top];
    }
    //判断栈是否为空
    public boolean isEmpty(){
        return (top == -1);
    }
     
    //删除栈顶元素
    public void remove(int top){
        //栈顶元素置为null
        elementData[top] = null;
        this.top--;
    }
     
    /**
     * 是否需要扩容,如果需要,则扩大一倍并返回true,不需要则返回false
     * @param minCapacity
     * @return
     */
    public boolean isGrow(int minCapacity){
        int oldCapacity = size;
        //如果当前元素压入栈之后总容量大于前面定义的容量,则需要扩容
        if(minCapacity >= oldCapacity){
            //定义扩大之后栈的总容量
            int newCapacity = 0;
            //栈容量扩大两倍(左移一位)看是否超过int类型所表示的最大范围
            if((oldCapacity<<1) - Integer.MAX_VALUE >0){
                newCapacity = Integer.MAX_VALUE;
            }else{
                newCapacity = (oldCapacity<<1);//左移一位,相当于*2
            }
            this.size = newCapacity;
            int[] newArray = new int[size];
            elementData = Arrays.copyOf(elementData, size);
            return true;
        }else{
            return false;
        }
    }
     
     
 
}

复制代码

1.3 应用--解析运算符

运算表4+3 * 2 如何运算?
机器从左到右的读取数据,第一次读到+运算符时候,继续读下一个数据,判断是否有优先级比+高,当读到*到最后时候,的优先级最高,这时候两边操作数先计算出结果。表达式变为4+6 ,又开始从左到右开始读。 从中我们能发现,当公式长的时候,这种方法就会变得非常慢了。 之所以会变得这么慢,主要还是我们写的公式机器读不懂,这样我们就把问题引导到了数据的存储问题上了,关于表达式我们定义了三种类型,以4+3 - 2为例子
前缀表达式:操作符在操作数前面 比如:+ - 432
中缀表达式:操作符在操作数中间,也就是4+3 -2
后缀表达式:操作符在操作数之后 ,比如: 4 3 +2-
相比较中缀表达式,前缀表达式和后缀表达式,两个比较容易读取,那么接下来只要将中缀表达式转换为后缀表达式或者是前缀表达式,问题就可以解决了。本文以后缀表达式为例子:



以中缀表达式4+3 * 2转换为后缀表达式为例:

当前扫描的元素a2(栈顶->低)a1(栈顶->低)说明
44遇到操作数直接结果a2中
+4+a1为空的,直接进入
34,3+遇到操作数直接进入a2中
*4,3+,*a1中栈顶+遇到优先级高的*自动加入
24,3,2+,*遇到操作数直接放到a2中
结束4 3 2 * +最后将a1中栈顶依次弹入a2中

此时我们得到了后缀表达式43 2 * +,机器读取的后缀表达式规则:

后缀表达式计算机求值
1、从左向有扫描
2、遇到数字,压入栈中
3、遇到运算符,弹出栈的两个数,并用运算符对这两个数相应计算,并将结构入栈
4、重复上述2、3步骤,知道表达式的最右端,最后的值即为表达式的结果

1.4 应用--深度优于搜索算法(DFS)

其实在图数据结构的深度优于搜索算法本质就是应用栈实现的, 深度优先搜索算法有如下规则:

  规则1:如果可能,访问一个邻接的未访问顶点,标记它,并将它放入栈中。

  规则2:当不能执行规则 1 时,如果栈不为空,就从栈中弹出一个顶点。

  规则3:如果不能执行规则 1 和规则 2 时,就完成了整个搜索过程。

基于上述图,应用栈来说明:
首先从1节点开始,查找2节点,此时第2节点比邻是3,5,将2弹入栈顶。选中第3节点,此时栈是[1,2,3] ,但是第3节点没有比邻了,那么将3弹出栈顶,回到了第2节点,因为第3节点已经被选过了,选中第5节点,此时栈是[1,2,5]...,依照规则,最后栈的结果[1,2,3,4,6,8,9]

转载于:https://juejin.im/post/5cc2ad88f265da038c021a31

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值