Algorithm 实现一个队列,其中push_rear()、pop_front()和get_min()都是常量时间操作

Algorithm 实现一个队列,其中push_rear()、pop_front()和get_min()都是常量时间操作,algorithm,data-structures,queue,big-o,Algorithm,Data Structures,Queue,Big O,我遇到了一个问题: 实现一个队列,其中push_rear()、pop_front()和get_min()都是固定时间操作。 我最初考虑使用min heap数据结构,对于get_min(),它的复杂性为O(1)。但是push_rear()和pop_front()将是O(log(n)) 有人知道实现这样一个包含O(1)push()、pop()和min()的队列的最佳方法是什么吗 我在谷歌上搜索了一下,想指出这一点。但是,对于push()、pop()和min()这三种方法,似乎没有一种解决方案遵循固定

我遇到了一个问题: 实现一个队列,其中push_rear()、pop_front()和get_min()都是固定时间操作。

我最初考虑使用min heap数据结构,对于get_min(),它的复杂性为O(1)。但是push_rear()和pop_front()将是O(log(n))

有人知道实现这样一个包含O(1)push()、pop()和min()的队列的最佳方法是什么吗

我在谷歌上搜索了一下,想指出这一点。但是,对于push()、pop()和min()这三种方法,似乎没有一种解决方案遵循固定时间规则


谢谢您的建议。

如果您不介意存储一些额外的数据,那么存储最小值应该很简单。如果新的或删除的元素是最小值,Push和pop可以更新该值,返回最小值与获取变量值一样简单

这是假设get_min()不会更改数据;如果您希望使用类似于pop_min()的内容(即删除最小元素),则只需存储一个指向实际元素及其前面的元素(如果有)的指针,并相应地使用push_rear()和pop_front()更新它们

评论后编辑:


显然,如果这些操作的最小值发生变化,那么这会导致O(n)push和pop,因此不能严格满足要求。

您可以使用O(1)pop()、push()和get_min()实现堆栈:只需将当前最小值与每个元素一起存储。因此,例如,堆栈
[4,2,5,1]
(1在顶部)变成
[(4,4)、(2,2)、(5,2)、(1,1)]

那你就可以了。推到一个堆栈,从另一个堆栈弹出;如果第二个堆栈在弹出期间为空,请将所有元素从第一个堆栈移动到第二个堆栈

例如,对于一个
pop
请求,移动第一个堆栈中的所有元素
[(4,4)、(2,2)、(5,2)、(1,1)]
,第二个堆栈将是
[(1,1)、(5,1)、(2,1)、(4,1)]
。现在从第二个堆栈返回顶部元素

要找到队列的最小元素,请查看各个最小堆栈中最小的两个元素,然后取这两个值中的最小值。(当然,在一个堆栈为空的情况下,这里有一些额外的逻辑,但这并不难解决)


它将有O(1)
get_min()
push()
和摊销O(1)
pop()

好的-我想我有一个答案,它给出了摊销O(1)中的所有操作,这意味着任何一个操作都可能占用O(n),但任何n个操作的顺序每次操作都需要O(1)时间

这样做的目的是将数据存储为数据库。这是一个二叉树,遵循min heap属性(每个节点不大于其子节点),其排序方式使节点的无序遍历以添加节点的相同顺序返回节点。例如,下面是序列
2 1 4 3 5
的笛卡尔树:

       1
     /   \
    2      3
          / \
         4   5
可以使用以下步骤在O(1)摊销时间内将元素插入笛卡尔树。看看树的右脊柱(从根部到最右边的叶子的路径,由一直向右行走形成)。从最右边的节点开始,沿此路径向上扫描,直到找到比要插入的节点小的第一个节点。
更改该节点,使其右子节点成为新节点,然后使该节点的前右子节点成为刚才添加的节点的左子节点。例如,假设我们想在上面的树中插入2的另一个副本。我们沿着右侧脊柱走,经过5和3,但在1下方停下来,因为1<2。然后,我们将树更改为如下所示:

       1
     /   \
    2      2
          /
         3
        / \
       4   5
请注意,按顺序遍历会得到2 1 4 3 5 2,这是我们添加值的顺序

这在摊销O(1)中运行,因为我们可以创建一个势函数,该函数等于树右侧脊椎中的节点数。插入一个节点所需的时间是1,加上我们考虑的脊柱节点数(称为K)。一旦找到插入节点的位置,脊椎的大小将收缩长度k-1,因为我们访问的每个k节点都不再位于右侧脊椎上,而新节点位于其位置。这使得摊销O(1)插入的摊销成本为1+k+(1-k)=2=O(1)。另一种思考方式是,一旦节点从右侧脊椎移动,它就不再是右侧脊椎的一部分,因此我们将不再需要再次移动它。由于n个节点中的每个节点最多只能移动一次,这意味着n个插入最多可以进行n次移动,因此对于每个元素的摊销O(1),总运行时间最多为O(n)

要执行出列步骤,只需从笛卡尔树中删除最左侧的节点。如果这个节点是一片叶子,我们就完成了。否则,节点只能有一个子节点(正确的子节点),因此我们用其正确的子节点替换节点。如果我们跟踪最左侧节点的位置,则此步骤需要O(1)个时间。但是,在删除最左侧的节点并用其右子节点替换它之后,我们可能不知道新的最左侧节点在哪里。要解决这个问题,我们只需沿着树的左侧脊椎走,从刚移动到最左侧子节点的新节点开始。我声称这仍然在O(1)摊销时间内运行。为了了解这一点,我声明在这些过程中的任何一个过程中,节点最多访问一次,以查找最左侧的节点。要看到这一点,请注意,一旦以这种方式访问了一个节点,我们唯一需要再次查看它的方法就是将它从最左侧节点的子节点移动到最左侧节点。但是所有被访问的节点都是最左边节点的父节点,所以这不可能发生。因此,在此过程中,每个节点最多访问一次,pop以O(1)的形式运行

我们可以
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();
    }
}
D [12]
Min[12]
D[12,5]
Min[5] //5>12 so 12 removed
D[12,5,10]
Min[5,10]
D[12,5,10,7]
Min[5,7]
D[12,5,10,7,11]
Min[5,7,11]
D[12,5,10,7,11,19]
Min[5,7,11,19]
 D[5,10,7,11,19]
 Min[5,7,11,19]
 D[10,7,11,19]
 Min[7,11,19]
    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;
    }
class LinkedListElement
{
   LinkedListElement next;
   int currentMin;
}
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])
#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;
}
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 __name__ == '__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
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);
        }
    }
}
const m = new MaxQueue();

m.enqueue(6);

/*
the dqStack now looks like:
[6, 6] - [value, max]
*/
m.enqueue(7);
m.enqueue(8);

/*
dqStack:         eqStack: 8
         [6, 6]           7 - just the value
*/
/*
dqStack:         eqStack: 8
         [6, 8]           7
*/
m.dequeue();
> 6

// equivalent to:
/*
const tuple = m.dqStack.pop() // [6, 8]
tuple[0];
> 6
*/
// if we build a MaxQueue
const maxQ = new MaxQueue(3, 5, 2, 4, 1);

/*
the stacks will look like:

dqStack:         eqStack: 1
                          4
                          2
         [3, 5]           5
*/
maxQ.dequeue(); // pops from dqStack (now empty), so move all from eqStack to dqStack
> 3

// as dequeue moves one value over, it checks if it's greater than the ***previous max*** and stores the max at tuple[1], i.e., [data, max]:
/*
dqStack: [5, 5] => 5 > 4 - update                          eqStack:
         [2, 4] => 2 < 4 - no update                         
         [4, 4] => 4 > 1 - update                            
         [1, 1] => 1st value moved over so max is itself            empty
*/
maxQ.getMax();
> 5

// equivalent to calling peek on the dqStack and pulling out the maximum value:
/*
const peekedTuple = maxQ.dqStack.peek(); // [5, 5]
peekedTuple[1];
> 5
*/