关于野指针报错问题,大多是两种情况:一是没有初始化指针指向地址,二是指针指向了已经删除的地址。
这里给出我报错的源码,代码是二叉搜索树,findNode()函数在读入一个数据后在二叉搜索树中查找,如果找到了,尝试用deleteNode()函数去删除findNode()返回的这个结点。
#include<iostream>
using namespace std;
typedef struct treenode {
int val;
treenode* lson;
treenode* rson;
treenode() :val(0), lson(NULL), rson(NULL) {}
treenode(int x) :val(x), lson(NULL), rson(NULL) {}
};
void addson(int x, treenode* root) {
if (x < root->val) {
if (root->lson == NULL) {
root->lson = new treenode(x);
return;
}
addson(x, root->lson);
}
else {
if (root->rson == NULL) {
root->rson = new treenode(x);
return;
}
addson(x, root->rson);
}
}
treenode* findNode(int x, treenode* root) {
if (root == NULL)
return NULL;
else if (x == root->val)
return root;
else if (x < root->val)
findNode(x, root->lson);
else
findNode(x, root->rson);
}
void inOrder(treenode* root) {
if (root != NULL) {
inOrder(root->lson);
cout << root->val << " ";
inOrder(root->rson);
}
}
void deleteNode(treenode* tar) {
if (tar == NULL)
return;
treenode* p = NULL;
treenode* q = NULL;
if (tar->lson == NULL) {
p = tar;
tar = tar->rson;
delete(p);
}
else if (tar->rson == NULL) {
p = tar;
tar = tar->lson;
delete(p);
}
else {
p = tar;
q = tar->lson;
while (q->rson != NULL) {
p = q;
q = q->rson;
}
tar->val = q->val;
if (p != tar)
p->rson = q->lson;
else
p->lson = q->lson;
delete(q);
}
}
int main() {
int n, x, tag;
cin >> n;
cin >> x;
treenode* root = new treenode(x);
for (int i = 1;i < n;i++) {
cin >> x;
addson(x, root);
}
inOrder(root);
cin >> tag;
treenode* target = findNode(tag, root);
if (target == NULL)
cout << "target not found" << endl;
else
cout << target->val << " find!" << endl;
deleteNode(target);
inOrder(root);
return 0;
}
这里删除结点分为三种情况:1.如果没有左子树或者没有右子树,直接把子树给接上,然后删除这个结点。2.如果左右子树都存在,那我们找到中序遍历的前一个数替换这个数,然后删除中序遍历的前一个数(这里一定会是第一种情况,不清楚的话想想中序遍历过程就懂了)
乍一看上去没什么毛病,跑了一下报了0xFFFFFFFFFFFFFFF7这个bug,但是每个结点都是new出来的,也写了构造函数,为什么报了野指针?
调试以后发现这段代码有问题:
if (tar->lson == NULL) {
p = tar;
tar = tar->rson;
delete(p);
}
else if (tar->rson == NULL) {
p = tar;
tar = tar->lson;
delete(p);
}
1.把p(treenode* 类型)修改为tar,相当于是把tar指向的地址复制给了p
2.tar=tar->rson,相当于把tar->rson指向的地址复制给了tar
3.delete(p),把原tar指向的空间给删除了
于是有了这个问题:在二叉搜索树中,双亲结点的指针还是指向tar原来的地址,但这个地址已经被delete(p)这一步删除了,在我们再一次中序遍历二叉树时,就出现了野指针报错
解决方法有两种,一是我们再写一个findParent()函数,在deleteNode()里调用这个findParent()找到双亲结点去修改父节点指针,二是使用递归,由于递归调用的参数是root->lson或者root->rson,在函数体中我们修改指针,父节点指向也就被修改了。由于递归代码量小一点就改写递归了这里:
void deleteNode(TreeNode*& root, int x) {
if (root == NULL)
return;
if (x < root->val) {
deleteNode(root->lson, x);
}
else if (x > root->val) {
deleteNode(root->rson, x);
}
else {
if (root->lson == NULL) {
TreeNode* temp = root;
root = root->rson;
delete temp;
}
else if (root->rson == NULL) {
TreeNode* temp = root;
root = root->lson;
delete temp;
}
else {
TreeNode* temp = root->lson;
while (temp->lson != NULL) {
temp = temp->rson;
}
root->val = temp->val;
deleteNode(root->lson, temp->val);
}
}
}
修改后代码可以正常编译运行: