数据结构【二】-栈

一。栈和栈的应用 撤销操作和系统栈


1. 简介
    1.1 栈Stack
        (1)栈是一种线性结构
        (2)相比数组,栈对应的操作是数组操作的子集。
        (3)栈的本质就是一个数组,我们把数据排开来放,但是规定:
            添加元素的时候只能从一端添加元素,也只能从一端取出元素。
            这一段通常称为“栈顶”
    1.2 添加数据的规则
        (1) 相当于在在一个桶中添加数据。这个操作叫入栈。
        (2) 第一个数据在栈的底部
        (3) 取出元素的时候只能取最顶部的元素。
        (4) 最后一个数据在栈顶
    1.3 栈的特质
        (1) 栈是一种后进先出的数据结构[LIFO](Last In First Out)
        (2) 计算机的世界中,栈拥有着不可思议的作用
2.栈和栈的应用    
    2.1 无处不在的Undo操作(撤销)
        (1) 实质:向栈中推入元素和向栈顶中取出元素。
        (2) 例如: 
            a. 输入“沉迷”     (bottom [沉迷] top)
            b. 输入“学习”     (bottom [沉迷 学习] top)
            c. 输入“不法”     (bottom [沉迷 学习 不法] top)
            d. 撤销输入“不法”  (bottom [沉迷 学习] top)
                --从编辑器的栈中拿出栈顶的元素
                --通过栈顶的元素确定最近的操作是什么
                --发现是最近的操作是输入了“不法”,这次撤销操作就是删除“不法”
                --做完撤销后,输入“不法”这个动作也不需要被栈保留。所以就出栈了
    2.2 栈的应用:程序调用的系统栈

        (1)例如:
            func A(){*,B(),*}
            func B(){*,C(),*}
            func C(){*,*,*}
        (2) 运行顺序
            a. 开始执行A()函数,执行第一行,第二行
            b. 在执行A()的第二行的时候,要调用B()子程序,暂时中断A()的执行.
                栈:  (bottom [A(2)] top)
            c. 开始执行B()函数,执行第一行,第二行
            d. 在执行B()的第二行的时候,要调用C()子程序,暂时中断B()的执行.
                栈:  (bottom [A(2) B(2)] top)
            e. 开始执行C()函数,执行第一行,第二行,第三行。执行完毕。
            f. 对于系统栈来说,栈顶的元素是B(2).计算机就知道了:当时是执行B()到第二行中断了,中断跳到C(),但是此时C()已经执行完毕了,就可以跳回到B(2)的位置。这样,系统栈就成功给计算机找到了上一次执行的位置。
            g. 回到B(2)继续执行,记录的B(2)也就没用了,它就可以出栈了。
                栈: (bottom [A(2)] top)
            h. 开始执行C(),第三行。执行完毕。
            i. 查看栈顶元素为A(2) .....[f,g]
                栈: (bottom [ ] top)
            j. 开始执行A(),第三行。执行完毕。
            k. 执完之后计算机又没有什么可执行了,这个时候查看系统栈为空。说明已经没有中断执行到一半的调用过程了。计算机就知道了,整个过程就已经执行完了。
        (3) 总结
            这就是在我们编程进行子过程调用的时候,当一个子过程执行完成之后,可以自动的回到上层调用中断的位置继续执行下去的背后原因。因为有一个系统栈来记录每一次调用过程中[中断]的调用点。
            也就是:子过程子逻辑的调用在我们的编译器内部运行实现的机理。


二。栈的实现


1. 需要实现的方法--Stack<E>
    具体的底层实现用户不关心。而且实际底层有多种实现方法。
    1.1 Void push(E) 向栈中添加一个元素(入栈) -- O(1):即使有resize,也是O(1)
    1.2 E pop()  从栈中取出栈顶的元素
    1.3 E peek()  查看栈顶的元素是什么
    1.4 int getSize()  
    1.5 boolean isEmpty()
2. 这个代码设计的思想
    为了让整个程序的逻辑更加清晰,同时也是为了支持面向对象的一些特性。比如说:多态。
    2.1 设计一个结构Stack<E>,定义了以上5种操作
    2.2 用ArrayStack<E>实现Stack<E>,具体实现操作。
    2.3 使用写好的数据ArrayDemo类中的方法。
3. 时间复杂度分析
    所有的都是O(1)的
4. 代码实现
    4.1 接口
  

  public interface Stack<E> {
        int getSize();
        boolean isEmpty();
        void push(E e);
        E pop();
        E peek();
    }


    4.2 底层实现
 

   public class ArrayStack<E> implements Stack {
        ArrayDemo<E> array;
        public ArrayStack(int capacity){
            array = new ArrayDemo<E>(capacity);
        }
        public ArrayStack(){
            array = new ArrayDemo<E>();
        }    
        @Override
        public int getSize() {
            return array.getSize();
        }
        @Override
        public boolean isEmpty() {
            return array.isEmpty();
        }
        //对于我们的底层是实现是一个静态数组,所以用户可能需要查看数组容积。getCapacity()不是接口的一部分,是因为Stack接口是和栈的具体实现无关的,只有在我们使用动态数组来实现栈的时候,才存在容积整个概念。
        public int getCapacity(){
            return array.getCapacity();
        }
        public void push(Object e) {
            array.addLast((E)e);
        }
        @Override
        public E pop() {
            return array.removeLast();
        }
        @Override
        public E peek() {
            return array.getLast();                
        }
        @Override
        public String toString(){
            StringBuilder res = new StringBuilder("Stack: [");
            for(int i=0;i<array.getSize();i++){
                res.append(array.get(i));
                if(i!=getSize()-1){
                    res.append(", ");
                }            
            }
            res.append("] top");
            return res.toString();
        }

    }    


    4.3 应用

    public class ArrayStackTest {
        public static void main(String[] args) {
            ArrayStack<Integer> stack = new ArrayStack<Integer>();
            for(int i=0;i<5;i++){
                stack.push(i);
                System.err.println(stack);
            }
            stack.pop();
            System.err.println(stack);
        }
    

}


三。 栈的应用:括号匹配


1. LeetCode
    1.1 介绍
        面向IT公司面试的在线平台。可以判断代码逻辑是否正确
    1.2 中英文版本
            中文 : https://leetcode-cn.com/
            英文 : https://leetcode.com/
    1.3 比较
        (1)题目数量
        (2)Explore里面的内容更多
        (3)Contest(竞赛) 每周一次
        (4)Articles有大量的文章
        (5)**Problems:某一个问题的Discuss功能
2. 括号匹配
    2.1 题目
        给定一个只包括'(', ')', '{', '}', '[', ']'的字符串,判断字符串是否有效,括号必须正确匹配。
    2.2 解答
        给定字符串"{[( ) ]}"
            (1)我们逐一遍历字符串中的每个字符,如果是左括号,就将之压住栈
                Stack 【{ [ ( 】
            (2)如果遇到的字符是右括号,查看栈顶的元素是否可以和右括号进行匹配。
            当前字符是')',我们就查看栈顶元素是不是左侧的'(',是的话')'就匹配成功,那么栈顶的'('就可以出栈了。
            (3)---(2)
            (4)---(2)
            (5)这个时候整个字符串已经遍历完了,但是此时栈是空的。说明当前字符串是合法的。
        给定字符串"{[ }]"
    2.3 结论
        栈顶元素反映了在嵌套的层次关系中,最近的需要匹配的元素。
    2.4 代码

        public boolean isVaild(String s){
            Stack<Character> stack = new Stack<Character>();
            for(int i=0;i<s.length();i++){
                char c = s.charAt(i);
                if(c=='(' || c=='[' || c=='{'){
                    stack.push(c);
                }else{
                    if(stack.isEmpty())
                        return false;
                        char topChar = stack.pop();
                    if(c ==')' && topChar !=')'){
                        return false;
                    }
                    if(c==']' && topChar!='['){
                        return false;
                    }
                    if(c=='}' && topChar!='{'){
                        return false;
                    }
                }
            
            }
            return stack.isEmpty();        
        }
    

以上所有内容都是通过"慕课网"听"liuyubobobo"的《玩转数据结构》课程后总结

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值