一个vector引发的血案 - deque, vector和数组的区别

Leetcode更新到155题了,这个easy的题acceptance却不高,我好奇的点开了它。

 

Design a stack that supports push, pop, top, and retrieving the minimum element in constant time.

  • push(x) -- Push element x onto stack.
  • pop() -- Removes the element on top of the stack.
  • top() -- Get the top element.
  • getMin() -- Retrieve the minimum element in the stack.

果然easy嘛,そして:

 

class MinStack {
    struct node
    {
        int value;
        node * next;
        node(int v, node * n=0):value(v),next(n){}
    };
    node * h;
    node * min;
    
public:
    MinStack():h(0), min(0){}
    void push(int x) {
        h = new node (x, h);
        if(!min || min->value >= x)
            min = new node (x, min);
    }

    void pop() {
        if(h)
        {
            node * p;
            if(h->value == min->value)
            {
                p = min;
                min = min->next;
                delete p;
            }
            p = h;
            h = h->next;
            delete p;
        }
    }

    int top() {
        if(h)
            return h->value;
        throw;
    }

    int getMin() {
        if(min)
            return min->value;
        throw;
    }
};

   

    据说通用的STL容器非常多虑,多线程神马的都考虑了进去,静态链接也会把从来不调用的函数加进去,因此可以的话我习惯于手动去实现容器的简单操作。但是等等,怎么回事:memory limited error!无非多了N个next指针,程序就耐不住了也太傲娇了!

    那么不用链式结构了,改用数组吧。就用vector试试。

class MinStack {
    vector<int> h, min;
public:
    void push(int x) {
        h.push_back(x);
        if (min.empty() || min.back() >= x)
            min.push_back(x);
    }

    void pop() {
        if (h.back() == min.back())
            min.pop_back();
        h.pop_back();
    }

    int top() {
        return h.back();
    }

    int getMin() {
        return min.back();
    }
};    

    XX,又是mle!最后看到别人用deque,做法大同小异,就抄了过来。好在STL里的函数名称比较统一,vector和deque的抽插都叫做push_back和pop_back,改下定义就好啦:

class MinStack {
    deque<int> h, min;
public:
    void push(int x) {
        h.push_back(x);
        if(min.empty() || min.back() >= x)
            min.push_back(x);
    }

    void pop() {
        if(h.back() == min.back())
            min.pop_back();
        h.pop_back();
    }

    int top() {
        return h.back();
    }

    int getMin() {
        return min.back();
    }
};

    查了下资料,又涨知识了!deque采用数组和链表的这种策略,初始先分配一块连续内存,不够了再分配一块,然后这些先后分配的内存通过指针“通通连起来”,逻辑上就是连续的空间了。这种东西早就想写一个,没想到STL已经提供了,而且STL中的stack和queue等容器底下正是它!perfect!

   现在比较下数组、vector和deque:

   1:原理:

    数组:连续且固定的空间;

    vector:连续但不固定的空间,原空间不够了将会调用allocator进行realloc,然后通过uninitialized_copy复制旧内容。

    deque:连续的链式空间,空间不够则分配新的空间,每个内存块呈链式(不是新鲜事吧?从前学数据结构,有这样实现过string,学操作系统,其中文件管理也有用链式方法的)。

 

    2:增长方向:

    数组:长不大

    vector:逻辑上是ForwardIncremental的

    deque:显然是双向的了。

 

    3:性能:

   用下面这段简单的代码测试下,因为我用的是上上个五年计划的时候出的Thinkpad X61,N=100000000,sizeof(x

) = 12B差不多是极限了。

  用int测试时,输出4  6;

  用struct测试时,deque的循环输出7,vector在插入过程中直接bad_alloc,原因是找不到足够大的连续内存了。

 

#include <cstdlib>
#include <ctime>
#include <deque>
#include <vector>
#include <iostream>
using namespace std;

struct s
{
    int v;
    int a[2];
    s(int i):v(i){}
    ~s()
    {
        //make it a non-trivial destructor;
    }
};

main()
{
    typedef s T;
    int const N = 100000000;
    int i;
    double start;
    deque<T> d;
    vector<T> v;
    start = time(0);
    for(i=0; i<N; i++)
        d.push_back(T(i));
    for(i=0; i<N; i++)
        d.pop_back();
    cout <<time(0) - start<<endl;
    start = time(0);
    for(i=0; i<N; i++)
        v.push_back(T(i));
    for(i=0; i<N; i++)
        v.pop_back();
    cout <<time(0) - start<<endl;
}

    

    当然并非以后就弃vector不用了,如果每次访问的内存地址比较跳跃,deque的效率必然就不行了,因为要跳到另一个块上,it++当然不行,至少需要访问两次内存了。果然存在即有理。

 

posted @ 2014-11-15 12:38  chng  阅读(487)  评论(0编辑  收藏  举报
BackToTop