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

队列基础知识

简介

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

在这里插入图片描述

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

顺序队列

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

  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():将一个元素添加到双端队列头部。 如果操作成功返回 trueinsertLast():将一个元素添加到双端队列尾部。如果操作成功返回 truedeleteFront():从双端队列头部删除一个元素。 如果操作成功返回 truedeleteLast():从双端队列尾部删除一个元素。如果操作成功返回 truegetFront():从双端队列头部获得一个元素。如果双端队列为空,返回 -1getRear():获得双端队列的最后一个元素。 如果双端队列为空,返回 -1isEmpty():检查双端队列是否为空。
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

代码如下:

class MyCircularDeque {
    
    

   class Node{
    
    
       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. */
    public MyCircularDeque(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. */
    public boolean insertFront(int value) {
    
    
        if(count >= capcity)return false;
        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++;
         return true;
    }
    
    /** Adds an item at the rear of Deque. Return true if the operation is successful. */
    public boolean insertLast(int value) {
    
    
       if(count >= capcity)return false;
        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++;
         return true;
    }
    
    /** Deletes an item from the front of Deque. Return true if the operation is successful. */
    public boolean deleteFront() {
    
    
       if(count == 0)return false;
       if(count == 1){
    
    
         head.next = null;
         tail.next = null; 
       }else{
    
    
         Node p = head.next;
         head.next = p.next;
         p.next = null;
       }
       count--;
       return true;
    }
    
    /** Deletes an item from the rear of Deque. Return true if the operation is successful. */
    public boolean deleteLast() {
    
    
       if(count == 0)return false;
       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--;
       return true;
    }
    
    /** Get the front item from the deque. */
    public int getFront() {
    
    
      if(count == 0)return -1;  
      Node node = head.next;
      return node.value;
    }
    
    /** Get the last item from the deque. */
    public int getRear() {
    
    
      if(count == 0)return -1;  
      Node node = tail.next;
      return node.value;
    }
    
    /** Checks whether the circular deque is empty or not. */
    public boolean isEmpty() {
    
    
       return count == 0;
    }
    
    /** Checks whether the circular deque is full or not. */
    public boolean isFull() {
    
    
      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).

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

代码如下:

class Solution {
    
    
    public int[] maxSlidingWindow(int[] nums, int k) {
    
    
       if(nums.length == 0)return new int[0]; 
       if(k == 1)return nums;
       int[] res = new int[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;
    }
}

必须掌握的代码实现

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

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

猜你喜欢

转载自blog.csdn.net/lichukuan/article/details/127063398