文章目录
- 一 栈和队列(Stack&&Queue)
- 1 栈(Stack)
- 2 队列(Queue)
- 1) 链式队列
- 2) 顺序队列
- 3 PriorityQueue
- 4 栈和队列practice
一 栈和队列(Stack&&Queue)
1 栈(Stack)
栈:一种后进先出(LIFO)的容器。有时也被称作叠加栈。
拥有记忆功能:可以说对进栈的数据进行了保存,等需要时就可以弹出之前进栈的元素。
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)容器。即从容器的一端放,从另一端取。
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) 顺序队列
如果采用数组实现队列可以使用循环队列,即通过牺牲一定的空间,来达到时间复杂度的降低。牺牲的空间主要用来区分空队列和队列满了的情况。
编程题:设计循环队列
循环数组实现队列: 尾插头出
/**
* 循环队列
*/
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
典型的队列规则:先进先出,出队列的下一个元素应该是等待时间最长的元素。
优先级队列规则:出队列的下一个元素应该是最需要的元素(具有最高优先级)
常用方法:
返回特征值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)两个栈实现一个队列
倾巢出动
编程题:用栈实现队列
栈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)两个队列实现一个栈
留守一兵
编程题:用队列实现栈
两个队列轮回出栈入栈,每次都是不为空的队列作为入栈窗口,出栈时队列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;
}
}