二叉树:节点最多只有两个子树,有左右之分,次序不可颠倒。
二叉树的建立
利用先序序列递归建立二叉树:给定一个先序遍历数组用链表创建二叉树。
建立一个二叉树节点类:
//二叉树节点的类
template<class T>
struct BinaryTreeNode
{
//节点的构造函数
BinaryTreeNode(const T& x)
:_data(x)
, _leftNode(NULL)
, _rightNode(NULL)
{}
T _data;//节点数据
BinaryTreeNode<T> *_leftNode;//左子树节点
BinaryTreeNode<T> *_rightNode;//右子树节点
};
当数组的元素不为 # 且数组的下标满足要求时,以数组元素数据建立头节点,左子树和右子树的建立属于子问题,用递归实现。
//二叉树类
template<class T>
class BinaryTree
{
typedef BinaryTreeNode<T> Node;
public:
。。。
//利用数组建立二叉树
//invalid用来标识 # 也就是数组中的空元素
BinaryTree(const T* arr, size_t size, const T& invalid)
{
size_t index = 0;
_root = CreatTree(arr, size, invalid, index);
}
Node* CreatTree(const T* arr, size_t size, const T& invalid, size_t& index)
//index传引用,可以有累加的功能,防止每次进函数都是从0开始
{
Node* root = NULL;
//当满足if条件时,创建根节点,左子树,右子树
if (arr[index] != invalid&&index < size)
{
root = new Node(arr[index]);
root->_leftNode = CreatTree(arr, size, invalid, ++index);
root->_rightNode = CreatTree(arr, size, invalid, ++index);
}
return root;
}
二叉树的复制
二叉树的复制包括创建新对象并用已存在的对象对其进行初始化,对已存在对象进行赋值操作。
//二叉树的复制(拷贝构造函数)
BinaryTree(BinaryTree<T>& t)
{
_root = CopyTree(t._root);
}
Node* CopyTree(Node* root)
{
if (root == NULL)
{
return NULL;
}
Node* cur = new Node(root->_data);
cur->_leftNode = CopyTree(root->_leftNode);
cur->_rightNode = CopyTree(root->_rightNode);
return cur;
}
//运算符的重载(还可以使用swap函数直接交换赋值)
BinaryTree<T>& operator= (BinaryTree<T>& t)
{
if (_root)
{
Destory(_root);
}
_root = CopyTree(t._root);
return *this;
}
二叉树的销毁
二叉树的销毁其实就是BinaryTree类的析构函数,用递归的方法,先判断二叉树是否为空,为空直接返回;不为空的时候,转化为子问题,先销毁左子树,再右子树,后根节点。
//二叉树的销毁(析构函数)
~BinaryTree()
{
Destory(_root);
}
void Destory(Node* root)
{
//二叉树为空
if (root == NULL)
{
return;
}
Destory(root->_leftNode);
Destory(root->_rightNode);
if (root)
{
delete root;
}
root = NULL;
}
二叉树的大小
用递归的方法,将左子树节点个数和右子树节点个数相加再加上根节点。
//第一种解法:
size_t Size1()
{
return _Size1(_root);
}
int _Size1(Node* root)
{
if (root == NULL)
{
return 0;
}
return _Size1(root->_leftNode) + _Size1(root->_rightNode) + 1;
}
创建了局部变量count用来对节点个数进行记录,只要每次传进函数的节点不为空,count都会进行累加操作, 切记参数传递的时候一定是引用,防止每次进函数时其值是0。
//第二种解法:
size_t Size2()
{
size_t count = 0;
return _Size2(_root,count);
}
int _Size2(Node* root,size_t& count)
{
if (root == NULL)
{
return 0;
}
count++;
_Size2(root->_leftNode,count);
_Size2(root->_rightNode,count);
return count;
}
二叉树的深度
二叉树的深度是指二叉树所有路径中最长的路径。
运用递归的方法,比较根节点左子树,右子树路径的长度,再对左子树,右子树进行单独处理;根节点的左子树节点会分为左子树,右子树;根节点的右子树节点也会分为左子树,右子树……不断的进行。当整棵树的节点被计算完选择最长的路径再加上根节点就是二叉树的深度。
size_t Depth()
{
return _Depth(_root);
}
size_t _Depth(Node* root)
{
if (root == NULL)
{
return 0;
}
size_t leftD = _Depth(root->_leftNode);
size_t rightD = _Depth(root->_rightNode);
return leftD > rightD ? leftD + 1 : rightD + 1;
}
二叉树的叶子节点
二叉树的叶子节点是指一个节点既没有左子树节点也没有右子树节点。
判断的条件:左子树节点为NULL,右子树节点为NULL。满足条件对其调用者返回1,然后调用者对返回的个数进行累加。
//第一种解法
size_t LeafSize1()
{
return _LeafSize1(_root);
}
int _LeafSize1(Node* root)
{
if (root == NULL)
{
return 0;
}
if (root->_leftNode == NULL&&root->_rightNode == NULL)
{
return 1;
}
return _LeafSize1(root->_leftNode) +_LeafSize1(root->_rightNode);
}
每找到一个满足条件的节点就对count进行++,再对根节点的左子树节点,右子树节点进行判断,直至整棵树的节点被访问。
//第二种解法
size_t LeafSize2()
{
size_t count = 0;
return _LeafSize2(_root, count);
}
size_t _LeafSize2(Node* root,size_t& count)
{
if (root == NULL)
{
return 0;
}
if (root->_leftNode == NULL&&root->_rightNode == NULL)
{
++count;
}
_LeafSize2(root->_leftNode,count);
_LeafSize2(root->_rightNode,count);
return count;
}
二叉树第K层节点
size_t GetKLevel(size_t k)
{
return _GetKLevel(_root,k);
}
int _GetKLevel(Node* root,size_t k)
{
//数为空时返回0
if (root == NULL)
{
return 0;
}
//当是第一层节点时,只有根节点返回1
if (k == 1)
{
return 1;
}
//当K大于1时,返回左子树第K-1层节点数加右子树第K-1层节点数
return _GetKLevel(root->_leftNode, k-1) + _GetKLevel(root->_rightNode, k-1);
}
二叉树的查找
在二叉树中查找一个指定的数
- 二叉树为空的时候,返回NULL;
- 不为空时,对二叉树的左子树节点和右子树节点分别进行判断,找到的话返回节点。
Node* FindNode(const T& x)
{
return _FindNode(_root,x);
}
Node* _FindNode(Node* root, const T& x)
{
if (root == NULL)
{
return NULL;
}
if (root->_data == x)
{
return root;
}
if(root->_leftNode)
{
Node* ret = _FindNode(root->_leftNode, x);
if (ret)
{
return ret;
}
else
{
return NULL;
}
}
if(root->_rightNode)
{
Node* cur = _FindNode(root->_rightNode, x);
if (cur)
{
return cur;
}
else
{
return NULL;
}
}
}
注:运用递归算法时,被调函数的参数在本函数中就是根节点,不再是左子树节点或右子树节点,这个问题需注意。