文章目录

  • 一 栈和队列(Stack&&Queue)
  • 1 栈(Stack)
  • 2 队列(Queue)
  • 1) 链式队列
  • 2) 顺序队列
  • 3 PriorityQueue
  • 4 栈和队列practice


一 栈和队列(Stack&&Queue)

1 栈(Stack)

栈:一种后进先出(LIFO)的容器。有时也被称作叠加栈。

拥有记忆功能:可以说对进栈的数据进行了保存,等需要时就可以弹出之前进栈的元素。

JavaScript 栈 队列 栈和队列java_Stack

import java.util.Stack;
Stack<Character> stack = new Stack<>();

Java中的栈(Stack):常用方法

方法

说明

E push(E e)

压栈

E pop()

出栈

E peek()

查看栈顶元素

boolean empty()

判断栈是否为空

注意:

  • LinkedList具有能够直接实现栈的所有功能方法,因此也可以直接将LinkedList当作栈使用。

头插入栈(LinkedList):

package com.trs.collection;
 
import java.util.LinkedList;
 
public class Stack<T> {
	private LinkedList<T> storage = new LinkedList<T>();
	public void push(T v) {
		storage.addFirst(v);
	}
	
	public T peek() {
		return storage.getFirst();
	}
	
	public T pop() {
		return storage.removeFirst();
	}
	
	public boolean empty() {
		return storage.isEmpty();
	}
	
	public String  toString() {
		return storage.toString();
	}
	
}

1)顺序栈

数组实现,采用尾插尾删的方式

2) 链式栈

单链表实现,头尾皆可
尾插入栈:O(n) 出栈尾出:O(n)
头插入栈:O(1) 出栈头出: O(1)
使用头插入栈,时间复杂度较低。 因为如果尾插入栈,每次入栈都需要寻找队尾元素,增加了时间复杂度

对于栈,数组实现起来比较简单,这里我们直接用数组实现栈:
数组实现栈:

public class MyStack<T> {

    public T[] elem;
    public int top;//下标

    public MyStack() {
        this.elem = (T[]) new Object[10];
        this.top = 0;
    }
    
    private boolean isFull() {
        return this.top == this.elem.length;
    }
    
    public void push(T val) {
        if(isFull()) {
            return;
        }
        this.elem[this.top] = val;
        this.top++;
    }
    
    private boolean isEmpty() {
        return this.top == 0;
    }
    
    //出栈
    public T pop() {
        if(isEmpty()) {
            return null;
        }
        T tmp = this.elem[top-1];//保存你出栈的数据
        this.top--;//真正的出栈
        return tmp;
    }
    
    //得到栈顶元素,但是不出栈    peek
    public T getTop() {
        if(isEmpty()) {
            return null;
        }
        return this.elem[top-1];
    }
}
2 队列(Queue)

队列:一种典型的先进先出(FIFO)容器。即从容器的一端放,从另一端取。

JavaScript 栈 队列 栈和队列java_java_02


LinkedList提供了方法以支持队列的行为,并且实现了Queue接口,因此LinkedList可以作为Queue的一种实现:

import java.util.Queue;
Queue<Integer> queue = new LinkedList<>();

Java中的队列Queue:常用方法

抛出异常

返回特征值null

入队列

add(e)

offer()

出队列

remove()

poll()

队首元素

element()

peek()

队列可以采用数组链表实现,使用链表的结构更优一些。
如果采用数组的结构,出队列在数组头上出数据,效率会比较低。

1) 链式队列

入队头插O(1) 出队 队尾O(n)
入队尾插:O(n) 出队 队头O(1)
定义一个指向队尾的引用,则能达到入队:O(1) 出队 O(1)

单链表实现队列: 尾插头出

/**
 * 单链表实现队列,带索引
 */
public class MyListQueue {
    static class Node {
        public int data;
        public Node next;

        public Node(int data) {
            this.data = data;
        }
    }
    public Node front;
    public Node rear;
    public int usedSize;

    public boolean isEmpty() {
        return this.usedSize == 0;
    }
    //入队
    public void offer(int data) {
        Node node = new Node(data);
        if(isEmpty()) {
            this.front = node;
            this.rear = front;
        }else {
            this.rear.next = node;
            this.rear = node;
        }
        this.usedSize++;
    }
    public int poll() {
        if(isEmpty()) {
            throw new UnsupportedOperationException("队列为空");
        }
        int val = this.front.data;
        this.front = this.front.next;
        this.usedSize--;
        return val;
    }
    public int peek() {
        if(isEmpty()) {
            throw new UnsupportedOperationException("队列为空");
        }
        return this.front.data;
    }
}
2) 顺序队列

如果采用数组实现队列可以使用循环队列,即通过牺牲一定的空间,来达到时间复杂度的降低。牺牲的空间主要用来区分空队列和队列满了的情况。

编程题:设计循环队列

JavaScript 栈 队列 栈和队列java_java_03


循环数组实现队列: 尾插头出

/**
 * 循环队列
 */
public class MyCircularQueue {

    public int[] elem;
    public int front;
    public int rear;
    public int usedSize;

    /** Initialize your data structure here.
     * Set the size of the queue to be k. */
    public MyCircularQueue(int k) {
        this.elem = new int[k+1];
        //队列长度为k,需要牺牲一个空间,所以要k+1
        this.front = 0;
        this.rear = 0;
        this.usedSize = 0;
    }

    /** Insert an element into the circular queue. Return true if the operation is successful. */
    public boolean enQueue(int value) {
        if(isFull()) {
            return false;
        }
        this.elem[this.rear] = value;
        this.rear = (this.rear+1)%this.elem.length;//防止越界
        this.usedSize++;
        return true;
    }

    /** Delete an element from the circular queue. Return true if the operation is successful. */
    public boolean deQueue() {
        if(isEmpty()) {
            return false;
        }
        this.front = (this.front+1)%this.elem.length;
        this.usedSize--;
        return true;
    }

    /** Get the front item from the queue. */
    public int Front() {
        if(isEmpty()) {
            return -1;
        }
        return this.elem[this.front];
    }

    /** Get the last item from the queue. */
    public int Rear() {
        if(isEmpty()) {
            return -1;
        }
        int index = this.rear == 0 ?
                this.elem.length-1 : this.rear-1;
        return this.elem[index];
    }

    /** Checks whether the circular queue is empty or not. */
    public boolean isEmpty() {
        return this.front == this.rear;
    }

    /** Checks whether the circular queue is full or not. */
    public boolean isFull() {
        return (this.rear+1)%this.elem.length == this.front;
    }
}
3 PriorityQueue

典型的队列规则:先进先出,出队列的下一个元素应该是等待时间最长的元素。

优先级队列规则:出队列的下一个元素应该是最需要的元素(具有最高优先级)

JavaScript 栈 队列 栈和队列java_queue_04


常用方法:

返回特征值null

入队列

offer()

出队列

poll()

队首元素

peek()

当用PriorityQueue的offer()方法来插入一个对象时,这个对象会在队列中被排序,默认的排序使用对象在队列中的自然顺序(Integer,String,Character已经内建了自然顺序),我们可以提供一个Comparator来修改这个顺序。

import java.util.PriorityQueue;
    PriorityQueue<Integer> minHeap = new PriorityQueue<>
            (k, new Comparator<Integer>() {
                @Override
                public int compare(Integer o1, Integer o2) {
                     return o1.compareTo(o2);//默认是小堆
                    //return o2.compareTo(o1);//大堆
                }
            });


4 栈和队列practice

(1)设计一个最小栈

编程题:设计一个最小栈 用一个栈正常进栈出栈,在此基础上再另维护一个记录最小值的栈min。

/**
 * 设计一个最小栈,总能得到最小值,设计一个记录当前栈的最小值的栈
 */
import java.util.Stack;
public class Minstack {
    Stack<Integer> stack = new Stack<>();
    Stack<Integer> min = new Stack<>();
    public int minnum;
    /** initialize your data structure here. */
    public Minstack() {
    }
    public void push(int x) {
        if(stack.empty()){
            stack.push(x);
            min.push(x);
        }else{
            if(x<=min.peek()){
                stack.push(x);
                min.push(x);
            }else{
                stack.push(x);
            }
        }
    }
    public void pop() {
        int top = stack.peek();
        if(top>min.peek()){
            stack.pop();
        }else if(top==min.peek()){
            stack.pop();
            min.pop();
        }
    }
    public int top() {
        return stack.peek();
    }
    public int getMin() {
        return min.peek();
    }
}

注意x<=min.peek()这里是小于等于。

(2)括号匹配问题

编程题:括号匹配 要考虑,左括号多,右括号多及括号匹配错误的情况。

/**
 * 用栈判断有效括号对
 */
import java.util.Stack;

public class bracketMatch {
    public  static boolean isValid(String s) {
        Stack<Character> stack = new Stack<>();
        for (int i = 0; i < s.length(); i++) {
            char ch = s.charAt(i);
            if(ch == '(' || ch == '{'|| ch == '[') {
                stack.push(ch);
            }else {
                if(stack.empty()) {
                    System.out.println("右括号多");
                    return false;
                }
                char top = stack.peek();
                if(top == '(' && ch == ')' ||
                        top == '{' && ch == '}'||
                        top == '[' && ch == ']' ){
                    stack.pop();
                }else {
                    System.out.println("右括号匹配错误!");
                    return false;
                }
            }
        }
        if(!stack.empty()) {
            System.out.println("左括号多!");
            return false;
        }
        return true;
    }
}

注意:
ch和top的左右括号别搞混了。

if(top == '(' && ch == ')' ||
                        top == '{' && ch == '}'||
                        top == '[' && ch == ']' )

(3)两个栈实现一个队列

倾巢出动

编程题:用栈实现队列

JavaScript 栈 队列 栈和队列java_Stack_05


栈1负责入队列,栈2负责出队列。只有当栈2出队列为空时,才将栈1中的所有元素入到栈2中。

import java.util.Stack;
/**
 *两个栈实现队列
 */
public class MyQueue {
    private Stack<Integer> stack1;
    private Stack<Integer> stack2;
    /** Initialize your data structure here. */
    public MyQueue() {
        stack1 = new Stack<>();
        stack2 = new Stack<>();
    }
    /** Push element x to the back of queue. */
    public void push(int x) {
        stack1.push(x);
    }
    /** Removes the element from in front of queue and returns that element. */
    public int pop() {
        if(stack2.empty()) {
            while (!stack1.empty()) {
                stack2.push(stack1.pop());
            }
        }
        if(!stack2.empty()) {
            return stack2.pop();
        }
        return -1;
    }
    /** Get the front element. */
    public int peek() {
        if(stack2.empty()) {
            while (!stack1.empty()) {
                stack2.push(stack1.pop());
            }
        }
        if(!stack2.empty()) {
            return stack2.peek();
        }
        return -1;
    }
    /** Returns whether the queue is empty. */
    public boolean empty() {
        return stack2.empty() && stack1.empty();
    }
}

(3)两个队列实现一个栈

留守一兵

编程题:用队列实现栈

JavaScript 栈 队列 栈和队列java_java_06


两个队列轮回出栈入栈,每次都是不为空的队列作为入栈窗口,出栈时队列1出队列到队列2中,在队列1中保留一个元素作为出栈元素。或者是队列2出队列到队列1,队列2保留一个出栈元素。

import java.util.LinkedList; 
import java.util.Queue;
/**
 * 两个队列实现栈
 */
public class MyStack {
    public Queue<Integer> queue1 = new LinkedList<>();
    public Queue<Integer> queue2 = new LinkedList<>();
    /** Initialize your data structure here. */
    public MyStack() {
    }
    /** Push element x onto stack. */
    public void push(int x) {
        if(!queue1.isEmpty()) {
            queue1.offer(x);
        }else if(!queue2.isEmpty()){
            queue2.offer(x);
        }else {
            queue1.offer(x);
        }
    }
    /** Removes the element on top of the stack and returns that element. */
    public int pop() {
        if(!queue1.isEmpty()) {
            int size1 = queue1.size()-1;
            //因为:queue1.poll()--》queue1.size()发生改变
            for (int i = 0; i < size1; i++) {
                queue2.offer(queue1.poll());
            }
            return queue1.poll();
        }
        if(!queue2.isEmpty()){
            int size2 = queue2.size()-1;
            for (int i = 0; i < size2; i++) {
                queue1.offer(queue2.poll());
            }
            return queue2.poll();
        }
        return -1;
    }
    /** Get the top element. */
    public int top() {
        if(!queue1.isEmpty()) {
            int tmp = 0;
            int size1 = queue1.size();
            for (int i = 0; i < size1; i++) {
                tmp = queue1.poll();
                queue2.offer(tmp);
            }
            return tmp;
        }
        if(!queue2.isEmpty()){
            int tmp2 = 0;
            int size2 = queue2.size();
            for (int i = 0; i < size2; i++) {
                tmp2 = queue2.poll();
                queue1.offer(tmp2);
            }
            return tmp2;
        }
        return -1;
    }
    /** Returns whether the stack is empty. */
    public boolean empty() {
        if(queue1.isEmpty() && queue2.isEmpty()){
            return true;
        }
        return false;
    }
}