Data Structure and Algorithm (5) - Queue and its related algorithms

Queue Basics

Introduction

As shown in the picture ( the picture comes from Geek Time's "The Beauty of Data Structure and Algorithm" column )

insert image description here

Only the tail of the queue is allowed to enter the queue, and the head of the queue is discharged (that is, the storage structure of first-in-first-out).

sequential queue

The queue implemented using an array, the queue that is often tested in general interviews is a circular queue (the implementation is described below). The characteristics of this queue are:

  1. fixed queue size
  2. The time complexity of dequeue and enqueue is O(1)

chained queue

A queue implemented in the form of a linked list. The characteristics of the queue are:

  1. Unlimited queue size
  2. The time complexity of dequeue and enqueue is O(1)

common algorithm

implement a circular queue

The circular queue is realized by double pointers head and tail pointing to the head and tail of the queue respectively. When implementing a circular queue, attention should be paid to the judgment of whether the queue is empty and whether the queue is full. The conditions are as follows:

Queue is empty: head == tail

Queue is full: (tail + 1)%capacity == head

code show as below:

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++;
              }
          }
      }

Design circular double-ended queue

设计实现双端队列。
你的实现需要支持以下操作:

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

code show as below:

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

The maximum value of the sliding window

给定一个数组 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

Implementation idea:

Think of the window as a queue, and each swipe is a dequeue and enqueue operation.

Method 1: If the maximum value in the queue is obtained by comparison every time, since the queue size is k, the time complexity is O(n * k).

Method 2: When the dequeue value of the sliding window is the opposite element of the queue, let the opposite element in the queue be dequeued; when the value of the sliding window entering the queue is greater than the element at the end of the queue, let the element at the end of the queue be dequeued, non-stop Loop until the element at the end of the queue is greater than the element entering the queue or the queue is empty, and then insert an element at the end of the queue. In this way, the values ​​in the queue can be sorted in reverse order, and each time only the right element needs to be taken as the maximum value.

code show as below:

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

Code implementation that must be mastered

  • Implement a sequential queue with an array
  • Implement a chained queue with a linked list
  • implement a circular queue

Algorithmic questions related to queues that are often tested in interviews

Guess you like

Origin blog.csdn.net/lichukuan/article/details/127063398