栈和队列
栈
1、栈和栈的应用:撤销操作和系统栈
基础:栈也是一种线性结构;相比数组,栈对应的操作是数组的子集;只能从栈顶添加或取出元素。;
后进先出(LIFO)。
应用:
- Undo操作(撤销)
- 程序调用的系统栈
2、栈的实现:
Stack
- void push(E)
- E pop()
- E peek()
- int getSize()
- boolean isEmpty()
结合Array动态数组实现栈:ArrayStack,重写Array类中的getSize()、isEmpty等方法。
push:addLast() ; pop:removeLast() ; peek:getLast()
时间复杂度都是O(1)
@Override
public int getSize() {
return array.getSize();
}
public boolean isEmpty() {
return array.isEmpty();
}
public int getCapacity() {
return array.getCapacity();
}
@Override
public void push(E e) {
array.addLast(e);
}
@Override
public E pop() {
return array.removeLast();
}
@Override
public E peek() {
return array.getLast();
}
3、栈的应用:括号匹配
左括号依次入栈,遇到有括号时与栈顶元素匹配,若匹配成功,则左括号出栈。
栈顶元素反应了在嵌套的层次关系中,最近的需要匹配的元素。
import java.util.Stack;
public class Solution {
public boolean isValid(String s) {
Stack<Character> stack=new Stack<>();
for(int i=0;i<s.length();i++) {
char c=s.charAt(i);
if(c=='('||c=='['||c=='{') {
stack.push(c);
}else {
if(stack.isEmpty())
return false;
char topChar=stack.pop();
if(c==')'&&topChar!='(')
return false;
if(c==']'&&topChar!='[')
return false;
if(c=='}'&&topChar!='{')
return false;
}
}
return stack.isEmpty();
}
}
4、关于LeetCode
若要使用自己定义的接口,可以把自己定义的类和接口当做内部类。
队列
1、先进先出
队列也是一种线性结构,从一端放入(队尾)从另一端(队首)取出。
队尾是数组末尾。
实现:Queue
- void enqueue
- E dequeue()
- E getFront()
- int getSize()
- boolean isEmpty()
复用Array实现队列
public class ArrayQueue<E> implements Queue<E> {
private Array<E> array;
public ArrayQueue(int capacity) {
array=new Array<E>(capacity);
}
public ArrayQueue() {
array=new Array<E>();
}
@Override
public int getSize() {
return array.getSize();
}
@Override
public boolean isEmpty() {
return array.isEmpty();
}
public int getCapacity() {
return array.getCapacity();
}
@Override
public void enqueue(E e) {
array.addLast(e);//O(1)
}
@Override
public E dequeue() {
return array.removeFirst();//O(n)
}
@Override
public E getFront() {
return array.getFirst();
}
@Override
public String toString() {
StringBuilder res=new StringBuilder();
res.append("Queue:");
res.append("front[");
for(int i=0;i<array.getSize();i++) {
res.append(array.get(i));
if(i!=array.getSize()-1) {
res.append(",");
}
}
res.append("]tail");
return res.toString();
}
}
2、循环队列
删除队首元素时,所有元素都要向前挪动一个位置,这就导致时间复杂度变成O(n)。能否不让所有元素移动,而是将front指向新的队首元素。----循环队列。
循环队列:tail指向下一个入队元素的位置。front指向新的队首元素。
front==tail队列为空
入队:tail++;(tail+1)%c //c指数组长度
出队:front++
队满:(tail+1)%c==front
实现:
借助数组,capacity是队列能存的数据量;data.length是数组大小,比capacity多1;size是队列中实际数据量的大小。
public class LoopQueue<E> implements Queue<E> {
private E[] data;
private int front,tail;
private int size;
public LoopQueue(int capacity) {
data=(E[])new Object[capacity+1];
front=0;
tail=0;
size=0;
}
public LoopQueue() {
this(10);
}
public int getCapacity() {
return data.length-1;//真实容量要比数组长度少一个(有意设计)。
}
@Override
public boolean isEmpty() {
return front==tail;
}
@Override
public int getSize() {
return data.size;
}
@Override
public void enqueue(E e) {
if((tail+1)%data.length==front) {//队列满时resize队列
resize(getCapacity()*2);
}
data[tail]=e;
tail=(tail+1)%data.length;//tail指向下一个进入队列的元素位置
size++;
}
public void resize(int newCapacity) {
E[] newData=(E[])new Object[newCapacity+1];
for(int i=0;i<size;i++) {
newData[i]=data[(front+i)%data.length];//!!!
}
data=newData;
front=0;
tail=size;
}
@Override
public E dequeue() {
if(isEmpty()) {
throw new IllegalArgumentException("cannot dequeue from an empty queue.");
}
E ret=data[front];
data[front]=null;
front=(front+1)%data.length;// front前进一位
size--;
if(size==getCapacity()/4&&getCapacity()/2!=0) {
resize(getCapacity()/2);
}
return ret;
}
@Override
public E getFront() {
if(isEmpty()) {
throw new IllegalArgumentException("cannot get front from an empty queue.");
}
return data[front];
}
public String toString() {
StringBuilder res=new StringBuilder();
res.append(String.format("Queue:size=%d,capacity=%d\n", size,getCapacity()));
res.append("front [");
for(int i=front;i!=tail;i=(i+1)%data.length) {
res.append(data[i]);
if((i+1)%data.length!=tail)
res.append(",");
}
res.append("]tail");
return res.toString();
}
public static void main(String[] args) {
LoopQueue<Integer> queue=new LoopQueue<Integer>();
for(int i=0;i<10;i++) {
queue.enqueue(i);
System.out.println(queue);
if(i%3==2) {
queue.dequeue();
System.out.println(queue);
}
}
}
}
3、循环队列复杂度分析
循环队列使出队时间复杂度变成O(1)。所有的操作时间复杂度均为O(1)
测试队列操作运行时间:
private static double testQueue(Queue<Integer> q,int opCount) {
long startTime=System.nanoTime();
Random random=new Random();
for(int i=0;i<opCount;i++) {
q.enqueue(random.nextInt(Integer.MAX_VALUE));
}
for(int i=0;i<opCount;i++) {
q.dequeue();
}
long endTime=System.nanoTime();
return (endTime-startTime)/1000000000.0;
}
ArrayQueue:2.6441648s
LoopQueue:0.0082098s