本片将续之前的文章二叉树的概念进行二叉树基本操作的实现,二叉树oj题将在下篇文章讲解。
目录
思想:要求二叉树的总节点个数,左子树节点个数加右子树节点个数再加上自己。再利用递归。
2.5 二叉树查找值为x的节点(一棵树只有一个这样的节点有这个值)
a. 创建二叉树
在学习其他二叉树的基本操作前,我们先简单的创建一颗二叉树:
我们需要创建一个如图的二叉树:
代码:
typedef int BTDataType;
typedef struct BinaryTreeNode
{
BTDataType 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);
TreeNode* node7 = BuyTreeNode(7);
node1->left = node2;
node1->right = node4;
node2->left = node3;
node4->left = node5;
node4->right = node6;
node5->right = node7;
return node1;
}
(你也可以根据你自己的情况与想法改变代码,创建一颗合适的二叉树)
注意:上述代码并不是创建二叉树的方式,真正创建二叉树方式后续重点讲解
再看二叉树基本操作前,再回顾下二叉树的概念,二叉树是:
1. 空树
2. 非空:根节点,根节点的左子树、根节点的右子树组成的
从概念中可以看出,二叉树定义是递归式的,因此后续的基本操作中基本都是按照该概念实现的。
一、二叉树的遍历
1.1 前序、中序以及后序遍历
二叉树遍历(Traversal)是按照某种特定的规则,依次对二叉树中的节点进行相应的操作,并且每个节点只操作一次。
1. 前序遍历(Preorder Traversal 亦称先序遍历)——访问根结点的操作发生在遍历其左右子树之前。
2. 中序遍历(Inorder Traversal)——访问根结点的操作发生在遍历其左右子树之中(间)。
3. 后序遍历(Postorder Traversal)——访问根结点的操作发生在遍历其左右子树之后。
// 二叉树前序遍历
void PreOrder(BTNode* root);
// 二叉树中序遍历
void InOrder(BTNode* root);
// 二叉树后序遍历
void PostOrder(BTNode* root);
二叉树的前中后序遍历都比较简单,这不做详细讲解:
代码:
void PrevOrder(TreeNode* root)
{
if (root == NULL)
{
printf("N ");
return;
}
printf("%d ", root->data);
PrevOrder(root->left);
PrevOrder(root->right);
}
void InOrder(TreeNode* root)
{
if (root == NULL)
{
printf("N ");
return;
}
InOrder(root->left);
printf("%d ", root->data);
InOrder(root->right);
}
void PostOrder(TreeNode* root)
{
if (root == NULL)
{
printf("N ");
return;
}
PostOrder(root->left);
PostOrder(root->right);
printf("%d ", root->data);
}
注意:我们利用递归的方式对根节点的每个子树进行递归,每个子树又有新的“根”节点,每个节点都会被访问到,每个节点的左右子树都会被访问到。
代码中我们用"N"来表示空。
大家可以自己写一下前中后序,将会打印的顺序:
根据我创建的子树,我可以写出:
前:1 2 3 N N N 4 5 N 7 N N 6 N N
中:N 3 N 2 N 1 N 5 N 7 N 4 N 6 N
后:N N 3 N 2 N N N 7 5 N N 6 4 1
如图:(前序遍历递归图解)
测试代码:
int main()
{
TreeNode* root = CreateTree();
PrevOrder(root);
printf("\n");
InOrder(root);
printf("\n");
PostOrder(root);
printf("\n");
return 0;
}
二、节点个数以及高度
// 二叉树节点个数
int TreeSize(TreeNode* root);
// 二叉树叶子节点个数
int TreeLeafSize(TreeNode* root);// 二叉树的高度
int TreeHeight(TreeNode* root)
// 二叉树第k层节点个数
int TreeLevelK(TreeNode* root, int k);
// 二叉树查找值为x的节点
TreeNode* TreeFind(TreeNode* root, BTDataType x)
2.1 二叉树节点个数
要求二叉树的节点个数,我们可以利用“分治”的思想,
每一棵树,都有许多节点,每一个节点都有自己的左右孩子节点,而第一层的节点就是一个最终统计节点。
我们来举一个例子:假如一个年级组长现在需要知道全校人数,那么第二层就是各班班主任,第三层可以是每班级的班长,第四层是一个班级中每个小组的组长。年级组长找班主任,班主任找班长,班长找组长。组长统计一个小组的人数再加上自己 告诉班长,班长统计每个组长所报的人数 加上自己,将一个班级的人数告诉班主任,每个班主任将每班的人数,加上自己 告诉年级组长,年级组长统计每个班主任所报的人数进行综合 再加上自己 。这就是我们的分治思想。
思想:要求二叉树的总节点个数,左子树节点个数加右子树节点个数再加上自己。再利用递归。
图解:
代码:
int TreeSize(TreeNode* root)
{
return root == NULL ? 0 :
TreeSize(root->left) +
TreeSize(root->right) + 1;//左子树加右子树再加自己
}
2.2 二叉树叶子节点个数
思想:左子树叶子节点个数+右子树叶子结点个数
返回条件:
1. 空 返回0
2. 叶子 返回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);
}
2.3 二叉树的高度
思想:
返回条件:
1. 空 返回0
2. 不是空,左子树高度和右子树高度比较,更大的+1(加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;
//第一种
/*if (root == NULL)
{
return 0;
}
return TreeHeight(root->left) > TreeHeight(root->right) ?
TreeHeight(root->left) + 1 : TreeHeight(root->right) + 1;*/
//第二种
/*return root == NULL ? 0 :
TreeHeight(root->left) > TreeHeight(root->right) ?
TreeHeight(root->left) + 1 : TreeHeight(root->right) + 1;*/
//第三种:利用内置函数防止值丢失
if (root == NULL)
{
return 0;
}
return fmax(TreeHeight(root->left), TreeHeight(root->right)) + 1;
}
2.4 二叉树第k层节点个数
思想:
返回条件:
1. 空 返回0
2. 不为空 ,k = 1,返回1
3. 不为空 ,k > 1,返回左子树的k+1层 + 右子树的k+1层。
图解:
代码:
int TreeLevelK(TreeNode* root, int k)
{
assert(k>0);
if (root == NULL)
{
return 0;
}
if (k == 1)
{
return 1;
}
return TreeLevelK(root->left,k - 1)+
TreeLevelK(root->right, k - 1);
}
2.5 二叉树查找值为x的节点(一棵树只有一个这样的节点有这个值)
TreeNode* TreeFind(TreeNode* root, BTDataType x)
这里,需要返回的值是一个指针
思想:
1. 当树为空:返回NULL
2. 当节点值为要找的值,返回该节点
3. 在左右子树遍历查找,找到后直接返回
4. 当节点左右不为空,返回该节点,用于记忆。
代码:
TreeNode* TreeFind(TreeNode* root, BTDataType x)
{
if (root == NULL)
return NULL;
if (root->data == x)
return root;
TreeNode* ret1 = TreeFind(root->left, x);
if (ret1)
return ret1;
TreeNode* ret2 = TreeFind(root->right, x);
if (ret2)
return ret2;
return NULL;
}
图解:所示是一个递归展开图
可以自己画一画再理解。
在这里,我们需要搜索的 x = 5
三、本篇文章完整代码:
typedef int BTDataType;
typedef struct BinaryTreeNode
{
BTDataType 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);
TreeNode* node7 = BuyTreeNode(7);
node1->left = node2;
node1->right = node4;
node2->left = node3;
node4->left = node5;
node4->right = node6;
node5->right = node7;
return node1;
}
void PrevOrder(TreeNode* root)
{
if (root == NULL)
{
printf("N ");
return;
}
printf("%d ", root->data);
PrevOrder(root->left);
PrevOrder(root->right);
}
void InOrder(TreeNode* root)
{
if (root == NULL)
{
printf("N ");
return;
}
InOrder(root->left);
printf("%d ", root->data);
InOrder(root->right);
}
void PostOrder(TreeNode* root)
{
if (root == NULL)
{
printf("N ");
return;
}
PostOrder(root->left);
PostOrder(root->right);
printf("%d ", root->data);
}
int TreeSize(TreeNode* root)
{
return root == NULL ? 0 :
TreeSize(root->left) +
TreeSize(root->right) + 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);
}
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;
//第一种
/*if (root == NULL)
{
return 0;
}
return TreeHeight(root->left) > TreeHeight(root->right) ?
TreeHeight(root->left) + 1 : TreeHeight(root->right) + 1;*/
//第二种
/*return root == NULL ? 0 :
TreeHeight(root->left) > TreeHeight(root->right) ?
TreeHeight(root->left) + 1 : TreeHeight(root->right) + 1;*/
//第三种:利用内置函数防止值得丢失
if (root == NULL)
{
return 0;
}
return fmax(TreeHeight(root->left), TreeHeight(root->right)) + 1;
}
//第k层的节点个数
int TreeLevelK(TreeNode* root, int k)
{
assert(k>0);
if (root == NULL)
{
return 0;
}
if (k == 1)
{
return 1;
}
return TreeLevelK(root->left,k-1)+
TreeLevelK(root->right, k - 1);
}
// 二叉树查找值为x的结点
TreeNode* TreeFind(TreeNode* root, BTDataType x)
{
if (root == NULL)
return NULL;
if (root->data == x)
return root;
TreeNode* ret1 = TreeFind(root->left, x);
if (ret1)
return ret1;
TreeNode* ret2 = TreeFind(root->right, x);
if (ret2)
return ret2;
return NULL;
}
//int main()
//{
// //CreateNDate();
// PrintTopK("data.txt", 5);
// return 0;
//}
int main()
{
TreeNode* root = CreateTree();
PrevOrder(root);
printf("\n");
InOrder(root);
printf("\n");
PostOrder(root);
printf("\n");
printf("treesize:%d\n", TreeSize(root));
printf("TreeLeafSize:%d\n", TreeLeafSize(root));
printf("TreeHeight:%d\n", TreeHeight(root));
printf("TreeLevelK:%d\n", TreeLevelK(root, 4));
TreeNode* ret = TreeFind(root, 5);
printf("TreeFind:%p\n", ret);
ret->data++;//这里用来测试
PrevOrder(root);
printf("\n");
return 0;
}
标签:right,return,链式,二叉树,讲解,TreeNode,root,left
From: https://blog.csdn.net/2303_77756141/article/details/140887408