<h1>实现</h1>
在c++中,我们非常喜欢使用指针。因为指针非常适合人们的思考方式。
所以我们来优化一下指针的速度。
你们想一下:嵌套7,8层数组和一些平等无嵌套的箭头,哪个更容易理解?
当然是前者啦
指针非常亲和人们的语言和思维习惯,所以被众多OIer所追求。
当然你做工程时写指针 throw 出一堆 Error 时当然会绝望
所以说,为了获得更好的指针体验,我们现在分析一下,为什么指针比较慢。
我们来回顾一下图的常规写法:
struct Node{ struct Edge *firstEdge; // Other Attributes. }node[MAXN]; struct Edge{ Node s,t; int w; Edge *next; Edge(Node s,Node t,int w) : s(s), t(t), w(w), next(s->firstEdge) {} // Other Attributes. };
这种方式的缺点在哪里呢?我们发现建边的时候:
inline void add(int u,int v,int w){ node[u].firstEdge = new Edge(&node[u],&node[v],w); //or node[v].firstEdge = new Edge(&node[v],&node[u],w); }
会调用1~2次new函数,我们看一看new函数的执行过程:
A* pa = new A(3);
等价于:
A pa = (A)malloc(sizeof(A)); pa->A::A(3); return pa;
这是在做什么呢?它就是:获得一块内存空间、调用构造函数、返回正确的指针。
是不是很麻烦?我们能不能直接返回呢?
于是,我们手动实现new
的过程。
我们这样写:
struct Node{ struct Edge *firstEdge; // Other Attributes. }node[MAXN]; struct Edge{ Node s,t; int w; Edge *next; // Other Attributes. }pool[MAXM 2],frog = pool; Edge New(Node s,Node *t,int w){ Edge *ret = ++frog; frog->s = s;frog->t = t; frog->w = w;frog->next = s->firstEdge; return ret; }
这样我们在调用的时候只需:
inline void add(int u,int v,int w){ node[u].firstEdge = New Edge(&node[u],&node[v],w); // or node[v].firstEdge = New Edge(&node[v],&node[u],w); }
发现了吗?我们其实就是预先定义出了所有要用的边(pool
),然后用一个尾指针(frog
)来指向。
每次加边时,我们直接另指针后移,然后获取一个新的未被初始化过的Edge变量,对其进行初始化即可。
其他步骤和普通指针一样即可。
所以说,我们在预定义内存池时,我们必须要算好开多少空间。
那么,内存池有什么好处呢?
<h1>优点</h1>
- 速度比
new
快了不少。 - 我们在需要精确定位第$ i $条边时,不需要多定义一个$ num $成员变量,省下了空间和时间。
那么,我们再拿一个线段树的例子来加深理解。
线段树原实现(部分)
struct SegmentTree{ int l,r,sum,tag; SegmentTree lc,rc; SegmentTree(int l,int r,SegmentTree lc,SegmentTree rc) : l(l), r(r), sum(0), tag(0), lc(lc), rc(rc) {}; static SegmentTree *build(int l,int r){ int mid = (l + r) >> 1; return (l == r) ? SegmentTree(l,r,NULL,NULL) : SegmentTree(l,r,build(l,mid),build(mid + 1,r)); } // Other Attributes. }*segt;
我们对new
过程进行分析。
每次执行new
时,我们相当于初始化一个SegmentTree
类型的变量。那么我们可以如此初始化:
SegmentTree New(int l,int r,SegmentTree lc,SegmentTree *rc){ SegmentTree *ret = ++frog; ret->l = l;ret->r = r; ret->lc = lc;ret->rc = rc; ret->sum = ret->tag = 0; return ret; }
那么,现在的实现就需要这样:
struct SegmentTree; SegmentTree New(int,int,SegmentTree ,SegmentTree *); struct SegmentTree{ int l,r,sum,tag; SegmentTree lc,rc; static SegmentTree *build(int l,int r){ int mid = (l + r) >> 1; return (l == r) ? New(l,r,NULL,NULL) : New(l,r,build(l,mid),build(mid + 1,r)); } // Other Attributes. }pool[MAXN 4],frog = pool,*segt; SegmentTree New(int l,int r,SegmentTree lc,SegmentTree *rc){ SegmentTree *ret = ++frog; ret->l = l;ret->r = r; ret->lc = lc;ret->rc = rc; ret->sum = ret->tag = 0; return ret; }
好了,相信你学会了内存池,就能抛弃掉毒瘤的数组写法和超级慢的new
写法,能够获得更好的指针体验。
2 comments
这些东西我早就会了。。你也太菜了吧
Orz wyh!