初始队列
队列:是一种对存取有要求的数据结构
只能从尾部存入数据,从头部取出数据
遵循先进先出的原则
队列的实现方式:顺序队列(基于数组),链队列(基于链表)
需要两个指针:分布记录队头和队尾的位置
存数据时候,队尾指针向后移动
取数据时候,对头的指针向后移动
当队列为空时候,对头和队尾指针在某个位置重合
1,用数组实现队列
/**
* @ClassName &{NAME}
* @Description TODO
* @Author zhanghao MX8837
* @Date 2021/11/4 10:35
* @Version 1.0
**/
public class ArrayQueue {
//最大容量
int maxCapacity;
//声明头尾指针
int head;
int tail;
//存储元素
int[] arr;
public ArrayQueue(int maxCapacity){
arr = new int[maxCapacity];
maxCapacity = maxCapacity;
head = 0;
tail = 0;
}
public boolean isFull(){
return tail == maxCapacity;
}
public void add(int n){
//先判断队列容量的大小
if(!isFull()){
arr[tail] = n;
}
}
public boolean isEmpty(){
return head == tail;
}
public int get(){
//先判断队列是否为空
int result = arr[head];
return result;
}
}
2,用LinkedList实现
/**
* @ClassName &{NAME}
* @Description TODO
* @Author zhanghao MX8837
* @Date 2021/11/4 10:41
* @Version 1.0
**/
public class LinkedListTest {
public static void main(String[] args) {
Queue
//都能添加元素 offer更健壮友好 区别:当队列慢 add会抛出异常 offer返回false 更健壮友好
//queue.add(1);
queue.offer(1);
queue.offer(2);
queue.offer(3);
//取出元素份两种: 只获取头元素不取出 区别:当队列为空时候 element会抛出异常 peek返回null
queue.peek();
// queue.element();
//获取头元素 同时取出 区别:当队列为空时候 remove抛出异常 poll返回null
queue.poll();
// queue.remove();
}
}
3、链队列
双端队列,区别于普通队列
两端都可以进行入队和出队
Deque doble ended queue
LinkedList h和ArrayList
【特别的队列】
1》优先级队列
元素携带了相关的优先级,优先级更高的元素排在头部,PriorityQueue提供了Comparable接口,元素可以实现其方法,改变在队列中的顺序
2》阻塞队列
当队列满的时候,等待有空余的位置在存数据,当队列空的时候,等待有数据在读取
BlockingQueue put()和take()提供了存取的阻塞逻辑
分为两种情况,一种是无限期等,一种是指定阻塞时间
3》延迟队列
在指定时间内获取队列元素,头部元素是最近接过期时间的。
DelayQueue给定一个接口设置延迟时间 元素会按照时间排序
4,队列的最大值
分别对应 方法名 参数 以及返回结果
分析:
对头 队尾
【1,2,3,4,3,2,1】
当队列发生更改时,记录最大值
因为增删操作 都可能影响最大值的编号
如果是新增操作,比较新元素和当前最大值直接,更大的值是新最大值
如果删除操作,删除的元素如果不是最大值,那么最大值不变,如果是最大值,最大值需要改为剩余元素的最大值
使用额外的队列来记录最大值发生的变化
Max队列,对头元素是当前最大值,其他元素是未来可能成为最大值的候选值
新增元素 ele时 ele>max 取队列中队尾元素比较,如果满足,直接进行覆盖并且循环比较
直到把所有比他小的值都覆盖为止
ele 原队列 Max队列 1 1 1,2 2 1,2,3 3 2,3 3 2,3,2 3,2 2,3,2,1 3,2,1 3,2,1 3,2,1 2,1 2,1 2,1,4 4 import java.util.LinkedList; /** * @ClassName &{NAME} * @Description TODO * @Author zhanghao MX8837 * @Date 2021/11/4 11:18 * @Version 1.0 **/ public class MaxQueue { //原始队列 LinkedList //最大值候选值 LinkedList public MaxQueue(){ queue = new LinkedList<>(); max = new LinkedList<>(); } public int max_value(){ if(max.isEmpty()) return -1; return max.peekFirst(); } //新增元素 ele时 ele>max 取队列中队尾元素比较,如果满足,直接进行覆盖并且循环比较 //直到把所有比他小的值都覆盖为止 //ele public void push_back(int value){ queue.offer(value); while (!max.isEmpty() && max.peekLast() < value){ max.pollLast(); } max.add(value); } public int pop_front(){ //如果删除的元素是最大值,从max队列中同时删掉 if(!max.isEmpty() && queue.peekFirst().equals(max.peekFirst())){ max.pollFirst(); } if(queue.isEmpty()) return -1; return queue.poll(); } } 5. 滑动窗口最大值 给你一个整数数组 返回滑动窗口中的最大值。 示例 1: 输入:nums = [1,3,-1,-3,5,3,6,7], k = 3输出:[3,3,5,5,6,7]解释: 滑动窗口的位置 最大值 --------------- ----- [1 3 -1] -3 5 3 6 7 3 1 [3 -1 -3] 5 3 6 7 3 1 3 [-1 -3 5] 3 6 7 5 1 3 -1 [-3 5 3] 6 7 5 1 3 -1 -3 [5 3 6] 7 6 1 3 -1 -3 5 [3 6 7] 7 示例 2: 输入:nums = [1], k = 1输出:[1] 示例 3: 输入:nums = [1,-1], k = 1输出:[1,-1] 示例 4: 输入:nums = [9,11], k = 2输出:[11] 示例 5: 输入:nums = [4,-2], k = 2输出:[4] 分析: 数组的长度为n 窗口的大小为k 可形成的窗口个数 n-k+1 窗口未形成的阶段 遍历数组,同时更新队列 窗口已形成的阶段 每次窗口移动,都是在头部移除元素,尾部增加元素 在窗口变化过程中 记录其最大值的变化(让最大值一直出现在第一位) 让队列中已存在的元素 和新添加的元素比较 如果已存在的更小 那么覆盖 如果已存在的更大 新添加的元素是候选 缀到队列之后 /** * @ClassName &{NAME} * @Description TODO * @Author zhanghao MX8837 * @Date 2021/11/4 11:39 * @Version 1.0 **/ public class SlideWindow { public static void main(String[] args) { int[] arr = {1,3,-1,-3,5,3,6,7}; int[] result = maxSlidingWindow(arr,3); System.out.println(Arrays.toString(result)); } public static int[] maxSlidingWindow(int[] nums,int k){ //记录每个窗口的最大值 n-k+1为形成窗口个数 int[] result = new int[nums.length - k +1]; //使用队列记录最大值的候选值 Deque //窗口未形成的阶段 for (int i = 0; i < k; i++) { print(deQue); //每次都取 队尾元素和新元素比较 如果队尾更小 删除 while (!deQue.isEmpty() && deQue.peekLast() < nums[i]){ deQue.pollLast(); } deQue.offerLast(nums[i]); } //此时第一个窗口形成 deque的队头元素就是第一个窗口的最大值 result[0] = deQue.peekFirst(); //窗口已形成的阶段 for (int i = k; i < nums.length; i++) { System.out.println("--------第"+(i-k+1)+"次滑动"); //删除了元素nums[i-k] 添加了元素nums[i] if(nums[i-k] == deQue.peekFirst()){ //如果删的是最大值 同时从deque移除 deQue.peekFirst(); } //新增 while (!deQue.isEmpty() && deQue.peekLast() < nums[i]){ deQue.pollLast(); } deQue.offerLast(nums[i]); result[i-k+1] = deQue.peekFirst(); print(deQue); } return result; } public static void print(Deque for(Integer integer : deque){ System.out.print(integer+ " "); } System.out.println(); } } 6,设计循环队列 为解决,队列存取数据后,空间无法重复利用的问题,通过构造环形, 一直head和队列长度count tail=head+count-1 为了不超过容量大小 进行取模tail=(head+count-1)%capacity 设计你的循环队列实现。 循环队列是一种线性数据结构,其操作表现基于 FIFO(先进先出)原则并且队尾被连接在队首之后以形成一个循环。它也被称为“环形缓冲器”。 循环队列的一个好处是我们可以利用这个队列之前用过的空间。在一个普通队列里,一旦一个队列满了,我们就不能插入下一个元素,即使在队列前面仍有空间。但是使用循环队列,我们能使用这些空间去存储新的值。 你的实现应该支持如下操作: 示例: MyCircularQueue circularQueue = new MyCircularQueue(3); // 设置长度为 3 circularQueue.enQueue(1); // 返回 true circularQueue.enQueue(2); // 返回 true circularQueue.enQueue(3); // 返回 true circularQueue.enQueue(4); // 返回 false,队列已满 circularQueue.Rear(); // 返回 3 circularQueue.isFull(); // 返回 true circularQueue.deQueue(); // 返回 true circularQueue.enQueue(4); // 返回 true circularQueue.Rear(); // 返回 4 /** * @ClassName &{NAME} * @Description TODO * @Author zhanghao MX8837 * @Date 2021/11/4 13:52 * @Version 1.0 **/ public class MyCircularQueue { //存储元素的数组 int[] queue; //最大容量 int capacity; //头指针 int head; //时间队列长度 int count; public MyCircularQueue(int k) { capacity = k; queue = new int[k]; head = 0; count = 0; } public boolean enQueue(int value) { if(isFull()){ return false; } int index = (head + count) % capacity; queue[index] = value; count++; return true; } public boolean deQueue() { if(isEmpty()) return false; head = (head + 1) % capacity; count--; return true; } public int Front() { if(isEmpty()) return -1; return queue[head]; } public int Rear() { if(isEmpty()) return -1; int tail = (head + count - 1) % capacity; return queue[tail]; } public boolean isEmpty() { return count == 0; } public boolean isFull() { return count == capacity; } } 队列篇小结 VISUALGO 数据结构和算法动态可视化nums
,有一个大小为 k
的滑动窗口从数组的最左侧移动到数组的最右侧。你只可以看到在滑动窗口内的 k
个数字。滑动窗口每次只向右移动一位。
MyCircularQueue(k)
: 构造器,设置队列长度为 k 。
Front
: 从队首获取元素。如果队列为空,返回 -1 。
Rear
: 获取队尾元素。如果队列为空,返回 -1 。
enQueue(value)
: 向循环队列插入一个元素。如果成功插入则返回真。
deQueue()
: 从循环队列中删除一个元素。如果成功删除则返回真。
isEmpty()
: 检查循环队列是否为空。
isFull()
: 检查循环队列是否已满。