栈的与队列专题

用堆栈之前,我们首先介绍标准模板库中堆栈模板
通过对堆栈模板的使用,可以使我们跳过对堆栈具体实现的编码
而专注于堆栈在程序中的应用 (有兴趣可以查看源码,或者以后我会对数据结构实现继续写文章,请持续关注,本篇的内容在于竞赛和面试场上迅速入手运用)
import java.util.Stack;

  • Stack< integer > stack;创建一个内部元素是integer型的栈
  • stack.push(i);入栈
  • stack.pop();出栈
  • stack.peek();查看栈顶元素但是不移除,类似于c++ stack.top();但是c++中这个操作会弹出栈顶元素
  • stack.empty() 栈是否为空

了解基本概念后 我们尝试一下基本用法。

用法一 检验括号的匹配

入门

import java.util.Stack;

public class StackTest {
    public static void main(String[] args) {
        Stack<Integer> stack=new Stack<Integer>();
        stack.push(1);
        stack.push(2);
        stack.push(3);
        System.out.println(stack.peek());//显示栈顶元素 不弹出
        printStack(stack);
        System.out.println(stack.peek());//显示栈顶元素 不弹出 栈为空,故报错
    }
    //打印并弹出栈内所有元素
    public static void printStack(Stack stack){
        while (!stack.empty()) {
            System.out.println(stack.pop());
        }
    }
}

题目一:括号匹配问题

长度不超过100的字符串中有左括号右括号大小写字母。每一个左括号都
从内向外与它右边而且距离最近的括号做匹配。
输出
第一行原字符串
第二行无法匹配的右括号下面?左括号 下面 $ 其余输出为空
样例输入

)(rttyy())sss)(

样例输出

)(rttyy())sss)(
?           ? $ 

import java.util.Scanner;
import java.util.Stack;

public class 括号匹配问题 {
    public static void main(String[] args) {
//      Scanner input=new Scanner(System.in);
//      char[] str=input.nextLine().toCharArray();
        char[] str=")(rttyy())ss)(".toCharArray();
        char[] ans=new char[str.length+1];
        Stack<Character> stack=new Stack<Character>();
        for (int i = 0; i < str.length; i++) {
            //遇到左括号进栈
            if (str[i]=='(') {
                stack.push('(');
                ans[i]=' ';
            }else if (str[i]==')') {
                //遇到右括号出栈,如果站不为空那么证明匹配输出位置对应空
                if (!stack.empty()) {
                    stack.pop();
                    ans[i]=' ';
                }else {//右括号不匹配,在右括号下面填?
                    ans[i]='?';
                }
            }else {//其他字符

                ans[i]=' ';
            }
        }
        //为不匹配的左括号赋值
        for (int i = 0; i < ans.length; i++) {
            if (ans[i]==' '||ans[i]=='?') {
                System.out.print(ans[i]);               
            }else {
                System.out.print('$');
            }
        }
        System.out.println();
    }

}

c++版本

  • 头文件 #include 引入预处理
  • stack stack;创建一个内部元素是int型的栈
  • stack.push(i);入栈
  • stack.pop();出栈
  • stack.top();读取并弹出栈顶元素 java中stack.peek();是只读不弹
  • stack.empty() 栈是否为空
#include<cstdio> 
#include<stack> 
using namespace std;
stack<int> s;
char str[110];
char ans[110]; 
int main(){
    scanf("%s",str);

    int i;
        for (i = 0; i < str[i]!=0; i++) {
            if (str[i]=='(') {
                s.push('(');
                ans[i]=' ';
            }else if (str[i]==')') {
                if (!s.empty()) {
                    s.pop();
                    ans[i]=' ';
                }else {
                    ans[i]='?';
                }
            }else {
                ans[i]=' ';
            }
        }

        while(!s.empty()){
            ans[s.top()]='$';
            s.pop();
        }
        ans[i]=0;//为了使输出形成字符串,在其最后一个字符后面加一个空字符 
        puts(str);  
        puts(ans);
    return 0;
}

题目二 网易面试题 爱编程的小易

爱编程的小易发现,当自己代码中的括号较多时,如果括号未
成对出现,或者出现的顺序错误,他的编辑器 总是能立马给出
错误提示。好奇的小易决定自己尝试实现该功能。 对于一行代
码(字符串),里面可能出现大括号”{}”、中括号”[]”和小括号
“()”,请编程判断该行代码的括号嵌 套是否正确。
“()”,”({})”,”print (‘Hello Netease’)”等都是括号的正确使
用方法,”(]”,”print (Hello Netease]”则是错误的范例

import java.util.Stack;

public class Valid_Expression {

    public static boolean isValid(String expression) {
        char[] chs = expression.toCharArray();
        Stack<Character> stack = new Stack<>();
        for (int i = 0; i < chs.length; i++) {
            if (chs[i] == '{' || chs[i] == '[' || chs[i] == '(') {
                stack.add(chs[i]);
            }
            if (chs[i] == '}' || chs[i] == ']' || chs[i] == ')') {
                char match = chs[i] == '}' ? '{' : (chs[i] == ']' ? '[' : '(');
                if (stack.isEmpty() || !stack.pop().equals(match)) {
                    return false;
                }
            }
        }
        return stack.isEmpty();
    }

    public static void main(String[] args) {
        String test = "{1+(2+3)+[(1+3)+(4*5)]}";
        System.out.println(isValid(test));
    }

}

栈的常见用法还包括前中后缀表达式的运算,这里仅及介绍中缀转后缀表达式并运算

题目三:计算中缀表达式的值

输入一个只包含+ - * / ()的非负数计算表达式,计算该表达式的值
样例输入:

(1+2)*4/8+(3+2)*4/8

样例输出:

4.0

注意:题目说的仅是非负数的运算,故没有考虑减号当做负号的情况

/*
    操作步骤

 (1)当读到一个操作数时,立即将它放到输出中。操作符则不立即输出,放入栈中。遇到左圆括号也推入栈中

 (2)如果遇到一个右括号,那么就将栈元素弹出,将符号写出直到遇到一个对应的左括号。但是这个左括号只被弹出,并不输出

 (3)在读到操作符时,如果此时栈顶操作符优先性大于或等于此操作符,弹出栈顶操作符直到发现优先级更低的元素位置。除了处理)的时候,否则决不从栈中移走”(”。操作符中,+-优先级最低,()优先级最高

 (4)如果读到输入的末尾,将栈元素弹出直到该栈变成空栈,将符号写到输出中

 (5)上述四个原则便可将中缀表达式转化成后缀表达式,然后对后缀表达式进行处理得到最终结果<具体方式:利用一个数据栈,将后缀表达式的数据依次入栈,遇到符号时则将栈中弹出两个数据,然后利用该符号进行计算,将计算结果再入栈,从此往复>
 */
import java.util.Scanner;
import java.util.Stack;
public class 中缀表达式求值 {

    public static void main(String[] args) {
        // Scanner scan=new Scanner(System.in);
        // String string=scan.nextLine();
        String string = "(1+2)*4/8+(3+2)*4/8";//

        char[] arr1 = string.toCharArray();

        // 下面程序将中缀表达式转化成后缀表达式<重点>
        char a, b;
        int k = 0;
        char[] arr2 = new char[100];
        Stack<Character> stackchar = new Stack<Character>();
        Stack<Double> stackvalue = new Stack<Double>();
        for (int i = 0; i < string.length(); i++) {
            a = arr1[i];
            if (a >= '0' && a <= '9') {
                arr2[k++] = a;
            } else if (a == '*' || a == '/' || a == '(') {
                stackchar.push(a);
            } else if (a == ')') {
                b = stackchar.pop();
                while (b != '(') {
                    arr2[k++] = b;
                    b = stackchar.pop();
                }
            } else if (a == '+' || a == '-') {
                if (stackchar.isEmpty())
                    stackchar.push(a);
                else {
                    do {
                        b = stackchar.pop();
                        if (b == '(')
                            stackchar.push(b);
                        else
                            arr2[k++] = b;
                    } while (!stackchar.isEmpty() && b != '(');
                    stackchar.push(a);
                }
            }
        }

        while (!stackchar.isEmpty()) {
            b = stackchar.pop();
            arr2[k++] = b;
        }

        // 利用后缀表达式和一个整型数据栈计算出结果
        double data1, data2, data = 0;
        for (int i = 0; i < k; i++) {
            char ch = arr2[i];
            if (ch >= '0' && ch <= '9') {
                stackvalue.push(Double.parseDouble(ch + ""));// 也可以用String.valueOf(ch);
            } else {
                data1 = stackvalue.pop();
                data2 = stackvalue.pop();
                if (ch == '+') {
                    data = data1 + data2;
                    stackvalue.push(data);
                } else if (ch == '-') {
                    data = data2 - data1;
                    stackvalue.push(data);
                } else if (ch == '*') {
                    data = data1 * data2;
                    stackvalue.push(data);
                } else if (ch == '/') {
                    if (data1 == 0) {
                        System.out.println("除数为0!");
                        System.exit(0);// 等价于return;
                    } else {
                        data = data2 / data1;
                        stackvalue.push(data);
                    }
                }
            }
        }

        data = stackvalue.pop();
        System.out.println("Result=" + data);
    }
}

队列

  • import java.util.Queue;
  • import java.util.LinkedList;
    注意:队列的使用不能像Stack一样直接new。它是一个接口,实现的时候可以用接口回调的方法创建

  • Queue< Integer> qmax =new LinkedList< Integer>();

  • 也可以直接用LinkedList new出来
  • LinkedList< Integer> qmax = new LinkedList< Integer>();
import java.util.LinkedList;
import java.util.Queue;

public class QueueTest {

    public static void main(String[] args) {
        Queue<Integer> queue=new LinkedList<Integer>();
        queue.add(1);//也叫enQueue操作
        queue.add(2);
        queue.add(3);
        System.out.println(queue.peek());//查看队头元素
        printQueue(queue);
        System.out.println(queue.peek());
    }
    public static void printQueue(Queue queue) {
        while (!queue.isEmpty()) {
            System.out.println(queue.poll());
        }
    }
}

双向队列 这种常用一点,适配性高

import java.util.LinkedList;
import java.util.Queue;

public class QueueTest {

    public static void main(String[] args) {
        //其实LinkedList也实现了 Deque双向队列接口
        LinkedList<Integer> queue=new LinkedList<Integer>();

        queue.add(1);//入队
        //queue.add(2,1);指定位置上加 这个LinkedList相当于一个链表结构,这里当队列处理,不记它链表的特性
        //queue.set(index,element)//将此列表中指定位置的元素替换为指定的元素。链表的特性
        System.out.println("size:"+queue.size());
        queue.addFirst(2);//队首加元素
        queue.addLast(3);//队尾加元素
        System.out.println("size:"+queue.size());
        printQueue(queue);

        queue.peek();//获取但不移除队头
//      queue.peekFirst();
//      queue.peekLast();
        queue.poll();//获取队头,队头出队

    }
    public static void printQueue(Queue queue) {
        while (!queue.isEmpty()) {
            System.out.println(queue.poll());
        }
    }
}

题目一:滑动窗口最大值

给定一个数组和滑动窗口的大小,找出所有滑动窗口里数值的最大值。例如,如果输入数组{2,3,4,2,6,2,5,1}及滑动窗口的大小3,
那么一共存在6个滑动窗口,他们的最大值分别为{4,4,6,6,6,5};
如果新来的值比队列尾部的数小,那就追加到后面,因为它可能在前面的最大值划出窗口后成为最大值
如果新来的值比尾部的大(或者等于),那就删掉尾部(因为有更大的在后面,所以它不会成为最大值,划出也是它先划出,不影响最大值),再追加到后面,循环下去直到小于
如果追加的值比的索引跟队列头部的值的索引超过窗口大小,那就删掉头部的值
其实这样每次队列的头都是最大的那个

import java.util.LinkedList;
public class SlidingWindowMaxArray {

    public static int[] getMaxWindow(int[] arr, int w) {
        if (arr == null || w < 1 || arr.length < w) {
            return null;
        }
        LinkedList<Integer> qmax = new LinkedList<Integer>();
        int[] res = new int[arr.length - w + 1];
        int index = 0;
        for (int i = 0; i < arr.length; i++) {
            while (!qmax.isEmpty() && arr[qmax.peekLast()] <= arr[i]) {
                qmax.pollLast();
            }
            qmax.addLast(i);
            if (qmax.peekFirst() == i - w) {
                qmax.pollFirst();
            }
            if (i >= w - 1) {
                res[index++] = arr[qmax.peekFirst()];
            }
        }
        return res;
    }

    // for test
    public static void printArray(int[] arr) {
        for (int i = 0; i != arr.length; i++) {
            System.out.print(arr[i] + " ");
        }
        System.out.println();
    }

    public static void main(String[] args) {
        int[] arr = { 4, 3, 5, 4, 3, 3, 6, 7 };
        int w = 3;
        printArray(getMaxWindow(arr, w));

    }

}

栈和队列综合

题目一:用数组结构实现大小固定的队列和栈


public class Array_To_Stack_Queue {
    //用一个index下标记位置只对index下标的值操作,天然实现
    public static class ArrayStack {
        private Integer[] arr;
        private Integer size;

        public ArrayStack(int initSize) {
            if (initSize < 0) {
                throw new IllegalArgumentException("The init size is less than 0");
            }
            arr = new Integer[initSize];
            size = 0;
        }

        public Integer peek() {
            if (size == 0) {
                return null;
            }
            return arr[size - 1];
        }

        public void push(int obj) {
            if (size == arr.length) {
                throw new ArrayIndexOutOfBoundsException("The queue is full");
            }
            arr[size++] = obj;
        }

        public Integer pop() {
            if (size == 0) {
                throw new ArrayIndexOutOfBoundsException("The queue is empty");
            }
            return arr[--size];
        }
    }
    /*
     * 这里用size对end和start做了解耦
     * end-->新进的数放的位置 
     * start-->出队的数从哪拿
     * 优于那种start追end的结构,多一个空余位置判断是否满,更容易实现也容易理解
     * size!=0的时候能拿数(pop)
     * size!=length的时候能入队(push)
     * 在已经满的队列push的时候,end位置从数组末尾跳到数组开头
     */
    public static class ArrayQueue {
        private Integer[] arr;
        private Integer size;
        private Integer first;
        private Integer last;

        public ArrayQueue(int initSize) {
            if (initSize < 0) {
                throw new IllegalArgumentException("The init size is less than 0");
            }
            arr = new Integer[initSize];
            size = 0;
            first = 0;
            last = 0;
        }

        public Integer peek() {
            if (size == 0) {
                return null;
            }
            return arr[first];
        }
        //入队 size++
        public void push(int obj) {
            if (size == arr.length) {
                throw new ArrayIndexOutOfBoundsException("The queue is full");
            }
            size++;
            arr[last] = obj;
            last = last == arr.length - 1 ? 0 : last + 1;
        }
        //出队size--
        public Integer poll() {
            if (size == 0) {
                throw new ArrayIndexOutOfBoundsException("The queue is empty");
            }
            size--;
            int tmp = first;
            first = first == arr.length - 1 ? 0 : first + 1;
            return arr[tmp];
        }
    }

    public static void main(String[] args) {

    }

}

题目二:实现一个特殊的栈,在实现栈的基本功能的基础上,再实现返回栈中最小元素的操作。

【要求】
1.pop、push、getMin操作的时间复杂度都是O(1)。
2.设计的栈类型可以使用现成的栈结构

/*
 * 修改基本栈
 * 加一个最小数栈
 * 入栈   3 2 4 1 5
 * 最小栈3 2 2 1 1 //同步压 同步弹 压入的时候,最小栈每次压入的是值栈中存在的最小数
 * 
 * 实现方法2 同样加入一个最小数栈
 * 入栈   3 2 4 1 5
 * 最小栈3 2 1 //压入的时候,最小栈每次只把小于等于最小栈栈顶的元素同步压入 弹出时候比较值栈和最小栈栈顶元素是否相同,相同就同步弹出
 * 
 * peek函数返回栈顶元素
 */
import java.util.Stack;

public class Code_02_GetMinStack {
    public static class MyStack1 {
        private Stack<Integer> stackData;
        private Stack<Integer> stackMin;

        public MyStack1() {
            this.stackData = new Stack<Integer>();
            this.stackMin = new Stack<Integer>();
        }

        public void push(int newNum) {
            if (this.stackMin.isEmpty()) {
                this.stackMin.push(newNum);
            } else if (newNum <= this.getmin()) {
                this.stackMin.push(newNum);
            }
            this.stackData.push(newNum);
        }

        public int pop() {
            if (this.stackData.isEmpty()) {
                throw new RuntimeException("Your stack is empty.");
            }
            int value = this.stackData.pop();
            if (value == this.getmin()) {
                this.stackMin.pop();
            }
            return value;
        }

        public int getmin() {
            if (this.stackMin.isEmpty()) {
                throw new RuntimeException("Your stack is empty.");
            }
            return this.stackMin.peek();
        }
    }

    public static class MyStack2 {
        private Stack<Integer> stackData;
        private Stack<Integer> stackMin;

        public MyStack2() {
            this.stackData = new Stack<Integer>();
            this.stackMin = new Stack<Integer>();
        }

        public void push(int newNum) {
            if (this.stackMin.isEmpty()) {
                this.stackMin.push(newNum);
            } else if (newNum < this.getmin()) {
                this.stackMin.push(newNum);
            } else {
                int newMin = this.stackMin.peek();
                this.stackMin.push(newMin);
            }
            this.stackData.push(newNum);
        }

        public int pop() {
            if (this.stackData.isEmpty()) {
                throw new RuntimeException("Your stack is empty.");
            }
            this.stackMin.pop();
            return this.stackData.pop();
        }

        public int getmin() {
            if (this.stackMin.isEmpty()) {
                throw new RuntimeException("Your stack is empty.");
            }
            return this.stackMin.peek();
        }
    }

    public static void main(String[] args) {
        MyStack1 stack1 = new MyStack1();
        stack1.push(3);
        System.out.println(stack1.getmin());
        stack1.push(4);
        System.out.println(stack1.getmin());
        stack1.push(1);
        System.out.println(stack1.getmin());
        System.out.println(stack1.pop());
        System.out.println(stack1.getmin());

        System.out.println("=============");

        MyStack1 stack2 = new MyStack1();
        stack2.push(3);
        System.out.println(stack2.getmin());
        stack2.push(4);
        System.out.println(stack2.getmin());
        stack2.push(1);
        System.out.println(stack2.getmin());
        System.out.println(stack2.pop());
        System.out.println(stack2.getmin());
    }

}

题目三:如何仅用队列结构实现栈结构?

import java.util.LinkedList;
import java.util.Queue;

public class StackAndQueueConvert {
    /*
     * 用两个栈实现队列
     * 队A 队B
     * 队A 入队 1 2 3 4 5
     * 队A出队填入队B,保留队A最后一个元素 
     * 此时 队A 5
     *    队B 1 2 3 4
     *    队A最后一个数弹出
     *    然后队B填入队A,队B保留最后一个元素
     *    队B最后一个数弹出
     *    然后队A填入队B,队A保留最后一个元素
     *    队A最后一个数弹出
     */
    public static class TwoStacksQueue {
        private Stack<Integer> stackPush;
        private Stack<Integer> stackPop;

        public TwoStacksQueue() {
            stackPush = new Stack<Integer>();
            stackPop = new Stack<Integer>();
        }

        public void push(int pushInt) {
            stackPush.push(pushInt);
        }

        public int poll() {
            if (stackPop.empty() && stackPush.empty()) {
                throw new RuntimeException("Queue is empty!");
            } else if (stackPop.empty()) {
                while (!stackPush.empty()) {
                    stackPop.push(stackPush.pop());
                }
            }
            return stackPop.pop();
        }

        public int peek() {
            if (stackPop.empty() && stackPush.empty()) {
                throw new RuntimeException("Queue is empty!");
            } else if (stackPop.empty()) {
                while (!stackPush.empty()) {
                    stackPop.push(stackPush.pop());
                }
            }
            return stackPop.peek();
        }
    }

}

题目四:如何仅用栈结构实现队列结构?

import java.util.Stack;

public class StackAndQueueConvert {
    /*
     * 用两个队列实现栈
     * push栈和pop栈
     * 压只进push,出只弹pop
     * 原则一 pop栈有东西,push栈不能压入
     * 原则二 push栈往pop栈压入的时候,必须一次性把所有数据压完
     * 只要满足两个原则,可以在任意一个时刻判断和操作
     */
    public static class TwoQueuesStack {
        private Queue<Integer> queue;
        private Queue<Integer> help;

        public TwoQueuesStack() {
            queue = new LinkedList<Integer>();
            help = new LinkedList<Integer>();
        }

        public void push(int pushInt) {
            queue.add(pushInt);
        }

        public int peek() {
            if (queue.isEmpty()) {
                throw new RuntimeException("Stack is empty!");
            }
            while (queue.size() != 1) {
                help.add(queue.poll());
            }
            int res = queue.poll();
            help.add(res);
            swap();
            return res;
        }

        public int pop() {
            if (queue.isEmpty()) {
                throw new RuntimeException("Stack is empty!");
            }
            while (queue.size() != 1) {
                help.add(queue.poll());
            }
            int res = queue.poll();
            swap();
            return res;
        }

        private void swap() {
            Queue<Integer> tmp = help;
            help = queue;
            queue = tmp;
        }

    }

}

关于dfs bfs还会用到栈和队列的知识,在图的章节会继续讨论
第三、四题其实就是面试中会考如何用栈实现bfs如何用队列实现dfs
其实就是用栈和队列相互模仿另一种数据结构。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值