有趣的函数式数据结构《一》----不可变栈
什么是不可变?往栈中插入一个元素,原来的栈保持不变,返回一个新的栈(已插入新的元素)。
push, pop,getMax 等操作都要求在 常数时间内完成。
可能读者会产生疑惑,既然要返回一个新的栈,是不是就必须先拷贝一份原来的栈,然后在新的栈中插入元素。
但是这样复杂度就是线性的,如何能够在常数时间内完成呢??
这里,就是immutable DATA STRUCTRUE 的强大。。
本文给出一个C++ 的实现版本,基本理想是利用内存共享,以及均摊时间的思想。
下图给出了实现的核心思想。
三个链表[1,2,3], [1,2,4], [1,2,5]共用节点1,2
这是因为一个节点可以有多个前驱节点指向它本身。。而栈的特性:仅仅在链表的头部插入删除的性质保证了节点的共享。
//copyrigt @ L.J.SHOU May.25, 2014
#include
using namespace std;
/**
* This class is a functional datatype, representing
* an immutable stack of objects.
*
* 1. stack is based on a list implementation
* 2. std::shared_ptr is used to collect garbage
* 3. thread safe
*/
template
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_;
};
template
class Stack
{
public:
Stack(): count_(0), head_(nullptr) {}
Stack(T x, Stack 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_;}
Stack getTail() { assert(!empty()); return Stack(head_->next_, count_-1);}
Stack addHead(const T &x) {
return Stack(x, *this);
}
Stack reverse() {
Stack new_list;
Stack it = *this; // make a "copy" first
for(int i=0; i > tail, int count)
: head_(tail), count_(count) {}
shared_ptr > head_;
int count_;
};
int main(void)
{
Stack list;
for(int i=0; i<10; ++i)
list = list.addHead(i);
//print
Stack temp = list;
while(!temp.empty()) {
cout << temp.getHead() << " ";
temp = temp.getTail();
}
cout << endl;
return 0;
}