如何释放指针树中的所有内存

时间:2018-01-23 15:04:06

标签: c++ pointers struct segment-tree

我为Codechef上的以下问题编写了一个持久的分段树:https://www.codechef.com/problems/GIVEAWAY,我的节点结构如下所示:

struct node {
    int val;
    node *left, *right;
    node (int val, node *left, node *right) : val(val), left(left), right(right) {};
};

我有一个名为version的指针数组,定义如下:

node *version[maxn];

在我的代码中,我想从头开始构建段树,但我想释放以前分配的内存。我不知道该怎么做。我尝试过像

这样的事情
for (int i = 1; i <= N; i++) {
        delete version[i];
    }

然而,当我提交时,我似乎并没有减少很多内存使用量。 早些时候它显示了大约1000mb,但现在它显示了960mb。我认为这是因为我没有释放大量内存,因为有一整个指针树。但我不确定如何释放所有这些。

这是我的其余代码,如果你想引用它。

#include <iostream>
#include <vector>
#include <algorithm>
#include <climits>
#include <stdio.h>
#include <queue>
#include <set>
#include <list>
#include <cmath>
#include <assert.h>
#include <bitset>
#include <cstring>
#include <map>
#include <unordered_map>
#include <unordered_set>
#include <iomanip> //cout << setprecision(node) << fixed << num
#include <stack>
#include <sstream>


#define all(x) x.begin(), x.end()
#define pb push_back
#define mp make_pair
#define fi first
#define se second
#define print(arr) for (auto it = arr.begin(); it != arr.end(); ++it) cout << *it << " "; cout << endl;
#define debug(x) cout << x << endl;
#define debug2(x,y) cout << x << " " << y << endl;
#define debug3(x,y,z) cout << x << " " << y << " " << z << endl;

typedef long long int ll;
typedef long double ld;
typedef unsigned long long int ull;
typedef std::pair <int, int> ii;
typedef std::vector <int> vi;
typedef std::vector <ll> vll;
typedef std::vector <ld> vld;

const int INF = int(1e9);
const ll INF64 = ll(1e18);
const ld EPS = 1e-9, PI = 3.1415926535897932384626433832795;
using namespace std;
const int maxn = (int)5e5+10;
int A[maxn], N;

struct node {
    int val;
    node *left, *right;
    node (int val, node *left, node *right) : val(val), left(left), right(right) {};
    //~node () { delete left; delete right; }
};

#define null new node (0, NULL, NULL);

node *version[maxn];

void update(node *prev, node *curr, int L, int R, int idx, int val) {
    if (L == R) {
        assert(idx == L);
        curr->val = val;
    }
    else {
        int mid = (L+R)/2;
        if (idx <= mid) {
            curr->right = prev->right;
            curr->left = null;
            update(prev->left, curr->left, L, mid, idx, val);
        }
        else {
            curr->left = prev->left;
            curr->right = null;
            update(prev->right, curr->right, mid+1, R, idx, val);
        }
        curr->val = curr->right->val + curr->left->val;
    }
}

int query(node *curr, int L, int R, int li, int ri) {
    if (ri < L || li > R)
        return 0;
    if (li <= L && ri >= R)
        return curr->val;
    int mid = (L+R)/2;
    return query(curr->left, L, mid, li, ri) + query(curr->right, mid+1, R, li, ri);
}

map <int, int> occ;

void build () {
    //cout << "building..\n";
    vector <ii> V;
    for (int i = 1; i <= N; i++) {
        V.pb(mp(A[i], i));
    }
    sort(all(V));
    occ.clear();
    for (int i = 1; i <= N; i++) {
        delete version[i];
    }
    for (int i = 1; i <= N; i++) {
        ii e = V[i-1];
        occ[e.fi] = i;
        version[i] = null;
        update(version[i-1], version[i], 1, N, e.se, 1);
    }
}

int main() {
    scanf("%d", &N);
    for (int i = 1; i <= N; i++) {
        scanf("%d", &A[i]);
    }
    version[0] = null;
    version[0]->right = version[0];
    version[0]->left = version[0];
    int Q;
    scanf("%d", &Q);
    int block = (int)sqrt(Q);
    for (int i = 0; i < Q; i += block) {
        build();
        vector <ii> updates;
        for (int j = i; j < i+block && j < Q; j++) {
            int type;
            scanf("%d", &type);
            if (type == 0) {
                int a, b, c;
                scanf("%d %d %d", &a, &b, &c);
                auto it = occ.lower_bound(c);
                int cnt = 0;
                if (it != occ.begin()) {
                    it = prev(it);
                    cnt = query(version[it->second], 1, N, a, b);
                }
                int ans = b-a+1-cnt;
                for (ii update : updates) {
                    int idx = update.fi;
                    int pre = A[idx];
                    int nw = update.se;
                    if (a <= idx && idx <= b) {
                        if (nw >= c && pre < c)
                            ans++;
                        if (nw < c && pre >= c)
                            ans--;
                    }
                }
                printf("%d\n", ans);
            }
            else {
                int a, b;
                scanf("%d %d", &a, &b);
                updates.pb(mp(a, b));
            }
        }
        for (ii update : updates) {
            A[update.fi] = update.se;
        }
    }
}

帮助会受到很多赞赏!

编辑:

对此最好的解决方案是创建一个没有指针的持久分段树。我可以轻松地重新创建树而不必递归地删除所有内存,这实现起来非常麻烦和烦人,特别是因为我不熟悉指针。这大大减少了内存使用量。

新节点如下所示:

struct node {
    int val;
    int left, right;
    node() : val(0), left(0), right(0) {}
    node(int val) : val(val), left(0), right(0) {}
    node(int val, int l, int r) : val(val), left(l), right(r) {}
};

如果有人有兴趣,请执行以下内容。

#include <iostream>
#include <vector>
#include <algorithm>
#include <climits>
#include <stdio.h>
#include <queue>
#include <set>
#include <list>
#include <cmath>
#include <assert.h>
#include <bitset>
#include <cstring>
#include <map>
#include <unordered_map>
#include <unordered_set>
#include <iomanip> //cout << setprecision(node) << fixed << num
#include <stack>
#include <sstream>


#define all(x) x.begin(), x.end()
#define pb push_back
#define mp make_pair
#define fi first
#define se second
#define print(arr) for (auto it = arr.begin(); it != arr.end(); ++it) cout << *it << " "; cout << endl;
#define debug(x) cout << x << endl;
#define debug2(x,y) cout << x << " " << y << endl;
#define debug3(x,y,z) cout << x << " " << y << " " << z << endl;

typedef long long int ll;
typedef long double ld;
typedef unsigned long long int ull;
typedef std::pair <int, int> ii;
typedef std::vector <int> vi;
typedef std::vector <ll> vll;
typedef std::vector <ld> vld;

const int INF = int(1e9);
const ll INF64 = ll(1e18);
const ld EPS = 1e-9, PI = 3.1415926535897932384626433832795;
using namespace std;

const int maxn = (int)5e5+10;
int A[maxn], upd[maxn], N;


struct node {
    int val;
    int left, right;
    node() : val(0), left(0), right(0) {}
    node(int val) : val(val), left(0), right(0) {}
    node(int val, int l, int r) : val(val), left(l), right(r) {}
};

node stree[35*maxn];
int root[maxn], nodeCnt = 0;

void update(int old, int &curr, int L, int R, int idx, int val) {
    curr = ++nodeCnt;
    stree[curr] = stree[old];
    if (L == R) {
        assert(idx == L);
        stree[curr].val += val;
    }
    else {
        int mid = (L+R)/2;
        if (idx <= mid) {
            update(stree[old].left, stree[curr].left, L, mid, idx, val);
        }
        else {
            update(stree[old].right, stree[curr].right, mid+1, R, idx, val);
        }
        stree[curr].val = stree[stree[curr].left].val + stree[stree[curr].right].val;
    }
}

int query (int curr, int L, int R, int li, int ri) {
    if (curr == 0 || ri < L || li > R)
        return 0;
    if (li <= L && ri >= R)
        return stree[curr].val;
    int mid = (L+R)/2;
    return query(stree[curr].left, L, mid, li, ri) + query(stree[curr].right, mid+1, R, li, ri);
}

map <int, int> occ;

void build () {
    //cout << "building..\n";
    vector <ii> V;
    for (int i = 1; i <= N; i++) {
        V.pb(mp(A[i], i));
    }
    sort(all(V));
    occ.clear();
    memset(root, 0, sizeof(root));
    for (int i = 0; i <= nodeCnt; i++) {
        stree[i] = node();
    }
    nodeCnt = 0;
    for (int i = 1; i <= N; i++) {
        ii e = V[i-1];
        occ[e.fi] = i;
        update(root[i-1], root[i], 1, N, e.se, 1);
    }
}

int main() {
    scanf("%d", &N);
    for (int i = 1; i <= N; i++) {
        scanf("%d", &A[i]);
    }
    int Q;
    scanf("%d", &Q);
    int block = (int)sqrt(Q);
    for (int i = 0; i < Q; i += block) {
        build();
        vector <pair <int, ii>> updates;
        memset(upd, 0, sizeof(upd));
        for (int j = i; j < i+block && j < Q; j++) {
            int type;
            scanf("%d", &type);
            if (type == 0) {
                int a, b, c;
                scanf("%d %d %d", &a, &b, &c);
                auto it = occ.lower_bound(c);
                int cnt = 0;
                if (it != occ.begin()) {
                    it = prev(it);
                    cnt = query(root[it->second], 1, N, a, b);
                }
                int ans = b-a+1-cnt;
                for (pair <int, ii> update : updates) {
                    int idx = update.fi;
                    int pre = A[idx];
                    int nw = update.se.fi;
                    if (upd[idx] != update.se.se) continue;
                    if (a <= idx && idx <= b) {
                        if (nw >= c && pre < c)
                            ans++;
                        if (nw < c && pre >= c)
                            ans--;
                    }
                }
                printf("%d\n", ans);
            }
            else {
                int a, b;
                scanf("%d %d", &a, &b);
                updates.pb(mp(a, mp(b, j)));
                upd[a] = j;
            }
        }
        for (pair <int, ii> update : updates) {
            A[update.fi] = update.se.fi;
        }
    }
}

4 个答案:

答案 0 :(得分:2)

简单复发:

void releaseNode(node* n)
{
  if (!n) return;
  releaseNode(n->left);
  releaseNode(n->right);
  delete n;
}

循环本身的更新:

for (int i = 1; i <= N; i++)
{
  releaseNode(version[i]);
}

如果数组version的大小始终是编译时常量,则可以将其包装成一个简单的函数:

template <size_t N>
void releaseArrayOfNodes(node*(&array)[N])
{
  for (int i = 1; i <= N; i++)
  {
    releaseNode(array[i]);
  }
}

然后写下:

releaseArrayOfNodes(version);

对不起,我没有注意到这个事实,递归解决方案可能不适合这里,今天对我来说糟糕的一天,不知道为什么。

您可以尝试迭代解决方案:

void releaseNode(node* n)
{
    std::stack<node*> context;
    context.push(n);

    while (!context.empty())
    {
        node* top = context.top();
        context.pop();

        if (top->left != nullptr)
            context.push(top->left);

        if (top->right != nullptr)
            context.push(top->right);

        delete top;
    }
}

答案 1 :(得分:1)

递归删除是确保释放所有内存所必需的(如上一个答案中的建议)但对我来说问题是你试图删除不使用“new”运算符分配的向量的内存空间。

这可能会导致一些问题,如以下帖子中所述:Is it possible to delete a non-new object?

答案 2 :(得分:1)

其他答案已经适当地介绍了如何设置递归delete操作,但由于这是C ++,我觉得有必要补充一点,使用智能指针和标准库容器

例如,如果你的数组是std::vector<std::unique_ptr<node> >而且类中的节点指针也是unique_ptr,那么当它超出范围时,整个事情就会很好地清理掉。< / p>

最后,在您的问题中,您声明要从头开始再次构建树。这似乎没有必要。只需重新使用已分配的内存用于重建树,您可能会更容易。

答案 3 :(得分:0)

你显然在update()函数中扩展了你的树:

curr->left = null;
curr->left = null;

但是当你想要删除节点时,我没有看到树的任何遍历。

向节点添加析构函数并重置版本数组。