-2

I'm coding a persistent segment tree for the following problem on Codechef: https://www.codechef.com/problems/GIVEAWAY, and my struct for the node looks like this:

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

I have an array of pointers called version, defined like this:

node *version[maxn];

In my code, I want to build the segment tree from scratch, but I want to free the previously allocated memory. I'm not sure how to do this. I've tried doing something like

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

However when I submit, I don't seem to have reduced memory usage by a lot. Earlier it showed about 1000mb, but now it shows 960mb. I think it is because I haven't freed a lot of memory, because there is an entire tree of pointers. But I'm not sure how to free all of them.

This is the rest of my code, if you want to refer to it.

#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;
        }
    }
}

Help would be appreciated a lot!

EDIT:

Well the best solution for this would be to create a persistent segment tree without pointers. I can easily recreate the tree without having to delete all the memory recursively, which is very buggy and annoying to implement, especially since I'm not familiar with pointers. This considerably reduces memory usage.

The new node looks like:

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) {}
};

Here's the implementation if anyone is interested.

#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;
        }
    }
}
SinByCos
  • 613
  • 1
  • 10
  • 22
  • I think it would be better if you didn't delete memory until the program terminates, since that reduces performance. You can do this by either reusing nodes and/or use an array with indices that imitate pointer nodes. – Cpp plus 1 Jan 23 '18 at 15:06
  • put in the node destructor the deletion of its left & right; as you did ... but before you do that set the left->right = null_ptr & right->left to null_ptr (if left & right respectively are not null) else you'll end up with a double delete. – UKMonkey Jan 23 '18 at 15:10
  • As a competitive programmer I need to use such shortcuts to write code quickly! Sorry if it seems bad :( – SinByCos Jan 23 '18 at 15:12
  • 5
    _to write code quickly_ bullshit! –  Jan 23 '18 at 15:12
  • 1
    ***As a competitive programmer I need to use such shortcuts to write code quickly!*** Why? Is there a time limit for how long you type? Can't you edit and test your code in an IDE and copy / paste? – drescherjm Jan 23 '18 at 15:12
  • As you may or may not know, competitive programming contests do have time limits.. – SinByCos Jan 23 '18 at 15:13
  • The problem calls for using an array, not a tree, – Maxim Egorushkin Jan 23 '18 at 15:13
  • 1
    ***competitive programming contests do have time limits*** I don't think your typing speed is part of the limit. – drescherjm Jan 23 '18 at 15:14
  • any good IDE will auto complete that for you; you're just going to end up spending more time writing the #define and then trying not to use the variable "pb" in any of your classes. – UKMonkey Jan 23 '18 at 15:14
  • I copy paste my template each time. Its a common practice, the best competitive programmers use even more contrived templates. Anyway, this argument is pointless. If anyone can answer my question that would be far more helpful. – SinByCos Jan 23 '18 at 15:15
  • @UKMonkey I'm not sure what I mean. I think this calls for a recursive function to delete all the memory in the tree, but I got SIGSEGV the last time I tried it. – SinByCos Jan 23 '18 at 15:20
  • put the delete left&right back in; create a test with just 2 nodes and then use your debugger to look at *exactly* what's going on. I promise you you'll write code faster when you understand every line than when you save yourself just a few character presses. – UKMonkey Jan 23 '18 at 15:24
  • @SinByCos You were probably trying to delete a non-existing node or node already freed. Take a look at my answer. Remember to set empty nodes to `nullptr`, this will save you some errors. Also, I think that tree may not be the best option here. And finally, part about being *competitive programmer who needs to use such shortcuts to write code quickly* is a story with dragons and magic. Good luck with debugging such code when you get SIGSEGV like you did. – Mateusz Grzejek Jan 23 '18 at 15:26
  • Why are you wasting you time on "competitive programming" in the first place? Most likely you are just going to pick up a bunch of bad habits. Join an Open Source project instead and spend your time learning while doing something that actually has value. – Jesper Juhl Jan 23 '18 at 16:48
  • Lol, I'm a high school student and I'm aiming for the IOI. I feel its better to develop fundamental algorithmic skills first, learning actual development can come any time later. :) – SinByCos Jan 23 '18 at 17:42

4 Answers4

2

Simple recurrence:

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

And an update for the loop itself:

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

If size of the array version is always a compile-time constant, you can wrap this into a simple function:

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

And then just write:

releaseArrayOfNodes(version);

Sorry, I did not notice the fact, that recursive solution may not be an option here, bad day for me today, don't know why.

You can try iterative solution:

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;
    }
}
Mateusz Grzejek
  • 11,698
  • 3
  • 32
  • 49
  • This seems like it should work, but I dont know why my code just aborts when I run it on a small test case. :/ – SinByCos Jan 23 '18 at 15:23
  • The test case used was this: 5 1 2 3 4 5 3 0 1 5 10 1 2 20 0 1 3 10 – SinByCos Jan 23 '18 at 15:23
  • Just a note on this, recursive solutions are prone to stack overflows. If you cannot guarantee that the size of the tree will be small enough to not have this problem, you need to transform it to an iterative solution. – patatahooligan Jan 23 '18 at 15:26
  • This will suffer the same problem that the OP had with their destructor. `releaseNode(n->left)` leads to `releaseNode(left->right)` leads to ... an infinate loop (although OP had nested destructors which died rather quickly) You need to blank the right and left of the left and right nodes respectively – UKMonkey Jan 23 '18 at 15:26
  • Why would this lead to an infinite loop? If at any point the node is a nullptr it returns, right? – SinByCos Jan 23 '18 at 15:29
  • @SinByCos It's not about infinite loop. This kind of recursion may easily lead to stack overflow if tree is big. Iterative version is required here. Sorry, I didn't noticed that you have already tried this solution, see updated answer. – Mateusz Grzejek Jan 23 '18 at 15:46
  • Still not sure why but my code still aborts. :/ Also it won't lead to stack overflow because the size of the tree will never be too large, as you can see by the constraints of the problem and what my persistent segment tree tries to do. – SinByCos Jan 23 '18 at 16:09
  • Could it be because in my update i'm assigning curr->left = prev->left, hence meaning when I call delete I'm trying to delete some pointers that weren't created using the "new" operator? – SinByCos Jan 23 '18 at 16:11
1

The recursive delete is necessary to ensure to free all memory (like suggest in previous answer) but for me the problem is that you are trying to delete the memory space of vector allocated without using "new" operator.

This could cause some problems like explained in the following post: Is it possible to delete a non-new object?

LucaG
  • 74
  • 9
1

The other answers have appropriately covered how to set up the recursive delete operation, but since this is C++ I feel obliged to add that this entire structure would be far better handled using smart pointers and the standard library containers.

As an example, if your array was instead a std::vector<std::unique_ptr<node> > and the node pointers inside the class were also unique_ptr, then the entire thing would be nicely cleaned up when it went out of scope.

Finally, in your question you state that you want to build the tree again from scratch. This does not appear necessary. You would likely have a far easier time simply reusing the already-allocated memory for the rebuilt tree.

Qwertycrackers
  • 626
  • 4
  • 12
0

You apparently extend your tree in the update() function:

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

but I don't see any traversal of the tree when you want to delete the nodes.

Add a destructor to the node and reset the version array.

Thinkeye
  • 888
  • 12
  • 22