**二叉树编程题万变不离其宗在于对递归的理解和使用
要弄懂用好递归 重要的在于一下几条:
1.搞清楚函数递归调用栈帧的变化 特别是二叉递归时的栈帧变化
2.搞清楚各个函数参数 传值和传引用 的函数参数在递归调用时值的变化。
本文中不加注明 所说的二叉树都是普通二叉树
1.定义并构建一颗二叉树
对于一颗普通二叉树的节点 至少要定义出他的值域 和指向其左右子树的左右指针域。
并定义出节点的构造函数 该函数中参数应为要赋予该节点的值 左右指针初始化为空 有树的构造函数处理。
template<class T>
struct BinaryTreeNode
{
T _value;
BinaryTreeNode<T>* _pleft;
BinaryTreeNode<T>* _pright;
BinaryTreeNode(const T& value)
: _value(value)
, _pleft(NULL)
, _pright(NULL)
{}
};
对于一颗二叉有 类成员变量中至少应有该树根节点的指针。
在实现构造函数时 运用递归思想 把一棵树分成左子树 右子树 根 三部分 。 每个子树也分成这三部分。
//按前序遍历构造二叉树 ‘#’代表该位置的节点为空
char arr[] = { '1', '2', '3', '#', '#', '4', '#', '#', '5', '6' };
//变量invalid代表该节点为空 //数组的索引必须传引用
BinaryTree<char> BiTree(arr, sizeof(arr) / sizeof(arr[0]), '#');
Node* _CreateBinaryTree2(const T arr[], size_t size, size_t& index, const T invalid)
{
Node* pRoot = NULL;
if (index < size && invalid != arr[index])
{
pRoot = new Node(arr[index]);
pRoot->_pleft = _CreateBinaryTree2(arr, size, ++index, invalid);
pRoot->_pright = _CreateBinaryTree2(arr, size, ++index, invalid);
//在这里接住下一层的返回值
}
return pRoot;
}
BinaryTree()
:_pRoot(NULL)
{}
BinaryTree(const T arr[], size_t size, const T& invalid)
{
assert(arr);
size_t index = 0;
//_CreateBinaryTree1(_pRoot, arr, size, index, invalid);
_pRoot = _CreateBinaryTree2(arr, size, index, invalid);
}
二叉树的拷贝构造函数 类似于二叉树的构造函数
Node* _CopyBinaryTree(Node* pRoot)
{
Node* pNewRoot = NULL;
if (pRoot){
pNewRoot = new Node(pRoot->_value);
pNewRoot->_pleft = _CopyBinaryTree(pRoot->_pleft);
pNewRoot->_pright = _CopyBinaryTree(pRoot->_pright);
}
return pNewRoot;
}
BinaryTree(const BinaryTree<T>& bt)
{
_pRoot = _CopyBinaryTree(bt._pRoot);
}
二叉树的析构函数
有所不同 它的逻辑是 先析构左右子树
再析构根节点 因为如果先析构·根节点的话 左右子树将找不到 形成内存泄漏。
~BinaryTree()
{
_DestoryBinaryTree(_pRoot);
}
void _DestoryBinaryTree(Node*& pRoot)
{
if (pRoot){
_DestoryBinaryTree(pRoot->_pleft);
_DestoryBinaryTree(pRoot->_pright);
delete pRoot;
pRoot = NULL;
}
}
二叉树类的赋值运算符重载 注意三个问题
1.不可以自己给自己复制。
2.赋值之前必须进行析构,释放原来自己的内存空间
3.调用二叉树的构造逻辑在根结点上构造新二叉树
BinaryTree<T>& operator = (const BinaryTree<T>& bt)
{
if (this != &bt){
_DestoryBinaryTree(_pRoot);
_pRoot = _CopyBinaryTree(bt._pRoot);
}
return *this;
}
2.二叉树的前序、中序、后序、层序遍历
1.前序遍厉根 -> 左 ->右
先遍历树的根和每一棵子树的根 再递归遍历左子树 左子树遍历好以后退回到根节点这一层函数栈帧再递归遍历右子树 知道每一个节点
都经过遍历:
void _PreOrder(Node* pRoot)
{
if (pRoot){
cout << pRoot->_value << " ";
_PreOrder(pRoot->_pleft);
_PreOrder(pRoot->_pright);
}
}
void PreOrder()
{
cout << "PreOrder" << endl;
_PreOrder(_pRoot);
cout << endl;
}
前序遍历的非递归实现
//把问题分成 树的左路 与 树的其他部分 两块
//先将节点压栈 遍历后再出栈 以此模仿递归
void Pre_Order()
{
stack<Node*> s;
Node* cur = _pRoot;
if (_pRoot == NULL)
return;
while (cur || !s.empty()){
//栈不空说明还有节点未遍历
while (cur){
cout << cur->_value << " ";
s.push(cur);
cur = cur->_pleft;
}
Node* top = s.top();//top 的左边不可能有还没遍历的节点
s.pop();
if (top->_pright != NULL)
cur = top->_pright;
}
co