数据结构栈和队列以及常见算法题

概念:一种特殊的线性表,其只允许在固定的一端进行插入和删除元素操作。进行数据插入和删除操作的一端称为栈顶,另一端称为栈底。栈中的数据元素遵守后进先出LIFO(Last In First Out)的原则。
压栈:栈的插入操作叫做进栈/压栈/入栈,入数据在栈顶。
出栈:栈的删除操作叫做出栈。出数据在栈顶。
在这里插入图片描述

数据结构的栈和jvm运行时内存区域中的stack是啥关系:
方法的调用涉及到栈帧,调用一个方法就压栈,方法执行完就出栈。栈帧中有局部变量表,局部变量表存放的是方法调用过程中的局部变量。

在这里插入图片描述

操作:

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

//如何实现栈
public class StackDemo {
    public static void main(String[] args) {


        //使用stack
//        Stack<String> stack = new Stack<>();
//
//        stack.push("A");
//        stack.push("B");
//        stack.push("C");
//
//        System.out.println(stack.size());   // 3
//        System.out.println(stack.isEmpty());    // false
//        System.out.println(stack.empty());      // false
//        System.out.println(stack);
//
//        System.out.println(stack.peek());   // C
//        System.out.println(stack.peek());   // C
//        System.out.println(stack.pop());    // C
//        System.out.println(stack.pop());    // B

        //使用Deque
        Deque<String> stack = new LinkedList<>();

        stack.push("A");
        stack.push("B");
        stack.push("C");
        System.out.println(stack);
        System.out.println(stack.isEmpty());    // false
        System.out.println(stack.size());   // 3
        System.out.println(stack.peek());   // C
        System.out.println(stack.peek());   // C
        System.out.println(stack.pop());    // C
        System.out.println(stack.pop());    // B
    }
}

题目20:有效的括号

根据括号的匹配原则,选择数据结构为栈。
创建一个空栈,存储内容为字符,将字符串转化为字符数组;
遍历字符数组,如果是左括号就入栈,如果是右括号

  • 如果栈为空,直接返回false
  • 弹出栈顶元素看是否匹配,不匹配就直接返回false;匹配就继续遍历下一个字符
  public boolean isValid(String s) {

        Stack<Character> stack = new Stack<>();
        char [] chars = s.toCharArray();

        for (char ch : chars){
            if (ch == '(' || ch == '[' || ch == '{'){
                stack.push(ch);
            }else {
                if (stack.empty()){
                    return false;
                }
                char left = stack.pop();
                if (left == '(' && ch == ')'){
                    continue;
                }else if (left == '[' && ch == ']'){
                    continue;
                }else if (left == '{' && ch == '}'){
                    continue;
                }else {
                    return false;
                }
            }


        }
        return stack.empty();
    }
题目:剑指offer31栈的压入,弹出序列
	private List<Integer> arrayToList(int []array){
        List<Integer> list = new ArrayList<>(array.length);

        for (int e: array){
            list.add(e);
        }
        return list;
    }
    public boolean validateStackSequences(int[] pushed, int[] popped) {
        List<Integer> pushedList = arrayToList(pushed);

        List<Integer> popedList = arrayToList(popped);

        Stack<Integer> stack = new Stack<>();
        for (int e : pushedList){
            if (!stack.isEmpty() && stack.peek() == e){
                stack.pop();
                continue;
            }
            while (true) {
                if (pushedList.isEmpty()) {
                    return false;
                }
                int f = pushedList.remove(0);
                if (f != e) {
                    stack.push(f);
                } else {
                    break;
                }
            }
        }
        return stack.isEmpty();

    }
题目225:用两个队列实现栈

方法一:
push:
1.选择一个队列(哪个队列中有数据选哪个,都没有就指定一个)
2.把元素加入队列中
pop:
1.选择一个队列(有数据的队列)
2.把该队列中的size - 1个数据移动到另一个队列中
3.把队列剩余的元素出掉
top:
1.选择一个队列(有数据的那个队列)
2.把该队列中的size-1个数据移动到另一个队列中
3.把队列剩余的返回,并且把该元素放入另一个队列中
方法二:
还有一种方法不必使用到两个队列,使用一个队列即可,先将所有元素入队列,如果要取元素就把前size-1个个元素又放回队列,将最后一个出队列,依次这样操作。
方法一的实现:

class MyStack {

private Queue<Integer> queue1 = new LinkedList<>();
    private Queue<Integer> queue2 = new LinkedList<>();

    /** Initialize your data structure here. */
    public MyStack() {
    }

    /** Push element x onto stack. */
    public void push(int x) {
        Queue<Integer> queue;
        if (!queue1.isEmpty()) {
            queue = queue1;
        } else if (!queue2.isEmpty()) {
            queue = queue2;
        } else {
            queue = queue1;
        }

        queue.add(x);
    }

    /** Removes the element on top of the stack and returns that element. */
    public int pop() {
        Queue<Integer> queue;
        Queue<Integer> toQueue;
        if (!queue1.isEmpty()) {
            queue = queue1;
            toQueue = queue2;
        } else {
            queue = queue2;
            toQueue = queue1;
        }

        int size = queue.size();
        for (int i = 0; i < size - 1; i++) {
            int e = queue.remove();
            toQueue.add(e);
        }

        return queue.remove();
    }

    /** Get the top element. */
    public int top() {
        Queue<Integer> queue;
        Queue<Integer> toQueue;
        if (!queue1.isEmpty()) {
            queue = queue1;
            toQueue = queue2;
        } else {
            queue = queue2;
            toQueue = queue1;
        }

        int size = queue.size();
        for (int i = 0; i < size - 1; i++) {
            int e = queue.remove();
            toQueue.add(e);
        }

        int f = queue.remove();
        toQueue.add(f);
        return f;
    }

    /** Returns whether the stack is empty. */
    public boolean empty() {
        return queue1.isEmpty() && queue2.isEmpty();
    }
}

/**
 * Your MyStack object will be instantiated and called as such:
 * MyStack obj = new MyStack();
 * obj.push(x);
 * int param_2 = obj.pop();
 * int param_3 = obj.top();
 * boolean param_4 = obj.empty();
 */
题目155:最小栈

本题要求的时间复杂度是O(1),push操作个peek操作原本就是O(1),获取栈中的最小元素,要么是遍历栈的元素得出最小元素,要么就是创建一个遍历记录,这里我们使用一个栈来进行记录,这样做的好处是利用了入栈和出栈O(1)的时间复杂度。
在这里插入图片描述

push :val入栈普通栈;最小栈:如果val<=最小栈的栈顶元素,那么该元素也入栈最小栈。
为什么这里是<=呢?很多人可能会疑问,如果等于此时最小栈的栈顶元素,那么最小值不是已经在最小栈中存在了吗,为什么还要入栈?因为我们忽略了一种特殊情况:
在这里插入图片描述

pop:普通栈出栈x,如果x==最小栈的栈顶元素那么最小栈才出栈。

class MinStack {
    //正确
    private Stack<Integer> normal = new Stack<>();
    private Stack<Integer> min = new Stack<>();

    /** initialize your data structure here. */
    public MinStack() {
    }

    public void push(int val) {
        normal.push(val);
        if (min.isEmpty() || val <= min.peek()) {
            min.push(val);
        } else {
            min.push(min.peek());
        }
    }

    public void pop() {
        normal.pop();
        min.pop();
    }

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

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

队列

概念:只允许在一端进行插入数据操作,在另一端进行删除数据操作的特殊线性表,具有先进先出的特点。
入队列:进行插入操作的一端称为队尾(Tail/Rear)
出队列:进行删除操作的一端称为队头(Head/Front)
尾插头删
操作:

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

public class QueneDemo {
    public static void main(String[] args) {
        Queue<String> queue = new LinkedList<>();

        queue.add("A");
        queue.add("B");
        queue.add("B");
        System.out.println(queue.isEmpty()); //判断队列是否为空
        System.out.println(queue.size());    //队列的大小
        System.out.println(queue.remove());  //出队
        System.out.println(queue.element()); //查看队首元素

    }
}

题目232:用栈实现队列

在这里插入图片描述
push:将数据放入栈2
pop:如果1号栈空了,把二号栈的所有元素放到一号栈,从栈1出数据
peek:如果栈1中有数据,直接从栈1中取数据,如果栈1没有数据,栈2中有数据就去将栈2中的所有元素放入栈1再从栈1取数据

class MyQueue {
    private Stack<Integer> stack1 = new Stack<>();
    private Stack<Integer> stack2 = new Stack<>();

    /** Initialize your data structure here. */
    public MyQueue() {
    }

    /** Push element x to the back of queue.
     * 将元素 x 推到队列的末尾 */
    public void push(int x) {
        stack2.push(x);
    }

    /** Removes the element from in front of queue and returns that element.
     * 从队列的开头移除并返回元素*/
    public int pop() {
        if (stack1.isEmpty()) {
            // 把 stack2 的全部数据移入 stack1
//            int size = stack2.size();
//            for (int i = 0; i < size; i++) {
//                int e = stack2.pop();
//                stack1.push(e);
//            }

            while (!stack2.isEmpty()) {
                int e = stack2.pop();
                stack1.push(e);
            }
        }

        return stack1.pop();
    }

    /** Get the front element.
     *  返回队列开头的元素*/
    public int peek() {
        if (stack1.isEmpty()) {
            while (!stack2.isEmpty()) {
                int e = stack2.pop();
                stack1.push(e);
            }
        }

        return stack1.peek();
    }

    /** Returns whether the queue is empty.
     * 如果队列为空,返回 true ;否则,返回 false*/
    public boolean empty() {
        return stack1.isEmpty() && stack2.isEmpty();
    }
}
题目622:设计循环队列

在这里插入图片描述
在这里插入图片描述
但是rear==front的时候无法判断队列是空还是满。
为了达到判断循环队列满的条件,牺牲一个空间。
在这里插入图片描述
取队首元素:array[frontIndex]
取队尾元素:array[(rearIndex - 1 + array.length) % array.length]

队满:(rearIndex+1)%array.length == frontIndex
队空:rearIndex = = frontIndex

class MyCircularQueue {
 private int[] array;
    private int frontIndex;
    private int rearIndex;
    private int size;

    public MyCircularQueue(int k) {
        array = new int[k];
        frontIndex = 0;
        rearIndex = 0;
        size = 0;
    }

    //插入
    public boolean enQueue(int value) {
        if (isFull()) {
            return false;
        }

        array[rearIndex] = value;
        rearIndex = (rearIndex + 1) % array.length;
        size++;

        return true;
    }

    //删除
    public boolean deQueue() {
        if (isEmpty()) {
            return false;
        }

        frontIndex = (frontIndex + 1) % array.length;
        size--;
        return true;
    }

    //取队首
    public int Front() {
        if (isEmpty()) {
            return -1;
        }
        return array[frontIndex];
    }

    //
    public int Rear() {
        if (isEmpty()) {
            return -1;
        }

        int index = (rearIndex - 1 + array.length) % array.length;
        return array[index];
    }

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

    public boolean isFull() {
        return size == array.length;
    }
}

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值