1.二叉树的定义
二叉树( binary tree)t 是有限个元素的集合(可以为空)。当二叉树非空时,其中有一个称为根(root)的元素,余下的元素(如果有的话)被组成2个二叉树,分别称为t的左子树和右子树.
你会发现二叉树的定义都含有递归的思想,没错二叉树的很多操作都要依赖递归完成。
完全二叉树:
从满二叉树上删除有限个元素产生的二叉树。比较规则。
二叉树的遍历
遍历是二叉树中最常用的操作:
前序遍历:是先访问父节点并输出,前序遍历左子树,前序遍历右子树。
代码如下:
template <class T>
void linkedBinaryTree<T>::preOrder(binaryTreeNode<T> *t){
if (t != NULL){
visit(t);
preOrder(t->leftChild);
preOrder(t->rightChild);
}
}
中序遍历:
先中序遍历左子树,在输出父节点,在中序遍历右子树。
template <class T>
void linkedBinaryTree<T>::inOrder(binaryTreeNode<T> *t){
if (t!=NULL){
inOrder(t->leftChild);
visit(t);
inOrder(t->rightChild);
}
}
后序遍历:
先后序遍历左子树,在后序遍历右子树,在输出父节点。
template <class T>
void linkedBinaryTree<T>::postOrder(binaryTreeNode<T> *t){
if (t!=NULL){
postOrder(t->leftChild);
postOrder(t->rightChild);
visit(t);
}
}
层次遍历:
按从顶层到底层的次序访问树中元素,在同一层中,从左到右进行访问。依赖队列实现
template <class T>
//队列实现层次遍历
void linkedBinaryTree<T>::levelOrder(binaryTreeNode<T> *t){
arrayQueue<binaryTreeNode<T>*> q;
while (t!=NULL){
visit(t);
//将节点的孩子们放到队列中
if (t->leftChild!=NULL){
q.push(t->leftChild);
}
if (t->rightChild!=NULL){
q.push(t->rightChild);
}
//提取下一个访问节点
try {
t = q.front();
}catch (int e){ return;}
q.pop();
}
}
可以看到前三种遍历方式都要依赖递归实现。
3.二叉树的创建
1)利用层次遍历结构直接创建:
层次遍历是按从顶层到底层的次序访问树中元素,在同一层中,从左到右进行访问。
实现代码:
template<class T>
//按层次遍历的顺序创建树
void linkedBinaryTree<T>::LevelCreateTree(int nodeSize,char* string) {
treeSize = nodeSize;
//层次遍历有关的都要结合队列来做
arrayQueue<binaryTreeNode<T>*> *queues = new arrayQueue<binaryTreeNode<T>*>;
int count = 1;
binaryTreeNode<T> *node = new binaryTreeNode<T>();//用来移动的节点Node
root = new binaryTreeNode<T>();
node = root;
root->element = string[count-1];//头结点
//只有一个元素时
if (count == nodeSize){
return;
}
queues->push(node);
while (!queues->empty()) {
node = queues->front();
queues->pop();
if (node->leftChild == NULL) {
binaryTreeNode<T> *left = new binaryTreeNode<T>();
left->element = string[count];
node->leftChild = left;
queues->push(left);
count++;
if (count == nodeSize)//这时候中止并不会有数组越界的bug
break;
}
if (node->rightChild == NULL) {
binaryTreeNode<T> *right = new binaryTreeNode<T>;
right->element = string[count];
node->rightChild = right;
queues->push(right);
count++;
if (count == nodeSize)
break;
}
}
}
由于层次遍历的特性,需要用到的结构应该是队列,arrayQueue就是用数组实现的队列。通过队列就实现了通过层次遍历的结构创建完全二叉树。思路比较简单,就是不断进入队列和出队列的过程。
2)通过前序遍历和中序遍历结果创建二叉树
代码:
也是要用到递归理解的难点在于:通过父节点(在前序遍历中确定)在中序中的位置确定父节点的左右子树。
//利用前序遍历和中序遍历创建二叉树
template <class T>
binaryTreeNode<T>* linkedBinaryTree<T>::CreateByPreIn(T *VLR, T *LVR, int n) {
if (n == 0)
return NULL;
int k = 0;
while (VLR[0] != LVR[k])
k++;
binaryTreeNode<T> *t = new binaryTreeNode<T>();
t->element = VLR[0];//父节点创建
t->leftChild = CreateByPreIn(VLR + 1, LVR, k);
t->rightChild = CreateByPreIn(VLR + k + 1, LVR + k + 1, n - k - 1);
return t;
}
2)通过后序遍历和中序遍历结果创建二叉树
代码:
也是要用到递归理解的难点在于:通过父节点(在后序遍历中确定)在中序中的位置确定父节点的左右子树的长度,通过长度递归寻找
//利用后序遍历和中序遍历产生结果
template <class T>
binaryTreeNode<T>* linkedBinaryTree<T>::CreateByPostIn(T *LRV, T *LVR, int n) {//n为序列长度
if (n == 0){
return NULL;
}
int k =0;
while (LRV[n-1]!=LVR[k])
k++;
//创建父节点
binaryTreeNode<T> *t = new binaryTreeNode<T>();
t->element = LRV[n-1];
t->leftChild = CreateByPostIn(LRV,LVR,k);
t->rightChild = CreateByPostIn(LRV+k,LVR+k+1,n-k-1);
return t;
}
注意后两种创建完全二叉树的方法都是返回的二叉树节点,所以要让完全二叉树的根等于该方法。
4.二叉树的常用方法
1)计算二叉树的节点数目:
基本思路还是递归:
基本情况:如果节点是空返回是0.
否则返回:1+以左孩子为根的二叉树的节点数目+以右孩子为根的二叉树的节点数目。
实现代码:
template <class T>
int linkedBinaryTree<T>::getNodesNum(binaryTreeNode<T> *node) {
if (node==NULL){
return 0;
}
return 1+getNodesNum(node->leftChild)+getNodesNum(node->rightChild);
}
2)计算二叉树的高度:
基本思路还是递归,你会发现二叉树的操作离不开递归。
**基本情况:**如果节点为空,返回0.
否则返回:1+max(以左孩子为根的二叉树的高度,以右孩子为根的二叉树的高度)。
实现代码:
template <class T>
//计算树的高度
int linkedBinaryTree<T>::height_tree(binaryTreeNode<T> *node) {
if (node==NULL){
return 0;
}
return 1+max(height_tree(node->leftChild),height_tree(node->rightChild));
}
关于二叉树的应用我们下次再聊。