数据结构和算法(五)——队列及其相关算法

队列基础知识

简介

如图(图片来源极客时间的《数据结构与算法" title="数据结构与算法">数据结构与算法之美》专栏)

只允许队尾入队,队头出队(即先进先出)的存储结构。

顺序队列

使用数组实现的队列,一般面试常考的队列是循环队列(下面介绍实现)。该队列的特点是:

  1. 队列大小固定
  2. 出队和入队的时间复杂度为O(1)

链式队列

使用链表的方式实现的队列,该队列的特点为:

  1. 队列大小不限
  2. 出队和入队的时间复杂度为O(1)

常见算法

实现一个循环队列

循环队列是由双指针 head 、 tail 分别指向队列头和队列尾来实现的。实现循环队列时要注意其队列是否为空和队列是否已经满的判断。条件如下:

队列为空:head == tail

队列已经满:(tail + 1)%capacity == head

代码如下:

class LoopQueue{

int[] queue = null;

int capacity = 0;

int head = 0;

int tail = 0;

public LoopQueue(int capacity) {

this.capacity = capacity;

queue = new int[capacity];

}

public void add(int val){

if((tail + 1) % capacity == head){

System.out.println("队列已满");

}else {

System.out.println("插入值:"+val);

tail = tail%capacity;

queue[tail] = val;

tail++;

}

}

public void remove(){

if(head%capacity == tail){

System.out.println("队列已空");

}else {

head = head%capacity;

int val = queue[head];

System.out.println("删除的值为:"+val);

head++;

}

}

}

设计循环双端队列

设计实现双端队列。

你的实现需要支持以下操作:

MyCircularDeque(k):构造函数,双端队列的大小为k。

insertFront():将一个元素添加到双端队列头部。 如果操作成功返回 true

insertLast():将一个元素添加到双端队列尾部。如果操作成功返回 true

deleteFront():从双端队列头部删除一个元素。 如果操作成功返回 true

deleteLast():从双端队列尾部删除一个元素。如果操作成功返回 true

getFront():从双端队列头部获得一个元素。如果双端队列为空,返回 -1

getRear():获得双端队列的最后一个元素。 如果双端队列为空,返回 -1

isEmpty():检查双端队列是否为空。

isFull():检查双端队列是否满

示例:

MyCircularDeque circularDeque = new MycircularDeque(3); // 设置容量大小为3

circularDeque.insertLast(1); // 返回 true

circularDeque.insertLast(2); // 返回 true

circularDeque.insertFront(3); // 返回 true

circularDeque.insertFront(4); // 已经满了,返回 false

circularDeque.getRear(); // 返回 2

circularDeque.isFull(); // 返回 true

circularDeque.deleteLast(); // 返回 true

circularDeque.insertFront(4); // 返回 true

circularDeque.getFront(); // 返回 4

代码如下:

classMyCircularDeque{

classNode{

Node next;

int value;

Node(int value){

this.value = value;

}

}

Node head;

Node tail;

int count = 0;

int capcity;

/** Initialize your data structure here. Set the size of the deque to be k. */

publicMyCircularDeque(int k){

head = new Node(-1);

tail = new Node(-1);

capcity = k;

}

/** Adds an item at the front of Deque. Return true if the operation is successful. */

publicbooleaninsertFront(int value){

if(count >= capcity)returnfalse;

Node next = new Node(value);

if(count == 0){

head.next = next;

tail.next = next;

}else{

Node p = head.next;

head.next = next;

next.next = p;

}

count++;

returntrue;

}

/** Adds an item at the rear of Deque. Return true if the operation is successful. */

publicbooleaninsertLast(int value){

if(count >= capcity)returnfalse;

Node next = new Node(value);

if(count == 0){

head.next = next;

tail.next = next;

}else{

Node p = tail.next;

tail.next = next;

p.next = next;

}

count++;

returntrue;

}

/** Deletes an item from the front of Deque. Return true if the operation is successful. */

publicbooleandeleteFront(){

if(count == 0)returnfalse;

if(count == 1){

head.next = null;

tail.next = null;

}else{

Node p = head.next;

head.next = p.next;

p.next = null;

}

count--;

returntrue;

}

/** Deletes an item from the rear of Deque. Return true if the operation is successful. */

publicbooleandeleteLast(){

if(count == 0)returnfalse;

if(count == 1){

head.next = null;

tail.next = null;

}else{

Node p = tail.next;

Node root = head.next;

while(root!=null&&root.next != p)root = root.next;

tail.next = root;

root.next = null;

}

count--;

returntrue;

}

/** Get the front item from the deque. */

publicintgetFront(){

if(count == 0)return -1;

Node node = head.next;

return node.value;

}

/** Get the last item from the deque. */

publicintgetRear(){

if(count == 0)return -1;

Node node = tail.next;

return node.value;

}

/** Checks whether the circular deque is empty or not. */

publicbooleanisEmpty(){

return count == 0;

}

/** Checks whether the circular deque is full or not. */

publicbooleanisFull(){

return count == capcity;

}

}

滑动窗口的最大值

给定一个数组 nums 和滑动窗口的大小 k,请找出所有滑动窗口里的最大值。

示例:

输入: 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

实现思路:

将窗口看成一个队列,每次滑动就是一次出队和入队操作。

方法一:如果每次都通过比较来获取队列里的最大值,由于队列大小为 k ,则时间复杂度为 O(n * k).

方法二:当滑动窗口出队的值为队列对头元素时,就让队列中的对头元素出队;当滑动窗口入队的值比队尾元素大时,就让队尾元素出队,不停循环直到队尾元素大于入队元素或者队列为空时,再往队列末尾插入元素。这样就可以让队列里的值满足逆序排序,每次只需要取对头元素即为最大值。

代码如下:

classSolution{

publicint[] maxSlidingWindow(int[] nums, int k) {

if(nums.length == 0)returnnewint[0];

if(k == 1)return nums;

int[] res = newint[nums.length - k + 1];

Deque<Integer> l = new LinkedList<>();

for(int i = 0;i < k;i++){

if(l.isEmpty())l.add(nums[i]);

else{

while(!l.isEmpty() && nums[i] > l.peekLast()){

l.pollLast();

}

l.add(nums[i]);

}

}

res[0] = l.peekFirst();

for(int i = k;i < nums.length;i++){

if(nums[i - k] == l.peekFirst())l.pollFirst();

while(!l.isEmpty() && nums[i] > l.peekLast()){

l.pollLast();

}

l.add(nums[i]);

res[i - k + 1] = l.peekFirst();

}

return res;

}

}

必须掌握的代码实现

  • 用数组实现一个顺序队列
  • 用链表实现一个链式队列
  • 实现一个循环队列

面试常考的与队列相关的算法题

  • 设计循环双端队列
  • 滑动窗口的最大值

以上是 数据结构和算法(五)——队列及其相关算法 的全部内容, 来源链接: utcz.com/a/35750.html

回到顶部