【问题标题】:Implement stack using queue - the best complexity使用队列实现堆栈 - 最佳复杂度
【发布时间】:2014-10-30 01:19:54
【问题描述】:

我想知道是否有一种方法可以根据需要使用尽可能多的队列来实现堆栈,从而在 O(1) 中推送和弹出数据。 如果没有任何 O(1) 算法,那么最好的复杂度是多少?

【问题讨论】:

  • O(1) 是什么意思?
  • 我的意思是算法所花费的时间不应该依赖于栈的大小。它必须是恒定的
  • 算法做什么用的时间?
  • 我不这么认为。你为什么想要?直觉上,我倾向于说 O(n) 是最好的情况。
  • 好吧,如果您可以使用数组实现 O(1) 堆栈,那么您也可以使用 1 元素队列的数组,但这毫无意义。你能更好地定义问题吗?是否允许使用数组或变量,或者解决方案必须完全由队列组成?

标签: algorithm stack queue time-complexity


【解决方案1】:

如果允许在队列中递归定义队列,则可以使用以下方式进行 O(1) 次推送/弹出:

代码:

STACK:
  QUEUE q

PUSH (S, x):
  QUEUE temp
  ENQUEUE(temp, x)
  ENQUEUE(temp, S.q)
  S.q = temp


POP (S):
  ANY x := DEQUEUE(S.q) # Error here if queue is empty
  QUEUE S.q := DEQUEUE(S.q)
  return x

结果是递归形成的堆栈。

如果[1,2] 代表一个堆栈,其中dequeue([1,2]) 将返回1。然后将1 然后3 然后6 压入堆栈的数据结构如下所示:

[6,[3,[1,[]]]]

【讨论】:

  • 一开始我以为是 O(1),后来发现在入队时复制 S.q 仍然是 O(n)。
  • 通过引用而不是值传递没有必要实际复制它。根据需要修改代码以确保队列没有被实际复制。如果代码暗示它们是我的错。
  • 那么 S.q 将只包含 x 和一个引用。并且引用指向什么,因为它指向自己。
  • 嗯,也许分配一个指向队列的指针,然后将指针而不是队列入队。也许我不应该复制 Rob 的伪代码(或者至少我将其视为伪代码)。不确定如何让它以您正在使用的语言工作,但我知道一个事实是应该可以让它以某种方式工作。例如,上面的代码无需修改即可在 Python 中运行。
  • 哦,是的...我被误解了!优秀的算法。非常感谢:D
【解决方案2】:

您可以使用线性时间 PUSH 和恒定时间 POP 进行堆栈。

给定一个具有 ENQUEUE 和 DEQUEUE 函数的队列:

STACK:
  QUEUE q


PUSH (S, x):
  r := new QUEUE
  ENQUEUE(r, x)

  while S.q not empty:
    v := DEQUEUE(S.q)
    ENQUEUE(r, v)

  S.q := r


POP (S):
  RETURN DEQUEUE(S.q)

编辑:不需要临时队列 r 的替代解决方案:

STACK:
  QUEUE q


PUSH (S, x):
  ENQUEUE(S.q, x)

  n := SIZE(S.q) - 1

  repeat n times:
    v := DEQUEUE(S.q)
    ENQUEUE(S.q, v)


POP (S):
  RETURN DEQUEUE(S.q)

【讨论】:

  • 嗯.. 它是 O(n)。但我想不出更好的了!谢谢你的回答:)
【解决方案3】:

这是一个堆栈的 C++ 实现,具有 O(1) 的 push 和 pop 函数。

接口类似于std::stack:

void push(const T& val);
void pop();
const T& top() const;
bool empty() const;

这是完整的代码。我想不出一种方法来避免混乱的类型转换。

#include <iostream>
#include <stack>
#include <queue>
#include <stdexcept>


#define ASSERT(x) \
  if (!(x)) { \
    std::cout << "Assertion failed at line " << __LINE__ << "\n"; \
  } \


template <typename T>
class Stack {
  public:
    Stack()
      : m_head(NULL), m_tail(NULL) {}

    void push(const T& val) {
      std::queue<void*>* tail = new std::queue<void*>();
      tail->push(reinterpret_cast<void*>(m_head));
      tail->push(reinterpret_cast<void*>(m_tail));

      m_head = new std::queue<T>();
      m_head->push(val);

      m_tail = tail;
    }

    void pop() {
      if (m_head) {
        delete m_head;

        m_head = reinterpret_cast<std::queue<T>*>(m_tail->front());
        m_tail->pop();

        std::queue<void*>* tail = reinterpret_cast<std::queue<void*>*>(m_tail->front());
        delete m_tail;
        m_tail = tail;
      }
    }

    const T& top() const {
      if (!m_head) {
        throw std::runtime_error("Error retrieving top element; stack empty");
      }

      return m_head->front();
    }

    bool empty() {
      return !m_head;
    }

  private:
    std::queue<T>* m_head;
    std::queue<void*>* m_tail;
};


int main() {
  Stack<int> s;

  s.pop();

  s.push(0);
  ASSERT(s.top() == 0);

  s.push(1);
  ASSERT(s.top() == 1);

  s.push(2);
  ASSERT(s.top() == 2);

  s.push(3);
  ASSERT(s.top() == 3);

  s.pop();
  ASSERT(s.top() == 2)

  s.push(4);
  ASSERT(s.top() == 4);

  s.push(5);
  ASSERT(s.top() == 5);

  s.push(6);
  ASSERT(s.top() == 6);

  s.pop();
  ASSERT(s.top() == 5)

  s.pop();
  ASSERT(s.top() == 4)

  s.push(7);
  ASSERT(s.top() == 7);

  s.pop();
  ASSERT(s.top() == 4)

  s.pop();
  ASSERT(s.top() == 2)

  s.pop();
  ASSERT(s.top() == 1)

  s.pop();
  ASSERT(s.top() == 0)

  s.pop();

  ASSERT(s.empty())

  s.pop();

  int error = false;
  try {
    int x = s.top();
  }
  catch (std::exception&) {
    error = true;
  }

  ASSERT(error == true);

  return 0;
}

【讨论】:

  • +1:看到它用 C++ 实现很有趣。动态类型确实让这样的事情更容易实现。
猜你喜欢
  • 1970-01-01
  • 2021-07-08
  • 2021-12-24
  • 2014-03-14
  • 2017-05-18
  • 2014-12-23
  • 2010-10-15
  • 1970-01-01
  • 2015-05-15
相关资源
最近更新 更多