题目要求:根据键盘输入的二叉树前序遍历序列构建相应的二叉树,并计算该二叉树的叶子结点个数。
#include<iostream>
int num = 0;
//二叉树节点的固定格式
struct TreeNode
{
char val;
TreeNode* left;
TreeNode* right;
};
class Tree
{
public:
Tree();
~Tree();
TreeNode* Getroot() { return root; }//为了使root为private
void CountLeaf(TreeNode* root);
private:
TreeNode* root;
TreeNode* CreateTree();
void DestoryTree(TreeNode* root);
};
/*Tree::Tree(int x)
{
root->val = x;
root->right = NULL;
root->left = NULL;
}*/
//曾错误地写成上述内容,上述内容无法和CreateTree()构建联系
Tree::Tree()
{
root = CreateTree();//一定要给root赋值呀!!!!在此处犯过错:曾错误地只调用CreateTree()函数,未赋值给root,报错0xcccccccc。
}
Tree::~Tree()
{
DestoryTree(root);
}
TreeNode* Tree::CreatTree()//注意返回类型、类限定的前后顺序,注意指针*的位置,踩过坑
{
TreeNode* root;
char a;
std::cin >> a;
if (a == '#')
return NULL;
else
{
root = new TreeNode;//new函数知识点不牢固,容易忘记
root->val = a;
root->left = CreateTree();//一定要递归调用呀!!
root->right = CreateTree();
}
return root;
}
void Tree::CountLeaf(TreeNode* root)
{
if (root != NULL)//非常重要,不然root为空再找子节点时,会报错
{
std::cout << root->val << ' ';
if (root->left == NULL && root->right == NULL)
num++;
else
{
CountLeaf(root->left);
CountLeaf(root->right);
}
}
}
void Tree::DestoryTree(TreeNode* root)
{
if (root != NULL)//先释放叶子子树,再释放根(注意顺序,踩过坑)
{
DestoryTree(root->left);
DestoryTree(root->right);
delete root;
}
}
void main()
{
std::cout << "根据键盘输入创建一棵二叉树" << std::endl;
Tree tree;
std::cout << "前序遍历结果是" << std::endl;
tree.CountLeaf(tree.Getroot());
std::cout << std::endl;
std::cout << "二叉树的叶子结点数为" << num << std::endl;
}
查看理解参考程序之后,自己动手半独立实现,这个过程中发现了以下未被重视的小问题(犯过的部分错误,已在程序中注释)。因此,一定要自己动手,尽可能独立完成,千万不能眼高手低
。
- 由于忘记在构造函数中给root赋值,导致报错0xcccccccc。
对于0xcccccccc和0xcdcdcdcd,在 Debug模式下,VC会把未初始化的栈内存上的指针全部填成 0xcccccccc,当字符串看就是 “烫烫烫烫……”;会把未初始化的堆内存上的指针全部填成 0xcdcdcdcd,当字符串看就是 “屯屯屯屯……”;对于0xfeeefeee,是用来标记堆上已经释放掉的内存。
下面两篇文章有对此的具体解释。
内存中常见异常值的解释(比如0xcccccccc异常值)
关于C++中野指针的说明 - new与delete知识点掌握不牢固
总是忘记使用new,如在CreatTree()函数中;
程序执行完之后,自动跳到了析构函数,执行DestoryTree();
关于new和delete的使用,可参考文章new和delete用法。 - 析构函数
C++ 析构函数中提到的下面内容目前不是特别理解:
如果我们写了析构函数,就必须要注意三法则:同时编写:析构函数、赋值构造函数、赋值运算符。