简单构建一颗二叉树
在学习二叉树的基本操作前,需先要创建一棵二叉树,然后才能学习其相关的基本操作。由于现在我们对二叉树结构掌握还不够深入,为了降低大家学习成本,此处手动快速创建一棵简单的二叉树,快速进入二叉树操作学习,等二叉树结构了解的差不多时,我们反过头再来研究二叉树真正的创建方式。
typedef char BTDataTypa;
typedef struct BinaryTreeNode
{
BTDataTypa data;
struct BinaryTreeNode* left;
struct BinaryTreeNode* right;
}TreeNode;
//创建节点
TreeNode* BuyTreeNode(int x)
{
TreeNode* node = (TreeNode*)malloc(sizeof(TreeNode));
assert(node);
node->data = x;
node->left = NULL;
node->right = NULL;
return node;
}
//初始化
TreeNode* CreateTree()
{
TreeNode* node1 = BuyTreeNode(1);
TreeNode* node2 = BuyTreeNode(2);
TreeNode* node3 = BuyTreeNode(3);
TreeNode* node4 = BuyTreeNode(4);
TreeNode* node5 = BuyTreeNode(5);
TreeNode* node6 = BuyTreeNode(6);
node1->left = node2;
node1->right = node4;
node2->left = node3;
node4->left = node5;
node4->right = node6;
return node1;
}
注意:上述代码并不是创建二叉树的方式,真正创建二叉树方式后序详解重点讲解
二叉树的概念
再看二叉树基本操作前,再回顾下二叉树的概念。
二叉树是:
1. 空树
2. 非空:根节点,根节点的左子树、根节点的右子树组成的
任何一颗二叉树拆解成三部分:
- 根
- 左子树
- 右子树
从概念中可以看出,二叉树定义是递归式的,因此后序基本操作中基本都是按照该概念实现的。
二叉树的遍历
学习二叉树结构,最简单的方式就是遍历。所谓二叉树遍历(Traversal)是按照某种特定的规则,依次对二叉 树中的节点进行相应的操作,并且每个节点只操作一次。访问结点所做的操作依赖于具体的应用问题。 遍历是二叉树上最重要的运算之一,也是二叉树上进行其它运算的基础。
按照规则二叉树的遍历有:前序、中序以及后序遍历的递归遍历:
- 前序遍历(Preorder Traversal 亦称先序遍历)——访问根结点的操作发生在遍历其左右子树之前。 (根 左子树 右子树)
- 中序遍历(Inorder Traversal)——访问根结点的操作发生在遍历其左右子树之中(间)。(左子树 根 右子树)
- 后序遍历(Postorder Traversal)——访问根结点的操作发生在遍历其左右子树之后。 (左子树 右子树 根)
//前序遍历
void PrevOread(TreeNode* root)
{
if (root == NULL)
{
printf("N ");
return;
}
printf("%c ", root->data);
PrevOread(root->left);
PrevOread(root->right);
}
//中序遍历
void InOread(TreeNode*root)
{
if (root == NULL)
{
printf("N ");
return;
}
InOread(root->left);
printf("%d ",root->data);
InOread(root->right);
}
//后序遍历
void PostOread(TreeNode* root)
{
if (root == NULL)
{
printf("N ");
return;
}
PostOread(root->left);
PostOread(root->right);
printf("%d ", root->data);
}
前序遍历结果:1 2 3 4 5 6
中序遍历结果:3 2 1 5 4 6
后序遍历结果:3 2 5 6 4 1
在这里我们的结果就出来了,但是为了防止陷入误区,理解不深刻,以及更好的理解子问题分治的过程所以我们将空树也算上,便于更好的理解
前序遍历结果:1 2 3 N N N 4 5 N N 6 N N
中序遍历结果:N 3 N 2 N 1 N 5 N 4 N 6 N
后序遍历结果:N N 3 N 2 N N 5 N N 6 4 N
二叉树的创建
二叉树的构建
// 通过前序遍历的数组"ABD##E#H##CF##G##"构建二叉树
TreeNode* TreeCreate(char* a, int* pi)
{
if (a[*pi] == '#')
{
(*pi)++;
return NULL;
}
TreeNode* root = (TreeNode*)malloc(sizeof(TreeNode));
if (root == NULL)
{
perror("malloc fail");
exit(-1);
}
root->data = a[(*pi)++];
root->left = TreeCreate(a, pi);
root->right = TreeCreate(a, pi);
return root;
}
int main()
{
char a[] = "ABD##E#H##CF##G##";
//可以创建全局变量,存在数据段全局变量不受栈帧影响//但是可能会多次调用,需要重置
///局部变量,传地址,形参不影响实参
int i = 0;
TreeNode* root = TreeCreate(a, &i);
在二叉树中我们主要是通过递归的方法来实现各种操作
在递归的时候我们主要要掌握子问题分治的思路:分而治之 ,将一个复杂的问题分成两个或更多的相同或相似的子问题,再把子问题分成更小的子问题
二叉树的常规题目
求二叉树节点个数:
我们首先分析子问题:
1.返回条件 root为NULL
2.分治:左子树节点+右子树节点+1(根)
代码实现如下
int TreeSize(TreeNode* root)
{
return root == NULL ? 0 : TreeSize(root->left) + TreeSize(root->right) + 1;
}
求二叉树叶子节点的个数
我们首先分析子问题:
1. 返回条件 节点为空就返回
2. 分治:叶子节点没有孩子,所以我们可以把节点为空返回0,如果节点自身不为空并且该节点的左孩子为空,右孩子也为空,我们可以返回1,最后我们左子树+右子树就可以得到叶子节点的个数.
代码实现如下
//二叉树叶子节点个数
int TreeLeafSize(TreeNode*root)
{
if (root == NULL)
{
return 0;
}
if (root->left == NULL && root->right == NULL)
{
return 1;
}
return TreeLeafSize(root->left)+TreeLeafSize(root->right);
}
求二叉树的高度,深度
首先子问题分治:
1.为空 返回;
2.非空 左子树的高度和右子树的高度大的加1;
//二叉树求深度
int TreeHeight(TreeNode* root)
{
if (root == NULL)
{
return 0;
}
//左树,右树大的加根
int leftHeight = TreeHeight(root->left);
int rightHeight = TreeHeight(root->right);
return leftHeight > rightHeight ? leftHeight + 1 : rightHeight + 1;
//fmax也阔以头文件<math.h>
}
但是这里还有一种下意识会使用的方法,
int TreeHeight(TreeNode* root)
{
return root==NULL?0:
TreeHeight(root->left)>TreeHeight(root->right)?
TreeHeight(root->left)+1:
TreeHeight(root->right)+1;
}
这种方法存在大量的重复调用,得到的数据,没有被记录,每一次都需要从头开始调用,在数量稍多的情况就会出现崩溃。
二叉树第k层节点个数
子问题分治:
1.为空 返回;
2.不为k,k==1,返回1;
3.不为空,且k>1,返回左子树的k-1层+右子树的k-1层。
代码实现如下
//求第k层节点个数
int TreeLeveLK(TreeNode* root,int k)
{
if (root == NULL)
return 0;
if (k == 1)
return 1;
assert(k > 0);
return TreeLeveLK(root->left, k - 1) + TreeLeveLK(root->right, k - 1);
}
二叉树的销毁
我们在对二叉树进行free释放节点时最方便的方法,是使用后序遍历free节点,代码实现如下
//后序遍历销毁
void DestroyTree(TreeNode* root)
{
if (root == NULL)
return;
DestroyTree(root->left);
DestroyTree(root->right);
free(root);
}