栈和队列

本文介绍了栈和队列这两种基本数据结构。栈遵循后进先出(LIFO)原则,常用于撤销操作和系统调用,可以通过ArrayStack实现,时间复杂度为O(1)。括号匹配是栈的一个典型应用。队列则遵循先进先出(FIFO)原则,可以使用Array实现,循环队列避免了元素移动,提高了操作效率。
摘要由CSDN通过智能技术生成

栈和队列

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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值