什么是二叉树?
二叉树(binary tree)是指树中节点的度不大于2的有序树,它是一种最简单且最重要的树。二叉树的递归定义为:二叉树是一棵空树,或者是一棵由一个根节点和两棵互不相交的,分别称作根的左子树和右子树组成的非空树;左子树和右子树又同样都是二叉树。
1.通过前序遍历的数组"ABD##E#H##CF##G##"构建二叉树
这段代码实现了根据前序遍历的数组构建二叉树的过程。其原理是通过递归实现的,对于当前节点,如果它是空节点或者数组已经遍历完,那么返回NULL,否则创建新节点并赋初值,然后递归构建左子树和右子树。具体实现如下:
// 通过前序遍历的数组"ABD##E#H##CF##G##"构建二叉树
BTNode* BinaryTreeCreate(BTDataType* a, int n, int* pi)
{//如果数组已经遍历完或者当前节点是空节点,返回空指针if (*pi >= n || a[*pi] == '#'){//当前节点为空,返回NULL(*pi)++;return NULL;}//创建新节点并赋初值BTNode* root = new BTNode;root->_data = a[*pi];root->_left = NULL;root->_right = NULL;//递归构建左子树和右子树(*pi)++;root->_left = BinaryTreeCreate(a, n, pi);(*pi)++;root->_right = BinaryTreeCreate(a, n, pi);return root;
}
其中,a表示前序遍历的数组,n表示数组的长度,pi表示数组的当前位置。如果数组已经遍历完或者当前节点是空节点,那么返回空指针。否则,创建一个新节点,并将当前位置向后移动一位,然后递归构建左子树和右子树。最终返回根节点,即构建好的二叉树。
2.二叉树销毁
这段代码实现了二叉树的销毁过程。其原理是通过递归实现的,对于当前节点,如果它为空节点,直接返回;否则递归销毁左子树和右子树,最后释放当前节点的内存。具体实现如下:
// 二叉树销毁
void BinaryTreeDestory(BTNode** root)
{if (*root == NULL)//如果当前节点为空,直接返回return;//递归销毁左子树和右子树BinaryTreeDestory(&((*root)->_left));BinaryTreeDestory(&((*root)->_right));//释放当前节点的内存free(*root);*root = NULL;
}
其中,root表示当前节点的指针,如果当前节点为空,直接返回;否则递归销毁左子树和右子树,最后释放当前节点的内存,并将当前节点的指针设置为NULL。递归过程会一直向下遍历二叉树,直到叶子节点,然后逐层返回,将每个节点释放,并将其左右子节点的内存全部释放。最终得到一个空的二叉树。
3.二叉树节点个数
// 二叉树节点个数
int BinaryTreeSize(BTNode* root)
{if (root == NULL)//如果当前节点为空返回0return 0;//返回左子树节点个数加上右节点个数再加上1(当前节点)return BinaryTreeSize(root->_left) +BinaryTreeSize(root->_right) + 1;
}
4.二叉树叶子节点个数
这段代码实现了计算二叉树节点个数的过程。其原理是通过递归实现的,对于当前节点,如果它为空节点,直接返回0;否则返回左子树节点个数加上右子树节点个数再加上1(当前节点)。具体实现如下:
// 二叉树叶子节点个数
int BinaryTreeLeafSize(BTNode* root)
{if (root == NULL)//如果当前节点为空,返回0return 0;if (root->_left == NULL && root->_right == NULL){//如果当前节点是叶子节点,返回1return 1;}// 返回左子树叶子节点个数加上右子树叶子节点个数return BinaryTreeLeafSize(root->_left) + BinaryTreeLeafSize(root->_right);
}
其中,root表示当前节点,如果当前节点为空,直接返回0;否则递归计算左子树节点个数和右子树节点个数,最终返回左子树节点个数加上右子树节点个数再加上1(当前节点),即整棵二叉树的节点个数。递归过程会一直向下遍历二叉树,直到叶子节点,然后逐层返回,并将每个节点的左右子树节点个数相加,最终得到整棵二叉树的节点个数。
5.二叉树第k层节点个数
这段代码实现了计算二叉树叶子节点个数的过程。其原理是通过递归实现的,对于当前节点,如果它为空节点,直接返回0;如果它是叶子节点,返回1;否则返回左子树叶子节点个数加上右子树叶子节点个数。具体实现如下:
//二叉树第k层节点个数
int BinaryTreeLevelKSize(BTNode* root, int k)
{if (root==NULL)//如果当前节点为空,返回0return 0;if (k == 1)//如果当前节点是第k层节点,返回1return 1;// 返回左子树第 k-1 层节点个数加上右子树第 k-1 层节点个数int leftk = BinaryTreeLevelKSize(root->_left, k - 1);int rightk = BinaryTreeLevelKSize(root->_right, k - 1);return leftk + rightk;
}
其中,root表示当前节点,如果当前节点为空,直接返回0;如果当前节点是叶子节点,返回1;否则递归计算左子树叶子节点个数和右子树叶子节点个数,最终返回左子树叶子节点个数加上右子树叶子节点个数,即整棵二叉树的叶子节点个数。递归过程会一直向下遍历二叉树,直到叶子节点,然后逐层返回,并将每个节点的左右子树叶子节点个数相加,最终得到整棵二叉树的叶子节点个数。
6.二叉树查找值为x的节点
这段代码实现了在二叉树中查找值为x的节点的过程。其原理是通过递归实现的,对于当前节点,如果它为空节点,返回NULL;如果它的值等于x,返回指向当前节点的指针;否则在左子树中查找x,如果找到了,返回指向左子树中x的节点的指针;否则在右子树中查找x,如果找到了,返回指向右子树中x的节点的指针;如果左右子树都没有找到,返回NULL。具体实现如下:
// 二叉树查找值为x的节点
BTNode* BinaryTreeFind(BTNode* root, BTDataType x)
{if (root == NULL)//如果当前节点为空,返回NULLreturn 0;if (root->_data == x)//如果当前节点的值等于x,返回指向当前节点的指针return root;//在左子树中查找xBTNode* lret = BinaryTreeFind(root->_left, x);// 如果在左子树中找到了 x,返回指向左子树中 x 的节点的指针if (lret){return lret;}//在右子树中查找xBTNode* rret = BinaryTreeFind(root->_right, x);// 如果在右子树中找到了 x,返回指向右子树中 x 的节点的指针if (rret){return rret;}//否则,返回NULLreturn NULL;
}
其中,root表示当前节点,x表示要查找的值。如果当前节点为空,返回NULL;如果当前节点的值等于x,返回指向当前节点的指针;否则在左子树中查找x,如果找到了,返回指向左子树中x的节点的指针;否则在右子树中查找x,如果找到了,返回指向右子树中x的节点的指针;如果左右子树都没有找到,返回NULL。递归过程会一直向下遍历二叉树,直到找到值为x的节点或者遍历到叶子节点,然后逐层返回,找到值为x的节点或者返回NULL。
7.二叉树前序遍历
前序遍历首先访问根结点然后遍历左子树,最后遍历右子树。在遍历左、右子树时,仍然先访问根结点,然后遍历左子树,最后遍历右子树。
这段代码实现了二叉树的先序遍历过程。其原理是通过递归实现的,对于当前节点,如果它为空节点,直接返回;否则打印当前节点的值,并递归遍历左子树和右子树。具体实现如下:
// 二叉树前序遍历
void BinaryTreePrevOrder(BTNode* root)
{if (root == NULL){//如果当前节点为空,直接返回return;}// 打印当前节点的值,并递归打印左子树和右子树printf("%c ", root->_data);BinaryTreePrevOrder(root->_left);BinaryTreePrevOrder(root->_right);
}
其中,root表示当前节点。如果当前节点为空,直接返回;否则先打印当前节点的值,然后递归遍历左子树和右子树。递归过程会一直向下遍历二叉树,直到遍历到叶子节点,然后逐层返回,逐个打印节点的值,最终得到整棵二叉树的先序遍历序列。
8.二叉树中序遍历
中序遍历首先遍历左子树,然后访问根结点,最后遍历右子树。若二叉树为空则结束返回,否则:
(1)中序遍历左子树
(2)访问根结点
(3)中序遍历右子树
这段代码实现了二叉树的中序遍历过程。其原理是通过递归实现的,对于当前节点,如果它为空节点,直接返回;否则先递归遍历左子树,打印当前节点的值,再递归遍历右子树。具体实现如下:
// 二叉树中序遍历
void BinaryTreeInOrder(BTNode* root)
{if (root == NULL){// 如果当前节点为空,直接返回return;}// 递归打印左子树,打印当前节点的值,再递归打印右子树 BinaryTreePrevOrder(root->_left);printf("%c ", root->_data);BinaryTreePrevOrder(root->_right);
}
其中,root表示当前节点。如果当前节点为空,直接返回;否则先递归遍历左子树,打印当前节点的值,再递归遍历右子树。递归过程会一直向下遍历二叉树,直到遍历到叶子节点,然后逐层返回,逐个打印节点的值,最终得到整棵二叉树的中序遍历序列。
9.二叉树后序遍历
后序遍历首先遍历左子树,然后遍历右子树,最后访问根结点,在遍历左、右子树时,仍然先遍历左子树,然后遍历右子树,最后遍历根结点。即:
若二叉树为空则结束返回,
否则:
(1)后序遍历左子树
(2)后序遍历右子树
(3)访问根结点
这段代码实现了二叉树的后序遍历过程。其原理是通过递归实现的,对于当前节点,如果它为空节点,直接返回;否则先递归遍历左子树和右子树,再打印当前节点的值。具体实现如下:
// 二叉树后序遍历
void BinaryTreePostOrder(BTNode* root)
{if (root == NULL){// 如果当前节点为空,直接返回return;}// 递归打印左子树和右子树,再打印当前节点的值BinaryTreePrevOrder(root->_left);BinaryTreePrevOrder(root->_right);printf("%c ", root->_data);
}
其中,root表示当前节点。如果当前节点为空,直接返回;否则先递归遍历左子树和右子树,再打印当前节点的值。递归过程会一直向下遍历二叉树,直到遍历到叶子节点,然后逐层返回,最终得到整棵二叉树的后序遍历序列。
10.层序遍历
这段代码实现了二叉树的层序遍历过程。其原理是通过队列实现的,先将根节点入队,然后循环从队列中取出节点并打印节点的值,如果节点有左子节点,就将左子节点入队,如果节点有右子节点,就将右子节点入队,直到队列为空为止。具体实现如下:
// 层序遍历
void BinaryTreeLevelOrder(BTNode* root)
{if (root == nullptr) {return;}std::queue<BTNode*> q;q.push(root);while (!q.empty()) {BTNode* node = q.front();q.pop();std::cout << node->_data << " ";if (node->_left != nullptr) {q.push(node->_left);}if (node->_right != nullptr) {q.push(node->_right);}}
}
其中,root表示根节点。首先将根节点入队,然后循环从队列中取出节点并打印节点的值,如果节点有左子节点,就将左子节点入队,如果节点有右子节点,就将右子节点入队,直到队列为空为止。这个过程会按照层序遍历的顺序依次遍历二叉树的每一层节点,从而得到整棵二叉树的层序遍历序列。
11.判断二叉树是否是完全二叉树
这段代码实现了判断二叉树是否为完全二叉树的过程。其原理是通过层序遍历和队列实现的,在层序遍历的过程中判断每个节点是否有左右子树,如果有就将它们入队,如果没有就将一个标志位设为1,表示之后遍历到的节点必须都是叶子节点,否则该树就不是完全二叉树。如果遍历到了一个节点,发现它的左子树或右子树为空,且之前已经出现过空子树,则该树就不是完全二叉树。如果遍历到队列中所有节点,均无右子树,则该树是完全二叉树。具体实现如下:
// 判断二叉树是否是完全二叉树
int BinaryTreeComplete(BTNode* root) {if (root == NULL) {// 空树是完全二叉树return 1;}BTNode* queue[1000];int front = 0, rear = 0;queue[rear++] = root;int flag = 0; // 是否出现过空子树的标志while (front < rear) {BTNode* node = queue[front++];if (node->_left != NULL) {// 如果之前出现过空子树,则该树不是完全二叉树if (flag) {return 0;}queue[rear++] = node->_left;}else {flag = 1;}if (node->_right != NULL) {// 如果之前出现过空子树,则该树不是完全二叉树if (flag) {return 0;}queue[rear++] = node->_right;}else {flag = 1;}}// 如果队列中所有节点均无右孩子,则该树是完全二叉树return 1;
}
其中,root表示根节点。首先判断根节点是否为空,如果是,则空树是完全二叉树,直接返回1。然后定义一个队列,将根节点入队,并定义一个标志位flag表示是否出现过空子树。接下来,在队列不为空的情况下,从队列中取出一个节点,判断该节点的左右子树是否为空,如果有子树,则将子树入队;如果子树为空,则将标志位flag设为1表示之后遍历到的节点必须都是叶子节点。如果遍历到了一个节点,发现它的左子树或右子树为空,且之前已经出现过空子树,则该树就不是完全二叉树,直接返回0。如果遍历到队列中所有节点,均无右子树,则该树是完全二叉树,返回1。这个过程可以判断二叉树是否为完全二叉树。
12.完整代码
#define _CRT_SECURE_NO_WARNINGS
#include<iostream>
#include<string.h>
#include<assert.h>
#include<stdio.h>
#include<limits.h>
#include<stdbool.h>
#include<queue>
using namespace std;
typedef char BTDataType;
typedef struct BinaryTreeNode
{BTDataType _data;struct BinaryTreeNode* _left;struct BinaryTreeNode* _right;
}BTNode;// 通过前序遍历的数组"ABD##E#H##CF##G##"构建二叉树
BTNode* BinaryTreeCreate(BTDataType* a, int n, int* pi);
// 二叉树销毁
void BinaryTreeDestory(BTNode** root);
// 二叉树节点个数
int BinaryTreeSize(BTNode* root);
// 二叉树叶子节点个数
int BinaryTreeLeafSize(BTNode* root);
// 二叉树第k层节点个数
int BinaryTreeLevelKSize(BTNode* root, int k);
// 二叉树查找值为x的节点
BTNode* BinaryTreeFind(BTNode* root, BTDataType x);
// 二叉树前序遍历
void BinaryTreePrevOrder(BTNode* root);
// 二叉树中序遍历
void BinaryTreeInOrder(BTNode* root);
// 二叉树后序遍历
void BinaryTreePostOrder(BTNode* root);
// 层序遍历
void BinaryTreeLevelOrder(BTNode* root);
// 判断二叉树是否是完全二叉树
int BinaryTreeComplete(BTNode* root);// 通过前序遍历的数组"ABD##E#H##CF##G##"构建二叉树
BTNode* BinaryTreeCreate(BTDataType* a, int n, int* pi)
{//如果数组已经遍历完或者当前节点是空节点,返回空指针if (*pi >= n || a[*pi] == '#'){//当前节点为空,返回NULL(*pi)++;return NULL;}//创建新节点并赋初值BTNode* root = new BTNode;root->_data = a[*pi];root->_left = NULL;root->_right = NULL;//递归构建左子树和右子树(*pi)++;root->_left = BinaryTreeCreate(a, n, pi);(*pi)++;root->_right = BinaryTreeCreate(a, n, pi);return root;
}
// 二叉树销毁
void BinaryTreeDestory(BTNode** root)
{if (*root == NULL)//如果当前节点为空,直接返回return;//递归销毁左子树和右子树BinaryTreeDestory(&((*root)->_left));BinaryTreeDestory(&((*root)->_right));//释放当前节点的内存free(*root);*root = NULL;
}// 二叉树节点个数
int BinaryTreeSize(BTNode* root)
{if (root == NULL)//如果当前节点为空返回0return 0;//返回左子树节点个数加上右节点个数再加上1(当前节点)return BinaryTreeSize(root->_left) +BinaryTreeSize(root->_right) + 1;
}// 二叉树叶子节点个数
int BinaryTreeLeafSize(BTNode* root)
{if (root == NULL)//如果当前节点为空,返回0return 0;if (root->_left == NULL && root->_right == NULL){//如果当前节点是叶子节点,返回1return 1;}// 返回左子树叶子节点个数加上右子树叶子节点个数return BinaryTreeLeafSize(root->_left) + BinaryTreeLeafSize(root->_right);
}//二叉树第k层节点个数
int BinaryTreeLevelKSize(BTNode* root, int k)
{if (root==NULL)//如果当前节点为空,返回0return 0;if (k == 1)//如果当前节点是第k层节点,返回1return 1;// 返回左子树第 k-1 层节点个数加上右子树第 k-1 层节点个数int leftk = BinaryTreeLevelKSize(root->_left, k - 1);int rightk = BinaryTreeLevelKSize(root->_right, k - 1);return leftk + rightk;
}// 二叉树查找值为x的节点
BTNode* BinaryTreeFind(BTNode* root, BTDataType x)
{if (root == NULL)//如果当前节点为空,返回NULLreturn 0;if (root->_data == x)//如果当前节点的值等于x,返回指向当前节点的指针return root;//在左子树中查找xBTNode* lret = BinaryTreeFind(root->_left, x);// 如果在左子树中找到了 x,返回指向左子树中 x 的节点的指针if (lret){return lret;}//在右子树中查找xBTNode* rret = BinaryTreeFind(root->_right, x);// 如果在右子树中找到了 x,返回指向右子树中 x 的节点的指针if (rret){return rret;}//否则,返回NULLreturn NULL;
}// 二叉树前序遍历
void BinaryTreePrevOrder(BTNode* root)
{if (root == NULL){//如果当前节点为空,直接返回return;}// 打印当前节点的值,并递归打印左子树和右子树printf("%c ", root->_data);BinaryTreePrevOrder(root->_left);BinaryTreePrevOrder(root->_right);
}// 二叉树中序遍历
void BinaryTreeInOrder(BTNode* root)
{if (root == NULL){// 如果当前节点为空,直接返回return;}// 递归打印左子树,打印当前节点的值,再递归打印右子树 BinaryTreePrevOrder(root->_left);printf("%c ", root->_data);BinaryTreePrevOrder(root->_right);
}// 二叉树后序遍历
void BinaryTreePostOrder(BTNode* root)
{if (root == NULL){// 如果当前节点为空,直接返回return;}// 递归打印左子树和右子树,再打印当前节点的值BinaryTreePrevOrder(root->_left);BinaryTreePrevOrder(root->_right);printf("%c ", root->_data);
}// 层序遍历
void BinaryTreeLevelOrder(BTNode* root)
{if (root == nullptr) {return;}std::queue<BTNode*> q;q.push(root);while (!q.empty()) {BTNode* node = q.front();q.pop();std::cout << node->_data << " ";if (node->_left != nullptr) {q.push(node->_left);}if (node->_right != nullptr) {q.push(node->_right);}}}// 判断二叉树是否是完全二叉树
int BinaryTreeComplete(BTNode* root) {if (root == NULL) {// 空树是完全二叉树return 1;}BTNode* queue[1000];int front = 0, rear = 0;queue[rear++] = root;int flag = 0; // 是否出现过空子树的标志while (front < rear) {BTNode* node = queue[front++];if (node->_left != NULL) {// 如果之前出现过空子树,则该树不是完全二叉树if (flag) {return 0;}queue[rear++] = node->_left;}else {flag = 1;}if (node->_right != NULL) {// 如果之前出现过空子树,则该树不是完全二叉树if (flag) {return 0;}queue[rear++] = node->_right;}else {flag = 1;}}// 如果队列中所有节点均无右孩子,则该树是完全二叉树return 1;
}
int main()
{BTDataType a[] = { 'A', 'B', 'D', '#', '#', 'E', '#', 'H', '#', '#', 'C', 'F', '#', '#', 'G', '#', '#' };int n = sizeof(a) / sizeof(a[0]);int i = 0;BTNode* root = BinaryTreeCreate(a, n, &i);//cout << root << endl;/*cout <<"BinaryTreeSize:" << BinaryTreeSize(root) << endl;cout << "BinaryTreeLeafSize:" << BinaryTreeLeafSize(root) << endl;cout <<"BinaryTreeLevelKSize:" << BinaryTreeLevelKSize(root, 1) << endl;cout << "BinaryTreeFind:" << BinaryTreeFind(root, 'A') << endl;*///BinaryTreePrevOrder(root);//BinaryTreeInOrder(root);//BinaryTreePostOrder(root);//BinaryTreeLevelOrder(root);//cout<<BinaryTreeComplete(root) << endl;//cout << BinaryTreeComplete(root) << endl;
}