【算法与数据结构复习】| 队列和栈-02

今天跟着左程云算法课015和016,实现【最小栈】和【双端队列】。都是力扣上的mid题,是队列和栈复习的后半部分。

一、最小栈的实现

力扣题目和测试链接:https://leetcode.cn/problems/min-stack/

        这个题目要求在常数时间内检索到最小元素的栈,那么我们是不可以遍历的。要实现常数时间内返回最小元素值,可以用两个栈来实现。用两个栈data和min,data用来存放数据,min栈用来同步存放当前最小值。核心思想为:当数据入栈时,判断当前数据val是否大于min栈的栈顶,若大于等于则data.push(val),min.push(ming.peek());若小于则data.push(val),min.push(val)。数据出栈时,data和min栈一起出栈。

        可以用Java内部的Stack来实现,也可以用数组实现。

(1)Stack实现最小栈

        只需要在push的时候遵循思路即可,其他都非常简单。不过使用Stack的常数时间会比数组要慢。用Stack实现最小栈的代码如下:

	//用Java内部的Stack来实现
	public class MinStack{
		public Stack<Integer> data;
		public Stack<Integer> min;
		
		public MinStack() {
			data=new Stack<Integer>();
			min =new Stack<Integer>();
		}
		
		public void push(int val) {
			//当min栈为空
			if(min.isEmpty()) {
				data.push(val);
				min.push(val);
			}
			else {
				int num=min.peek();
				if(val>num) {
					min.push(num);
					data.push(val);
				}
				else {
					min.push(val);
					data.push(val);
				}
			}
		}
		
		public void pop() {
			data.pop();
			min.pop();
		}
		
		public int top() {
			return data.peek();
		}
		
		
		public int getMin() {
			return min.peek();
		}
		
	}
(2)用数组实现最小栈

        核心思想和上面一样,只是需要一个size来做数组栈的栈顶指针,size指向栈顶的后一个位置。核心思路是不变的,代码实现如下:

//用数组实现最小栈	
public class MyStack2{
		public int MAX=8001;
		public int[] data;
		public int[] min;
		public int size;
		
		public MyStack2() {
			data=new int[MAX];
			min=new int[MAX];
			size=0;
		}
		
		public void push(int val) {
			//data和min栈都为空
			if(size==0) {
				data[size]=val;
				min[size]=val;
				size++;
			}
			else {
				int num=min[size-1];
				if(val>num) {
					data[size]=val;
					min[size]=num;
					size++;
				}
				else {
					data[size]=val;
					min[size]=val;
					size++;
				}
			}
		}
		
		public void pop() {
			if(size==0) {
				return;
			}
			else {
				size--;
			}
		}
		
		public int top() {
				return data[size-1];
		}
		
		public int getMin() {
				return min[size-1];
		}
		
		
	}

二、循环双端队列的实现

力扣题目和测试链接:. - 力扣(LeetCode)

        设计双端循环队列,也就是说原来的单向队列是前进后出,那么现在双端队列就是两头都可以进出,而队列中的元素要保持一定的次序。实现循环双端队列可以使用双向链表数组两种存储结构,和上面最小栈的实现相似,使用Java内部的双向链表的常数时间会慢一些,使用数组会快一点。

(1)双向链表(Deque)实现循环双端队列

        使用Java内部的双向链表LinkedList,前面在实现队列的时候使用过,这里使用的接口Deque,可以进行双头操作。具体区别在于:Queue和Deque是Java中两个不同的接口,Deque可以进行双端操作(offerFirst,offerLast,pollFirst,pollLast,peekFirst,peekLast),而Queue不能进行双端操作(只能调用offer,poll和peek)。

//队列接口
public Queue<Integer> queue=new LinkedList<Integer>();
//双端队列接口
public Deque<Integer> deque=new LinkedList<Integer>();

        那么使用java内部的双向链表就非常简单了,直接调用Deque接口的函数就可以,代码如下:

	public class MyCircularDeque{
		
		public Deque<Integer> deque=new LinkedList<Integer>();
		public int size;
		public int limit;
		
		public MyCircularDeque(int k) {
			size=0;
			limit=k;
		}
		
		public boolean isEmpty() {
			return size==0?true:false;
		}
		
		public boolean isFull() {
			return size==limit?true:false;
		}
		
		public boolean insertFront(int n) {
			if(isFull()) {
				return false;
			}
			else {
				deque.offerFirst(n);
				size++;
				return true;
			}
		}
		
		public boolean insertLast(int n) {
			if(isFull()) {
				return false;
			}
			else {
				deque.offerLast(n);
				size++;
				return true;
			}
		}
		
		public boolean deleteFront() {
			if(isEmpty()) {
				return false;
			}
			else {
				deque.pollFirst();
				size--;
				return true;
			}
		}
		
		public boolean deleteLast() {
			if(isEmpty()) {
				return false;
			}
			else {
				deque.pollLast();
				size--;
				return true;
			}
		}
		
		public int getFront() {
			if(isEmpty()) {
				return -1;
			}
			else {
				return deque.peekFirst();
			}
		}
		
		public int getRear() {
			if(isEmpty()) {
				return -1;
			}
			else {
				return deque.peekLast();
			}
		}
		
	}
(2)数组实现循环双端队列

        其实循环双端队列本质上是循环队列,在上半部分我们实现过循环队列,用size来判空和满,设置两个指针l和r,区别在于循环队列中,加入数据是r端操作,删除数据是l端操作;而双端循环队列的l端和r端都有加入和删除数据,看是从哪一边操作。要注意l和r与0和limit-1的边界关系。

核心思想在于:

头入:   l==0:arr[limit-1],l=limit-1;         l!=0:arr[l-1],l=l-1;

头出:   l==limit-1:l=0;                              l!=limit-1:l=l+1;

尾进:   r==limit-1:arr[0],r=0;                 r!=limit-1:r=r+1;

尾出:   r==0:r=limit-1;                             r!=0:r=r-1;

        那么在具体实现的时候,也是我踩过的坑。在insert数据时要根据size判空,若队列当前为空,则需要将l和r都置为0;这是为什么呢?因为l指向队头数据,r指向队尾数据,他们都指向数据本身。假如队列里有两个数据3和4,l指向3,r指向4,进行一次头出,一次尾出之后,队列为空,但是此时r指向l前面。这一点是需要特别注意的。代码实现如下:

	public class MyCircularDeque1{
		public int[] arr;
		public int size;
		public int limit;
		public int l;
		public int r;
		
		public MyCircularDeque1(int k) {
			limit=k;
			l=0;
			r=0;
			size=0;
			arr=new int[k];
		}
		
		public boolean isEmpty() {
			return size==0?true:false;
		}
		
		public boolean isFull() {
			return size==limit?true:false;
		}
		
		public boolean insertFront(int val) {
			//若队列为满
			if(isFull()) {
				return false;
			}
			
			//若队列为空
			if(isEmpty()) {
				l=0;
				r=0;
				arr[l]=val;
				size++;
				return true;
			}
			//若队列不为空
			else {
				if(l==0) {
					arr[limit-1]=val;
					l=limit-1;
					size++;
					return true;
				}
				else {
					arr[l-1]=val;
					l=l-1;
					size++;
					return true;
				}
			}
		}
		
		public boolean insertLast(int val) {
			//若队列为满
			if(isFull()) {
				return false;
			}
			//若队列为空
			if(isEmpty()) {
				l=0;
				r=0;
				arr[r]=val;
				size++;
				return true;
			}
			//若队列不为空
			else {
				if(r==limit-1) {
					arr[0]=val;
					r=0;
					size++;
					return true;
				}
				else{
					arr[r+1]=val;
					r++;
					size++;
					return true;
				}
			}
		}
		
		public boolean deleteFront() {
			//若队列为空
			if(isEmpty()) {
				return false;
			}
			//若队列不为空
			else {
				if(l==limit-1) {
					l=0;
					size--;
					return true;
				}
				else {
					l++;
					size--;
					return true;
				}
			}
		}
		
		public boolean deleteLast() {
			//若队列为空
			if(isEmpty()) {
				return false;
			}
			//若队列不为空
			else {
				if(r==0) {
					r=limit-1;
					size--;
					return true;
				}
				else {
					r--;
					size--;
					return true;
				}
			}
		}
		
		public int getFront() {
			if(isEmpty()) {
				return -1;
			}
			else {
				return arr[l];
			}
		}
		
		public int getRear() {
			if(isEmpty()) {
				return -1;
			}
			else {
				return arr[r];
			}
		}
		
	}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值