2014 WAP初试题----实现一个不可变的队列:
看似很简单。。实则,不同的版本效率的差距可能是巨大的。。甚至难以想象。。
之前用STL库的queue进行了对比,差别非常大。。
用上一篇文章的immutable stack 来实现 immutable queue.
其实就是用两个栈实现队列,具体的思想可以参考 编程之美。。
注意:代码中析构函数那一段代码的目的主要是为了避免析构函数递归调用时递归深度太大。。
通过将链表后序的节点的智能指针复制到局部的vector中,通过vector的析构函数自动调用成员的析构函数的规则来实现。。
可能有点绕,大家可以仔细琢磨以下。。
附加题:
实现一个reverse操作(这道题据说坑了不少同学,其实非常地简单,一句话就能搞定)
//copyrigt @ L.J.SHOU May.25, 2014
#include
using namespace std;
/**
* This class is a functional datatype, representing
* an immutable first-in-first-out(FIFO) queue of objects.
*
* amortized O(1) cost
*
* implementation details:
* 1. two lists are used as stacks to mimic queue
* front: for dequeue
* rear: for enqueue
* Once front is empty, rear are reversed and added to front
* so front is empty, if and only if rear is empty too.
*
* 2. std::shared_ptr is used to collect garbage
*/
template
class immutable_queue
{
class List;
public:
immutable_queue();
immutable_queue(List front, List rear);
~immutable_queue();
immutable_queue enqueue(const T &element);
immutable_queue dequeue();
immutable_queue reverse();
immutable_queue append(immutable_queue & rhs) {
immutable_queue res = *this;
immutable_queue temp = rhs;
while(!temp.empty()) {
res = res.enqueue(temp.peek());
temp = temp.dequeue();
}
return res;
}
T getMax();
T peek();
int size();
bool empty() {
return front_.empty() && rear_.empty();
}
private:
List front_, rear_; // two stacks to mimic queue
// utility list class
class List
{
struct Node
{
Node(T x, shared_ptr &tail)
: val_(x), next_(tail), max_(x){
if(tail)
max_ = max(val_, tail->max_);
}
/**
* To avoid too deep recursive call
*
* Normally, default destructor provided by c++ compiler is enough
* but in some cases, where the list is too long,
* the recursive call of this function would be too deep,
* leading a stack overflow error.
*/
~Node() {
shared_ptr *ptr = &next_, *next(nullptr);
vector > vec;
while(*ptr) {
if((*ptr).unique() == false) break;
next = &((*ptr)->next_);
vec.push_back(*ptr);
*ptr = nullptr;
ptr = next;
}
}
T val_;
T max_;
shared_ptr next_;
};
public:
List(): count_(0), head_(nullptr) {}
List(T x, List tail): head_(make_shared(x, tail.head_)){
count_ = 1 + tail.count_;
}
int size() { return count_;}
bool empty() { return count_ == 0;}
T getMax() { assert(!empty()); return head_->max_;}
T getHead() { assert(!empty()); return head_->val_;}
List getTail() { assert(!empty()); return List(head_->next_, count_-1);}
List addHead(const T &x) {
return List(x, *this);
}
// *this should not be changed
List reverse() {
List new_list;
List it = *this; // make a "copy" first
for(int i=0; i tail, int count)
: head_(tail), count_(count) {}
shared_ptr head_;
int count_;
};
};
/**
* requires default ructor
*/
template
immutable_queue::immutable_queue()
{
front_ = List();
rear_ = List();
}
template
immutable_queue::immutable_queue(List front, List rear)
{
// front is empty only if rear is empty too
if(front.empty() && !rear.empty()) {
this->front_ = rear.reverse();
this->rear_ = List();
}
else {
this->front_ = front;
this->rear_ = rear;
}
front = List();
rear = List();
}
/**
* default destructor
*/
template
immutable_queue::~immutable_queue()
{
}
/**
* Returns the queue that adds an item into the tail of this queue without
* modifying this queue.
*
*
* e.g.
* When this queue represents the queue (2,1,2,2,6) and we enqueue the value 4 into this queue,
* this method returns a new queue (2,1,2,2,6,4)
* and this object still represents the queue (2,1,2,2,6)
*
*
* @param e
* @return
*/
template
immutable_queue immutable_queue::enqueue(const T &element)
{
//make this method faster
return immutable_queue(front_, rear_.addHead(element));
}
/**
* Returns the queue that removes the object at the head of this queue
* without modifying this queue.
*
* * e.g. * When this queue represents the queue (7,1,3,3,5,1) . * this method returns a new queue (1,3,3,5,1) * and this object still represents the queue (7,1,3,3,5,1) ** * If this queue is empty, throws range_error * * @param e * @return */ template immutable_queue immutable_queue::dequeue() { //make this method faster if(empty()) { throw range_error("Opps, this queue is empty, nothing to dequeue"); } return immutable_queue(front_.getTail(), rear_); } template immutable_queue immutable_queue::reverse() { return immutable_queue(rear_, front_); } template T immutable_queue::getMax() { //make this method faster if(empty()) { throw range_error("Opps, this queue is empty"); } T res = front_.getMax(); if(!rear_.empty()) res = max(res, rear_.getMax()); return res; } /** * Looks at the object which is the head of this queue without removing it * from the queue. * *
* e.g. * When this queue represents the queue (7,1,3,3,5,1) . * this method returns 7 and this object still represents the queue (7,1,3,3,5,1) ** * If this queue is empty, throws range_error * * @param e * @return */ template inline T immutable_queue::peek() { if(empty()) { throw range_error("Opps, this queue is empty. nothing to peek"); } return front_.getHead(); } /** * Return the number of objectes in this queue * @return */ template inline int immutable_queue::size() { return front_.size() + rear_.size(); } int main(void) { immutable_queue p1; p1 = p1.enqueue(1); p1 = p1.enqueue(2); p1 = p1.enqueue(3); assert(p1.size() == 3); immutable_queue p2 = p1.enqueue(0); p2 = p2.enqueue(4); p2 = p2.enqueue(5); p2 = p2.enqueue(6); assert(p2.size() == 7); assert(p1.size() == 3); p1 = p1.append(p2); assert(p1.size() == 10); assert(p2.getMax() == 6); immutable_queue temp = p2.reverse(); while(!temp.empty()) { cout << temp.peek() << " "; temp = temp.dequeue(); } cout << endl; return 0; }