データ構造とアルゴリズム (5) - キューとその関連アルゴリズム

キューの基本

序章

写真に示すように (写真は Geek Time の「データ構造とアルゴリズムの美しさ」コラムから引用)

ここに画像の説明を挿入

キューの末尾のみがキューに入ることができ、キューの先頭は排出されます (つまり、先入れ先出しの記憶構造)。

シーケンシャルキュー

配列を使用して実装されたキュー、一般的なインタビューでよくテストされるキューは循環キューです (実装については後述します)。このキューの特徴は次のとおりです。

  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

実装アイデア:

ウィンドウをキューと考えると、各スワイプがデキューおよびエンキュー操作となります。

方法1:キュー内の最大値を毎回比較して求める場合、キューサイズがkであるため、時間計算量はO(n*k)となる。

方法 2: スライディング ウィンドウのデキュー値がキューの反対側の要素である場合、キューに入るスライディング ウィンドウの値がキューの最後にある要素より大きい場合、キュー内の反対側の要素がデキューされます。 、キューの最後にある要素をデキューさせます。キューの最後にある要素がキューに入る要素よりも大きくなるか、キューが空になるまでノンストップでループし、その後、キューの最後に要素を挿入します。列。このようにして、キュー内の値を逆順に並べ替えることができ、毎回正しい要素のみを最大値として取得する必要があります。

コードは以下のように表示されます。

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