我为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;
}
}
}
答案 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;
但是当你想要删除节点时,我没有看到树的任何遍历。
向节点添加析构函数并重置版本数组。