この質問に出くわしました: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つのメソッドすべてについて、どのソリューションも一定時間ルールに従っていないようです。
すべての提案をありがとう。
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()
。
わかりました、ここに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です。
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])
少しの余分なデータを保存しても構わない場合は、最小値を保存するのは簡単です。プッシュアンドポップは、新しい要素または削除された要素が最小の場合に値を更新でき、最小値を返すのは変数の値を取得するのと同じくらい簡単です。
これは、get_min()がデータを変更しないと仮定しています。 pop_min()のようなもの(つまり最小要素を削除する)が必要な場合は、実際の要素とその前の要素(存在する場合)へのポインタを保存し、Push_rear()とpop_front()で適宜更新します同様に。
コメント後に編集:
明らかに、これはO(n)これらの操作の最小変更がプッシュアンドポップであるため、要件を厳密に満たさない場合につながります。
実際にLinkedListを使用してキューを維持できます。
LinkedListの各要素はTypeになります
class LinkedListElement
{
LinkedListElement next;
int currentMin;
}
1つは開始点を指し、もう1つは終了点を指す2つのポインターを使用できます。
キューの先頭に要素を追加する場合。挿入する開始ポインターとノードを調べます。 currentminを挿入するノードがstart currentminより小さい場合、currentminを挿入するノードが最小です。それ以外の場合は、start currentminでcurrentminを更新します。
Enqueについても同じことを繰り返します。
コードを含むこの質問に対する解決策は、ここにあります: http://discuss.joelonsoftware.com/default.asp?interview.11.742223.32
プッシュとポップは一定時間の操作であることを知っています[正確には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;
}
#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;
}
このソリューションには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
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);
}
}
}