从0-1学习数据结构与算法--队列篇

初始队列

队列:是一种对存取有要求的数据结构

只能从尾部存入数据,从头部取出数据

遵循先进先出的原则

队列的实现方式:顺序队列(基于数组),链队列(基于链表)

需要两个指针:分布记录队头和队尾的位置

存数据时候,队尾指针向后移动

取数据时候,对头的指针向后移动

当队列为空时候,对头和队尾指针在某个位置重合

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 queue = new LinkedList<>();

//都能添加元素 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

从0-1学习数据结构与算法--队列篇_第1张图片

 

【特别的队列】

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 queue;

//最大值候选值

LinkedList max;

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. 滑动窗口最大值

给你一个整数数组 nums,有一个大小为 k 的滑动窗口从数组的最左侧移动到数组的最右侧。你只可以看到在滑动窗口内的 k 个数字。滑动窗口每次只向右移动一位。

返回滑动窗口中的最大值。

示例 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 deQue = new ArrayDeque<>();

//窗口未形成的阶段

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 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(k): 构造器,设置队列长度为 k 。
  • Front: 从队首获取元素。如果队列为空,返回 -1 。
  • Rear: 获取队尾元素。如果队列为空,返回 -1 。
  • enQueue(value): 向循环队列插入一个元素。如果成功插入则返回真。
  • deQueue(): 从循环队列中删除一个元素。如果成功删除则返回真。
  • isEmpty(): 检查循环队列是否为空。
  • isFull(): 检查循环队列是否已满。

示例:

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 数据结构和算法动态可视化

你可能感兴趣的:(java踩坑总结,微服务,数据结构,算法,链表)