如何释放指针树中的所有内存
How to free all the memory in a tree of pointers
我正在为 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) {};
};
我有一个称为版本的指针数组,定义如下:
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;
}
}
}
您显然在 update() 函数中扩展了您的树:
curr->left = null;
curr->left = null;
但是当你想删除节点时,我没有看到树的任何遍历。
向节点添加析构函数并重置版本数组。
简单重复:
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;
}
}
递归删除是确保释放所有内存所必需的(如之前答案中的建议),但对我来说,问题是您正在尝试删除未使用 [=15] 分配的向量的内存 space =]运算符。
这可能会导致一些问题,如下所述post:Is it possible to delete a non-new object?
其他答案已经适当地涵盖了如何设置递归 delete
操作,但由于这是 C++,我觉得有必要补充一点,使用 smart 可以更好地处理整个结构指针 和 标准库容器。
例如,如果您的数组是 std::vector<std::unique_ptr<node> >
而 class 中的节点指针也是 unique_ptr
,那么当它超出范围。
最后,在你的问题中你说你想从头开始重新构建树。这似乎没有必要。只需为重建的树重新使用已分配的内存,您可能会轻松得多。
我正在为 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) {};
};
我有一个称为版本的指针数组,定义如下:
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;
}
}
}
您显然在 update() 函数中扩展了您的树:
curr->left = null;
curr->left = null;
但是当你想删除节点时,我没有看到树的任何遍历。
向节点添加析构函数并重置版本数组。
简单重复:
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;
}
}
递归删除是确保释放所有内存所必需的(如之前答案中的建议),但对我来说,问题是您正在尝试删除未使用 [=15] 分配的向量的内存 space =]运算符。
这可能会导致一些问题,如下所述post:Is it possible to delete a non-new object?
其他答案已经适当地涵盖了如何设置递归 delete
操作,但由于这是 C++,我觉得有必要补充一点,使用 smart 可以更好地处理整个结构指针 和 标准库容器。
例如,如果您的数组是 std::vector<std::unique_ptr<node> >
而 class 中的节点指针也是 unique_ptr
,那么当它超出范围。
最后,在你的问题中你说你想从头开始重新构建树。这似乎没有必要。只需为重建的树重新使用已分配的内存,您可能会轻松得多。