05、数据结构与算法---栈与队列

多调试代码,调试才能自己发现各种问题😆


一、栈的理解与实现

1、理解

栈的存放数据形式比较像弹夹

2、实现

java 复制代码
package structure;

import java.util.Arrays;
import java.util.Stack;

public class MyStack {

    public int[] array1;
    public int count;

    public MyStack(){
    this.array1 = new int[2];
    }


    public void push(int val){
        if(isFull()){
            array1 = Arrays.copyOf(array1,array1.length * 2);
        }
        array1[count] = val;
        count++;
    }

    public boolean isFull(){
        return count == array1.length;
    }

    public boolean isEmpty(){
        return count == 0;
    }


    public int pop(){

        if (isEmpty()){
            return -1;
        }
        count--;
        return array1[count];

    }

    public int peek(){
        if (isEmpty()){
            return -1;
        }
        return array1[count-1];
    }

    public int size(){
        return count;
    }

    public static void main(String[] args) {
        MyStack s = new MyStack();

        s.push(1);
        s.push(2);
        s.push(3);
        s.push(4);
        s.push(5);

        System.out.println(s.peek());
        System.out.println(s.pop());
        System.out.println(s.peek());
        System.out.println(s.count);

    }

}

二、栈的练习题目

1、验证栈序列

1、主要逻辑

①在栈中放入pushed里的元素,同时判断栈顶元素跟j下标元素是否一样,一样则弹出栈顶元素,j++继续往下走

②如果不一样,观察pushed数组中是否还有元素,有则继续放,无则不是可能的出栈序列(此时栈有元素)

③注意:栈s不能为空,以及极端情况下j == poped.length的情况


2、图解


3、代码演示

java 复制代码
  //验证栈序列
    public boolean validateStackSequences(int[] pushed, int[] popped) {
        Stack<Integer> s = new Stack<>();
        int i = 0;
        int j = 0;
        for(;i < pushed.length;i++){
            s.push(pushed[i]);
            //栈为空则下次可能调用peek失败
            while(!s.empty() && j < popped.length && s.peek() == popped[j]){
                s.pop();
                j++;
            }
        }
        return s.empty();
    }

    public static void main(String[] args) {
        MyStack s = new MyStack();

        System.out.println(s.validateStackSequences(new int[]{1,2,3,4,5},new int[]{4,5,3,2,1}));
    }

}

2、有效的括号

1、主要逻辑

将字符串一个个分开,将左括号放入栈,空的直接判false;不是左括号则开始peek验证是否对应,对应的话就将放进去的左括号pop出去,否则就是右括号没有对应的左括号;最后栈中仍有元素,依旧为false


2、图解


3、代码演示

java 复制代码
//括号匹配
    public boolean isValid(String s) {

        Stack<Character> s2 = new Stack<>();

        for (int i = 0; i < s.length(); i++) {
            //接收字符串i下标的字符
            char ch1 = s.charAt(i);

           //压入左括号
           if (ch1 == '(' || ch1 == '[' || ch1 == '{'){
                s2.push(ch1);
           }else if(s2.empty()){
               return false;
           }else {
               char ch2 = s2.peek();
               if (ch2 == '(' && ch1 == ')' || ch2 == '[' && ch1 == ']' || ch2 == '{' && ch1 == '}') {
                   s2.pop();
               }else {
                   return false;
               }
           }
        }

        return s2.empty();
    }
    
    public static void main(String[] args) {
        MyStack s = new MyStack();

        System.out.println(s.isValid("([])"));
        System.out.println(s.isValid("]"));
        System.out.println(s.isValid("(])"));

    }

3、逆波兰表达式

1、主要逻辑

判断是否为运算符,是则压入栈,不是直接弹出俩元素进行运算

注意:①这俩元素的次序不能乱,应当为下图一右一左的顺序

②进行完运算后再弹回栈

最后返回栈顶元素即为最终运算结果


2、图解


3、代码演示

java 复制代码
 //逆波兰表达式
    public int evalRPN(String[] tokens) {

        Stack<Integer> stack = new Stack<>();

        for (String s : tokens) {
            //不是运算符就压入到栈
            if (!isOperation(s)) {
                int x = Integer.parseInt(s);
                stack.push(x);
            } else {

                int num2 = stack.pop();
                int num1 = stack.pop();

                //结果返回到栈
                switch (s) {
                    case "+":
                        stack.push(num1 + num2);
                        break;
                    case "-":
                        stack.push(num1 - num2);
                        break;
                    case "*":
                        stack.push(num1 * num2);
                        break;
                    case "/":
                        stack.push(num1 / num2);
                        break;
                }

            }

        }
        //返回栈顶元素
        return stack.peek();
    }

    //判断是否为运算符方法
    public boolean isOperation(String s) {
        if (s.equals("+") || s.equals("-") || s.equals("*") || s.equals("/")) {
            return true;
        }
        return false;
    }

4、最小栈

1、主要逻辑

我们需要两个栈,一个普通栈,一个最小栈

普通栈不断push输入的元素,

最小栈为空时,将第一个元素在两个栈各push一次;

将要push的元素与最小栈第一个元素比较,如果前者更小则push到最小栈,否则最小栈push自己栈内当前最小的元素

注意:pop时为了不影响次序,两个栈同时pop


2、图解


3、代码演示

java 复制代码
package structure;

import java.util.Stack;

class MinStack {

    private Stack<Integer> stack;
    private Stack<Integer> minStack;

    public MinStack() {
        this.stack = new Stack<>();
        this.minStack = new Stack<>();
    }

    public void push(int x) {
        stack.push(x);

        if(minStack.empty()){
            minStack.push(x);
        } else if(x <= minStack.peek()){
            minStack.push(x);
        }else {//否则push当前的最小值
            minStack.push(minStack.peek());
        }
    }

    public void pop() {
        stack.pop();
        minStack.pop();
    }

    public int top() {
        return stack.peek();
    }

    public int getMin() {
        return minStack.peek();
    }

    public static void main(String[] args) {
        MinStack m = new MinStack();
        m.push(-2);
        m.push(0);
        m.push(-3);

        System.out.println(m.getMin());
        m.pop();
        System.out.println(m.getMin());

    }
}

三、队列的理解与实现

1、理解

队列像是一个管道,尾进头出,也就是先进去的先出


2、第一种实现(双向链表模式)

java 复制代码
public class MyQueue {

    private Node head;
    private Node last;
    private int size;

    //结点类
    private static class Node{
        private int val;
        private Node next;
        private Node prev;

        public Node(){
        }
        public Node(int val){
            this.val = val;
        }
    }

    // 1、入队:在尾部添加元素
    public void enqueue(int val) {
        // 调用双向链表的 addLast()
        Node n = new Node(val);
        if(head == null){
            head = n;
            last = n;
        }else {
            last.next = n;
            n.prev = last;
            last = last.next;
        }
        size++;
    }

    // 2、出队:移除并返回头部元素的值
    public int dequeue() {
        // 调用双向链表的 removeFirst()
        // 空队列时抛出异常或返回-1
        if (head == null){
            return -1;
        }

        //存储头结点的值,要不然删完head就空了
        int headVal = head.val;
        if(head.next == null){
            head = null;
            last = null;
        }else {
            head = head.next;
            head.prev = null;
        }
        size--;
        return headVal;
    }

        // 3. 查看队首:只返回不删除
    public int peek() {
        // 调用双向链表的 getFirst() 或 first.element
        // 空队列时返回-1
        if (head == null){
            return -1;
        }
        return head.val;
    }


    // 4、判断是否为空
    public boolean isEmpty() {
        // 调用双向链表的 size() == 0
        return size == 0;
    }

    // 5、获取队列大小
    public int size() {
        // 调用双向链表的 size()
        return size;
    }

    // 6、清空队列
    public void clear() {
        // 调用双向链表的 clear()
        head = null;
        last = null;
        size = 0;
    }


    public static void main(String[] args) {

        MyQueue m = new MyQueue();
        
    }

}

3、第二种实现(数组实现的循环队列模式)

5、实现循环队列

java 复制代码
class MyCircularQueue {

    public int rear;
    public int[] array;
    public int front;

    // 构造方法,初始化一个容量为 k+1 的循环队列
    public MyCircularQueue(int k) {
        //保留一个位置
        this.array = new int[k+1];
    }

    // 入队操作,将一个元素添加到队尾
    public boolean enQueue(int value) {
        if(isFull()){
            return false;
        }else {
            array[rear] = value;
            //循环写法实现空间利用
            rear = (rear+1) % array.length;
            return true;
        }
    }

    // 出队操作,移除队首元素
    public boolean deQueue() {
        if (isEmpty()){
            return false;
        }
        //同理
        front = (front+1) % array.length;
        return true;
    }

    // 获取队首元素的值(不删除)
    public int Front() {
        if (isEmpty()){
            return -1;
        }
       return array[front];
    }

    // 获取队尾元素的值(不删除)
    public int Rear() {
        if (isEmpty()){
            return -1;
        }
        //直接去写rear-1,rear为0时可能就错了
        int index = (rear==0)?array.length-1:rear-1;
        return array[index];
    }

    // 判断队列是否为空
    public boolean isEmpty() {
        return rear == front;
    }

    // 判断队列是否已满
    public boolean isFull() {
        if ((rear+1)%array.length == front){
            return true;
        }else {
            return false;
        }
    }

}

四、队列的练习题目

6、用队列实现栈

java 复制代码
package structure;

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

//使用两个队列实现栈
public class MyQStack {

    private Queue<Integer> q1;
    private Queue<Integer> q2;

    public MyQStack() {
        this.q1 = new LinkedList<>();
        this.q2 = new LinkedList<>();
    }

    public void push(int x) {
        if(!q1.isEmpty()){
            q1.offer(x);
        } else if(!q2.isEmpty()){
            q2.offer(x);
        } else {
            q1.offer(x);
        }
    }

    public int pop() {
        if(empty()) {
            return -1;
        }

        if (!q1.isEmpty()) {
            int size = q1.size();
            while (size -1 != 0) {
                q2.offer(q1.poll());
                size--;
            }
            //取出并且删除
            return q1.poll();
        } else if (!q2.isEmpty()) {
            int size = q2.size();
            while (size - 1 != 0) {
                q1.offer(q2.poll());
                size--;
            }
            //取出并且删除
            return q2.poll();
        }
        //走不到这里的
        return -1;
    }

    public int top() {

        if(empty()) {
            return -1;
        }

        if (!q1.isEmpty()) {
            int size = q1.size();
            while (size -1 != 0) {
                q2.offer(q1.poll());
                size--;
            }
            //取出后放到q2
            int tmp = q1.poll();
            q2.offer(tmp);
            return tmp;

        } else if (!q2.isEmpty()) {
            int size = q2.size();
            while (size -1 != 0) {
                q1.offer(q2.poll());
                size--;
            }
            //取出后放到q1
            int tmp = q2.poll();
            q1.offer(tmp);
            return tmp;
        }
        //走不到这里的
        return -1;
    }

    public boolean empty() {
        return q1.isEmpty()&&q2.isEmpty();
    }

    public static void main(String[] args) {
        MyQStack stack = new MyQStack();

        stack.push(1);
        stack.push(2);
        System.out.println(stack.top());    // 2
        System.out.println(stack.pop());    // 2
        System.out.println(stack.empty());  // false
    }

}

7、用栈实现队列(这个就很简单了)😊

java 复制代码
package structure;

import java.util.Deque;
import java.util.LinkedList;
import java.util.Queue;
import java.util.Stack;

//用栈实现队列
public class MySQueue {

    //建议使用Deque,官方推荐
    private Deque<Integer> s1;
    private Deque<Integer> s2;

    public MySQueue() {
        this.s1 = new LinkedList<>();
        this.s2 = new LinkedList<>();
    }

    public void push(int x) {
        s1.push(x);
    }

    public int pop() {
        if (empty()){
            return -1;
        }
        if(s2.isEmpty()){
//            int size = s1.size();
//            while (size != 0 ){
//                s2.push(s1.pop());
//                size--;
//            }
            while (!s1.isEmpty()){
                s2.push(s1.pop());
            }
        }
        return s2.pop();
    }

    public int peek() {
        if(empty()){
            return -1;
        }
        if(s2.isEmpty()){
//            int size = s1.size();
//            while (size != 0 ){
//                s2.push(s1.pop());
//                size--;
//            }
            //上面写法太冗杂
            while (!s1.isEmpty()){
                s2.push(s1.pop());
            }
        }
        return s2.peek();
    }

    public boolean empty() {
        return s1.isEmpty() && s2.isEmpty();
    }
    
    public static void main(String[] args) {
        MySQueue queue = new MySQueue();

        queue.push(1);
        queue.push(2);
        System.out.println(queue.peek());   // 1
        System.out.println(queue.pop());    // 1
        System.out.println(queue.empty());  // false
    }

}

本章完

相关推荐
無限進步D2 小时前
蓝桥杯赛后总结
算法·蓝桥杯·竞赛
QuZero2 小时前
ReentrantLock principle
java·算法
m0_716765232 小时前
数据结构--顺序表的插入、删除、查找详解
c语言·开发语言·数据结构·c++·学习·算法·visual studio
Jasmine_llq2 小时前
《B3954 [GESP202403 二级] 乘法问题》
算法·顺序输入输出算法·布尔标记算法·累乘算法·循环迭代算法·阈值判断算法·条件分支输出算法
zjshuster2 小时前
流程引擎(Process Engine)简介
java·数据库·servlet
Halo_tjn2 小时前
Java 抽象类 知识点
java·开发语言·算法
say_fall2 小时前
滑动窗口算法
数据结构·c++·算法
落羽的落羽2 小时前
【算法札记】练习 | Week1
linux·服务器·c++·人工智能·python·算法·机器学习
qq_454245032 小时前
图数据标准化与智能去重框架:设计与实现解析
数据结构·架构·c#·图论