web-dev-qa-db-ja.com

Push_rear()、pop_front()、get_min()がすべて一定時間の操作であるキューを実装します

この質問に出くわしました:Push_rear()、pop_front()、get_min()がすべて一定時間の操作であるキューを実装します。

私は当初、get_min()にO(1)複雑さを持つmin-heapデータ構造を使用することを考えました。しかし、Push_rear()およびpop_front()はO(log(n))になります。

O(1) Push()、pop()、およびmin()を含むこのようなキューを実装する最良の方法は何かを知っていますか?

私はこれについてグーグルで調べ、これを指摘したかった Algorithm Geeks thread 。しかし、Push()、pop()、およびmin()の3つのメソッドすべてについて、どのソリューションも一定時間ルールに従っていないようです。

すべての提案をありがとう。

73
bits

O(1) pop()、Push()、get_min()でスタックを実装できます。各要素とともに現在の最小値を保存するだけです。たとえば、スタック_[4,2,5,1]_(先頭に1)は[(4,4), (2,2), (5,2), (1,1)]になります。

次に、 キューを実装するために2つのスタックを使用 を実行できます。あるスタックにプッシュし、別のスタックからポップします。ポップ中に2番目のスタックが空の場合、すべての要素を最初のスタックから2番目のスタックに移動します。

たとえば、最初のスタック[(4,4), (2,2), (5,2), (1,1)]からすべての要素を移動するpopリクエストの場合、2番目のスタックは[(1,1), (5,1), (2,1), (4,1)]になります。そして、2番目のスタックから一番上の要素を返します。

キューの最小要素を見つけるには、個々の最小スタックの最小の2つの要素を調べてから、これら2つの値の最小値を取得します。 (もちろん、スタックの1つが空の場合の追加ロジックがありますが、回避するのはそれほど難しくありません)。

O(1) get_min() and Push() and amortized O(1) pop()

94
adamax

わかりました、ここに1つのソリューションがあります。

最初に、Push_back()、Push_front()、pop_back()、およびpop_front()を0(1)で提供するものが必要です。配列と2つのイテレータで簡単に実装できます。最初のイテレータは前面を、2番目は背面を指します。そのようなものをdequeと呼びましょう。

擬似コードは次のとおりです。

class MyQueue//Our data structure
{
    deque D;//We need 2 deque objects
    deque Min;

    Push(element)//pushing element to MyQueue
    {
        D.Push_back(element);
        while(Min.is_not_empty() and Min.back()>element)
             Min.pop_back();
        Min.Push_back(element);
    }
    pop()//poping MyQueue
    {
         if(Min.front()==D.front() )
            Min.pop_front();
         D.pop_front();
    }

    min()
    {
         return Min.front();
    }
}

説明:

例番号[12,5,10,7,11,19]とMyQueueにプッシュしましょう

1)プッシュ12

D [12]
Min[12]

2)プッシュ5

D[12,5]
Min[5] //5>12 so 12 removed

3)10を押す

D[12,5,10]
Min[5,10]

4)プッシュ7

D[12,5,10,7]
Min[5,7]

6)プッシュ11

D[12,5,10,7,11]
Min[5,7,11]

7)プッシュ19

D[12,5,10,7,11,19]
Min[5,7,11,19]

では、pop_front()を呼び出しましょう。

私たちは得た

 D[5,10,7,11,19]
 Min[5,7,11,19]

最小値は5です

もう一度pop_front()を呼び出しましょう

説明:pop_frontはDから5を削除しますが、Dのフロント要素(5)と等しいため、Minのフロント要素もポップします。

 D[10,7,11,19]
 Min[7,11,19]

最小値は7です。

21
UmmaGumma

1つのdeque(A)を使用して要素を保存し、別のdeque(B)を使用して最小値を保存します。

Xがキューに入れられたら、それをAにPush_backし、Bの後ろがxより小さくなるまでpop_backing Bを保持し、次にxをBにPush_backします。

aをデキューするとき、pop_front Aを戻り値として使用し、それがBのフロントと等しい場合は、pop_front Bも同様にします。

aの最小値を取得する場合、戻り値としてBの前部を使用します。

dequeueとgetminは明らかにO(1)です。エンキュー操作では、n要素のPush_backを考慮してください。各要素はBに留まるか、Bから1回ポップアウトされるため、Aへのnプッシュバック、Bへのnプッシュバック、およびBの最大nポップバックがあります。O(3n) =操作であるため、エンキューの償却コストもO(1)です。

最後にこのアルゴリズムが機能する理由は、xをAにエンキューするときに、xがxより大きい要素がBにある場合、xはBの要素(キューFIFO)です。したがって、xをBにプッシュする前に、xよりも大きい要素をBから(後ろから)ポップアウトする必要があります。

from collections import deque


class MinQueue(deque):
    def __init__(self):
        deque.__init__(self)
        self.minq = deque()

    def Push_rear(self, x):
        self.append(x)
        while len(self.minq) > 0 and self.minq[-1] > x:
            self.minq.pop()
        self.minq.append(x)

    def pop_front(self):
        x = self.popleft()
        if self.minq[0] == x:
            self.minq.popleft()
        return(x)

    def get_min(self):
        return(self.minq[0])
3
jianglai

少しの余分なデータを保存しても構わない場合は、最小値を保存するのは簡単です。プッシュアンドポップは、新しい要素または削除された要素が最小の場合に値を更新でき、最小値を返すのは変数の値を取得するのと同じくらい簡単です。

これは、get_min()がデータを変更しないと仮定しています。 pop_min()のようなもの(つまり最小要素を削除する)が必要な場合は、実際の要素とその前の要素(存在する場合)へのポインタを保存し、Push_rear()とpop_front()で適宜更新します同様に。

コメント後に編集:

明らかに、これはO(n)これらの操作の最小変更がプッシュアンドポップであるため、要件を厳密に満たさない場合につながります。

1
Andy Mikula

実際にLinkedListを使用してキューを維持できます。

LinkedListの各要素はTypeになります

class LinkedListElement
{
   LinkedListElement next;
   int currentMin;
}

1つは開始点を指し、もう1つは終了点を指す2つのポインターを使用できます。

キューの先頭に要素を追加する場合。挿入する開始ポインターとノードを調べます。 currentminを挿入するノードがstart currentminより小さい場合、currentminを挿入するノードが最小です。それ以外の場合は、start currentminでcurrentminを更新します。

Enqueについても同じことを繰り返します。

1
Sandeep

コードを含むこの質問に対する解決策は、ここにあります: http://discuss.joelonsoftware.com/default.asp?interview.11.742223.32

1
Olhovsky

プッシュとポップは一定時間の操作であることを知っています[正確にはO(1)]。

しかし、get_min()[つまり、キュー内の現在の最小数を見つける]を考えると、一般的に最初に頭に浮かぶのは、最小要素のリクエストが行われるたびにキュー全体を検索することです。しかし、これは問題の主な目的である一定時間の操作を決して与えません。

これは通常、インタビューで非常に頻繁に尋ねられるため、トリックを知っておく必要があります

これを行うには、最小要素を追跡するキューをさらに2つ使用する必要があり、最小要素がO(1)時間。

上記のアプローチに基づいた自己記述的なSudoコードを次に示します。

    Queue q, minq1, minq2;
    isMinq1Current=true;   
    void Push(int a)
    {
      q.Push(a);
      if(isMinq1Current)
      {
        if(minq1.empty) minq1.Push(a);
        else
        {
          while(!minq1.empty && minq1.top < =a) minq2.Push(minq1.pop());
          minq2.Push(a);
          while(!minq1.empty) minq1.pop();
          isMinq1Current=false;
        }
      }
      else
      {
        //mirror if(isMinq1Current) branch. 
      }
    }

    int pop()
    { 
      int a = q.pop();
      if(isMinq1Current)
      {
        if(a==minq1.top) minq1.pop();
      }
      else
      {
        //mirror if(isMinq1Current) branch.    
      }
    return a;
    }
0
Sachin
#include <iostream>
#include <queue>
#include <deque>
using namespace std;

queue<int> main_queue;
deque<int> min_queue;

void clearQueue(deque<int> &q)
{
  while(q.empty() == false) q.pop_front();
}

void PushRear(int elem)
{
  main_queue.Push(elem);

  if(min_queue.empty() == false && elem < min_queue.front())
  {
      clearQueue(min_queue);
  }

  while(min_queue.empty() == false && elem < min_queue.back())
  {
      min_queue.pop_back();
  }

  min_queue.Push_back(elem);
}

void PopFront() 
{
  int elem = main_queue.front();
  main_queue.pop();

  if (elem == min_queue.front())
  {
       min_queue.pop_front();
  }
}

int GetMin() 
{ 
  return min_queue.front(); 
}

int main()
{
  PushRear(1);
  PushRear(-1);
  PushRear(2);

  cout<<GetMin()<<endl;
  PopFront();
  PopFront();
  cout<<GetMin()<<endl;

  return 0;
}
0
TheMan

このソリューションには2つのキューが含まれています。
1。 main_q-入力番号を保存します。
2。 min_q-説明する特定のルール(関数MainQ.enqueue(x)、MainQ.dequeue()、MainQ.get_min()に表示されます)によって最小値を格納します。

これがPythonのコードです。キューはリストを使用して実装されます。
主なアイデアは、MainQ.enqueue(x)、MainQ.dequeue()、MainQ.get_min()関数にあります。
1つの重要な前提は、キューを空にするのにo(0)が必要であることです。
テストは最後に提供されます。

import numbers

class EmptyQueueException(Exception):
    pass

class BaseQ():
    def __init__(self):
        self.l = list()

    def enqueue(self, x):
        assert isinstance(x, numbers.Number)
        self.l.append(x)

    def dequeue(self):
        return self.l.pop(0)

    def peek_first(self):
        return self.l[0]

    def peek_last(self):
        return self.l[len(self.l)-1]

    def empty(self):
        return self.l==None or len(self.l)==0

    def clear(self):
        self.l=[]

class MainQ(BaseQ):
    def __init__(self, min_q):
        super().__init__()
        self.min_q = min_q

    def enqueue(self, x):
        super().enqueue(x)
        if self.min_q.empty():
            self.min_q.enqueue(x)
        Elif x > self.min_q.peek_last():
            self.min_q.enqueue(x)
        else: # x <= self.min_q.peek_last():
            self.min_q.clear()
            self.min_q.enqueue(x)

    def dequeue(self):
        if self.empty():
            raise EmptyQueueException("Queue is empty")
        x = super().dequeue()
        if x == self.min_q.peek_first():
            self.min_q.dequeue()
        return x

    def get_min(self):
        if self.empty():
            raise EmptyQueueException("Queue is empty, NO minimum")
        return self.min_q.peek_first()

INPUT_NUMS = (("+", 5), ("+", 10), ("+", 3), ("+", 6), ("+", 1), ("+", 2), ("+", 4), ("+", -4), ("+", 100), ("+", -40),
              ("-",None), ("-",None), ("-",None), ("+",-400), ("+",90), ("-",None),
              ("-",None), ("-",None), ("-",None), ("-",None), ("-",None), ("-",None), ("-",None), ("-",None))

if __== '__main__':
    min_q = BaseQ()
    main_q = MainQ(min_q)

    try:
        for operator, i in INPUT_NUMS:
            if operator=="+":
                main_q.enqueue(i)
                print("Added {} ; Min is: {}".format(i,main_q.get_min()))
                print("main_q = {}".format(main_q.l))
                print("min_q = {}".format(main_q.min_q.l))
                print("==========")
            else:
                x = main_q.dequeue()
                print("Removed {} ; Min is: {}".format(x,main_q.get_min()))
                print("main_q = {}".format(main_q.l))
                print("min_q = {}".format(main_q.min_q.l))
                print("==========")
    except Exception as e:
        print("exception: {}".format(e))

上記のテストの出力は次のとおりです。

"C:\Program Files\Python35\python.exe" C:/dev/python/py3_pocs/proj1/priority_queue.py
Added 5 ; Min is: 5
main_q = [5]
min_q = [5]
==========
Added 10 ; Min is: 5
main_q = [5, 10]
min_q = [5, 10]
==========
Added 3 ; Min is: 3
main_q = [5, 10, 3]
min_q = [3]
==========
Added 6 ; Min is: 3
main_q = [5, 10, 3, 6]
min_q = [3, 6]
==========
Added 1 ; Min is: 1
main_q = [5, 10, 3, 6, 1]
min_q = [1]
==========
Added 2 ; Min is: 1
main_q = [5, 10, 3, 6, 1, 2]
min_q = [1, 2]
==========
Added 4 ; Min is: 1
main_q = [5, 10, 3, 6, 1, 2, 4]
min_q = [1, 2, 4]
==========
Added -4 ; Min is: -4
main_q = [5, 10, 3, 6, 1, 2, 4, -4]
min_q = [-4]
==========
Added 100 ; Min is: -4
main_q = [5, 10, 3, 6, 1, 2, 4, -4, 100]
min_q = [-4, 100]
==========
Added -40 ; Min is: -40
main_q = [5, 10, 3, 6, 1, 2, 4, -4, 100, -40]
min_q = [-40]
==========
Removed 5 ; Min is: -40
main_q = [10, 3, 6, 1, 2, 4, -4, 100, -40]
min_q = [-40]
==========
Removed 10 ; Min is: -40
main_q = [3, 6, 1, 2, 4, -4, 100, -40]
min_q = [-40]
==========
Removed 3 ; Min is: -40
main_q = [6, 1, 2, 4, -4, 100, -40]
min_q = [-40]
==========
Added -400 ; Min is: -400
main_q = [6, 1, 2, 4, -4, 100, -40, -400]
min_q = [-400]
==========
Added 90 ; Min is: -400
main_q = [6, 1, 2, 4, -4, 100, -40, -400, 90]
min_q = [-400, 90]
==========
Removed 6 ; Min is: -400
main_q = [1, 2, 4, -4, 100, -40, -400, 90]
min_q = [-400, 90]
==========
Removed 1 ; Min is: -400
main_q = [2, 4, -4, 100, -40, -400, 90]
min_q = [-400, 90]
==========
Removed 2 ; Min is: -400
main_q = [4, -4, 100, -40, -400, 90]
min_q = [-400, 90]
==========
Removed 4 ; Min is: -400
main_q = [-4, 100, -40, -400, 90]
min_q = [-400, 90]
==========
Removed -4 ; Min is: -400
main_q = [100, -40, -400, 90]
min_q = [-400, 90]
==========
Removed 100 ; Min is: -400
main_q = [-40, -400, 90]
min_q = [-400, 90]
==========
Removed -40 ; Min is: -400
main_q = [-400, 90]
min_q = [-400, 90]
==========
Removed -400 ; Min is: 90
main_q = [90]
min_q = [90]
==========
exception: Queue is empty, NO minimum

Process finished with exit code 0
0
user3139774

Java実装

import Java.io.*;
import Java.util.*;

public class queueMin {
    static class stack {

        private Node<Integer> head;

        public void Push(int data) {
            Node<Integer> newNode = new Node<Integer>(data);
            if(null == head) {
                head = newNode;
            } else {
                Node<Integer> prev = head;
                head = newNode;
                head.setNext(prev);
            }
        }

        public int pop() {
            int data = -1;
            if(null == head){
                System.out.println("Error Nothing to pop");
            } else {
                data = head.getData();
                head = head.getNext();
            }

            return data;
        }

        public int peek(){
            if(null == head){
                System.out.println("Error Nothing to pop");
                return -1;
            } else {
                return head.getData();
            }
        }

        public boolean isEmpty(){
            return null == head;
        }
    }

    static class stackMin extends stack {
        private stack s2;

        public stackMin(){
            s2 = new stack();
        }

        public void Push(int data){
            if(data <= getMin()){
                s2.Push(data);
            }

            super.Push(data);
        }

        public int pop(){
            int value = super.pop();
            if(value == getMin()) {
                s2.pop();
            }
            return value;
        }

        public int getMin(){
            if(s2.isEmpty()) {
                return Integer.MAX_VALUE;
            }
            return s2.peek();
        }
    }

     static class Queue {

        private stackMin s1, s2;

        public Queue(){
            s1 = new stackMin();
            s2 = new stackMin();
        }

        public  void enQueue(int data) {
            s1.Push(data);
        }

        public  int deQueue() {
            if(s2.isEmpty()) {
                while(!s1.isEmpty()) {
                    s2.Push(s1.pop());
                }
            }

            return s2.pop();
        }

        public int getMin(){
            return Math.min(s1.isEmpty() ? Integer.MAX_VALUE : s1.getMin(), s2.isEmpty() ? Integer.MAX_VALUE : s2.getMin());
        }

    }



   static class Node<T> {
        private T data;
        private T min;
        private Node<T> next;

        public Node(T data){
            this.data = data;
            this.next = null;
        }


        public void setNext(Node<T> next){
            this.next = next;
        }

        public T getData(){
            return this.data;
        }

        public Node<T> getNext(){
            return this.next;
        }

        public void setMin(T min){
            this.min = min;
        }

        public T getMin(){
            return this.min;
        }
    }

    public static void main(String args[]){
       try {
           FastScanner in = newInput();
           PrintWriter out = newOutput();
          // System.out.println(out);
           Queue q = new Queue();
           int t = in.nextInt();
           while(t-- > 0) {
               String[] inp = in.nextLine().split(" ");
               switch (inp[0]) {
                   case "+":
                       q.enQueue(Integer.parseInt(inp[1]));
                       break;
                   case "-":
                       q.deQueue();
                       break;
                   case "?":
                       out.println(q.getMin());
                   default:
                       break;
               }
           }
           out.flush();
           out.close();

       } catch(IOException e){
          e.printStackTrace();
       }
    }

    static class FastScanner {
        static BufferedReader br;
        static StringTokenizer st;

        FastScanner(File f) {
            try {
                br = new BufferedReader(new FileReader(f));
            } catch (FileNotFoundException e) {
                e.printStackTrace();
            }
        }
        public FastScanner(InputStream f) {
            br = new BufferedReader(new InputStreamReader(f));
        }
        String next() {
            while (st == null || !st.hasMoreTokens()) {
                try {
                    st = new StringTokenizer(br.readLine());
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            return st.nextToken();
        }

        String nextLine(){
            String str = "";
            try {
                str = br.readLine();
            } catch (IOException e) {
                e.printStackTrace();
            }
            return str;
        }

        int nextInt() {
            return Integer.parseInt(next());
        }
        long nextLong() {
            return Long.parseLong(next());
        }
        double nextDoulbe() {
            return Double.parseDouble(next());
        }
    }

    static FastScanner newInput() throws IOException {
        if (System.getProperty("JUDGE") != null) {
            return new FastScanner(new File("input.txt"));
        } else {
            return new FastScanner(System.in);
        }
    }
    static PrintWriter newOutput() throws IOException {
        if (System.getProperty("JUDGE") != null) {
            return new PrintWriter("output.txt");
        } else {
            return new PrintWriter(System.out);
        }
    }
}
0
Nitish Bhagat