ASAN:将二叉树平展为链表时释放后堆使用

ASAN: heap-use-after-free when flattening a binary tree to a linked list

本文关键字:释放 链表 二叉树 ASAN      更新时间:2023-10-16

这是一个Leetcode问题,将二叉树展平到链表。

我的解决方案非常简单。对于节点rootroot->right推送到堆栈上。将root->left设置为root->right,并将root->left设置为 NULL。

我收到运行时错误:

地址清理器:在 PC 上的地址0x603000000108释放后堆使用 0x00000046bf70 bp 0x7ffc5d6c5f70 sp 0x7ffc5d6c5f68

导致错误的原因是什么?

这是我的代码:

class Solution {
public:
void flat_tree(TreeNode* pre, TreeNode* root, stack<TreeNode*>& s){ 
if(root == NULL){
if(s.empty()) return;
else{
TreeNode* newroot = s.top();  
s.pop();
pre->right = newroot;
flat_tree(pre, newroot,s);
}
}else{
if(root->left == NULL) {
flat_tree(root, root->right, s); 
}else if(root->right == NULL){
root->right = root->left;
flat_tree(root, root->right, s); 
}else{
TreeNode* right = root->right;
root->right = root->left;
s.push(right);
flat_tree(root, root->right, s); 
}
}
}   
void flatten(TreeNode* root) {
stack<TreeNode*> s;
flat_tree(NULL ,root,s);  
}
};

这非常接近。在将左节点指针移动到根的右侧后,需要将其清空,从而防止当测试套件遵循指向同一内存位置的两个指针时发生的双释放堆损坏。

这是工作代码:

void flat_tree(TreeNode* pre, TreeNode* root, stack<TreeNode*>& s){ 
if(root == NULL){
if(s.empty()) return;
else{
TreeNode* newroot = s.top();  
s.pop();
pre->right = newroot;
flat_tree(pre, newroot,s);
}
}else{
if(root->left == NULL) {
flat_tree(root, root->right, s); 
}else if(root->right == NULL){
root->right = root->left;
root->left = NULL; /* set the left pointer to null */
flat_tree(root, root->right, s); 
}else{
TreeNode* right = root->right;
root->right = root->left;
root->left = NULL; /* do the same here */
s.push(right);
flat_tree(root, root->right, s); 
}
}
}   

我还建议重组分支以避免重复逻辑:

void flat_tree(TreeNode* pre, TreeNode* root, stack<TreeNode*>& s) { 
if (root) { 
if (root->right) {
s.push(root->right);
}
root->right = root->left;
root->left = NULL;
flat_tree(root, root->right, s); 
}
else if (!s.empty()) {
pre->right = s.top();
s.pop();
flat_tree(pre, pre->right, s);
}
}