Table of contents
Second, the traversal of the binary tree
2.1 Preorder, inorder and postorder traversal
3. The number and height of nodes
3.3 The number of nodes in layer k
3.4 Height/depth of binary tree
3.5 Find the node whose value is x
Fourth, the creation and destruction of the binary tree
4.3 Determine whether a binary tree is a complete binary tree
Don't choose ease when it's time to work hard!
1. Pre-declaration
It is worthless to add, delete, check and modify ordinary binary trees. If it is for purely storing data, it is better to use linear tables.
Second, the traversal of the binary tree
Traversal methods : pre-order traversal, in-order traversal, post-order traversal and layer-order traversal
(1) Pre-order traversal (also called root-first traversal): (root, left subtree, right subtree) Above: first traverse 1 (root), then traverse 1’s left subtree 2, then traverse 2’s left Subtree 3, then traverse 3's left subtree NULL, then 3's right subtree NULL; then traverse 2's right subtree NULL; then traverse 1's right subtree 4, then traverse 4's left subtree 5, Then traverse 5's left subtree NULL, then 5's right subtree NULL; then traverse 4's right subtree 6, and finally traverse 6's left subtree NULL, then 6's right subtree. That is, 1- > 2->3->NULL->NULL->NULL- > 4->5->NULL->NULL->6->NULL->NULL [colors are root, left subtree, right subtree Tree】
(2) In-order traversal (middle root traversal): (left subtree, root node, right subtree) namely: for 3, NULL -> 3- > NULL , for 2, NULL->3->NULL - >2-> NULL; for 1, NULL->3->NULL->2->NULL ->1-> NULL->5->NULL->4->NULL->6->NULL [think To visit 1, you must first visit the left subtree 2 of 1. If you want to visit 2, you must first visit the left subtree 3 of 2. If you want to visit 3, you must first visit the left subtree NULL of 3]
(3) Post-order traversal (post-root traversal): (left subtree, right subtree, root subtree): ie: NULL->NULL->3->NULL->2->NULL->NULL->5- >NULL->NULL>6->4->1
(4) Layer order traversal (layer by layer) (no recursion required): namely: 1->2->4->3->5->6
2.1 Preorder, inorder and postorder traversal
Preorder / Inorder / Postorder recursive structure traversal :
1. Preorder traversal (Preorder Traversal is also known as preorder traversal ) - the operation of visiting the root node occurs before traversing its left and right subtrees.2. Inorder Traversal (Inorder Traversal) - the operation of visiting the root node occurs in traversing its left and right subtrees (between).3. Postorder Traversal —— The operation of visiting the root node occurs after traversing its left and right subtrees.
code:
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
typedef int BTDataType;
typedef struct BinaryTreeNode
{
struct BinaryTreeNode* left;
struct BinaryTreeNode* right;
BTDataType data;
}BTNode;
BTNode* BuyBTNode(BTDataType x)
{
BTNode* node = (BTNode*)malloc(sizeof(BTNode));
if (node == NULL)
{
printf("malloc fail\n");
exit(-1);
}
node->left = node->right = NULL;
node->data = x;
return node;
}
BTNode* CreatBinaryTree()
{
BTNode* node1 = BuyBTNode(1);
BTNode* node2 = BuyBTNode(2);
BTNode* node3 = BuyBTNode(3);
BTNode* node4 = BuyBTNode(4);
BTNode* node5 = BuyBTNode(5);
BTNode* node6 = BuyBTNode(6);
node1->left = node2;
node1->right = node4;
node2->left = node3;
node4->left = node5;
node4->right = node6;
return node1;
}
//根 左 右
void PrevOrder(BTNode* root)
{
if (root == NULL)
{
return;
}
printf("%d ", root->data);
PrevOrder(root->left);
PrevOrder(root->right);
}
void InOrder(BTNode* root)
{
if (root == NULL)
{
return;
}
InOrder(root->left);
printf("%d ", root->data);
InOrder(root->right);
}
void PostOrder(BTNode* root)
{
if (root == NULL)
{
return;
}
PostOrder(root->left);
PostOrder(root->right);
printf("%d ", root->data);
}
int main()
{
BTNode* tree = CreatBinaryTree();
PrevOrder(tree);//前
printf("\n");
InOrder(tree);//中序
printf("\n");
PostOrder(tree);//后序
printf("\n");
return 0;
}
2.2 Layer order traversal
void LevelOrder(BTNode* root)
{
Queue q;
QueueInit(&q);
//首先把根进入到队列里面,
if (root != NULL)
{
QueuePush(&q, root);
}
//判断队列是否为空,
while (!QueueEmpty(&q))
{
BTNode* front = QueueFront(&q);
QueuePop(&q);
printf("%d ", front->data);
//出数据的同时,伴随着进数据
if (front->left)
{
QueuePush(&q, front->left);
}
if (front->right)
{
QueuePush(&q, front->right);
}
}
printf("\n");
QueueDestory(&q);
}
Idea : (1) Put the root into the queue first, and use the first-in-first-out nature of the queue (2) When the node is out, put the next layer of non-empty nodes into the queue. One side goes in and one side goes out.
Depth-first traversal (DFS): pre-order traversal, in-order traversal, post-order traversal;
Breadth-first traversal (BFS): level-order traversal
Forward declaration : If you want to use a structure, but this structure is defined later, you can use forward declaration (same as function declaration) struct BinaryTreeNode;
3. The number and height of nodes
3.1 Number of nodes
Idea: traverse + count
Code 1:
//前序遍历
int count = 0;
void BTreeSize(BTNode* root)
{
if (root == NULL)
{
return;
}
count++;
BTreeSize(root->left);
BTreeSize(root->right);
}
int main()
{
BTNode* tree = CreatBinaryTree();
count = 0;
BTreeSize(tree);
printf("Size = %d", count);
printf("\n");
count = 0;
BTreeSize(tree);
printf("Size = %d", count);
}
The idea that we can easily think of is to change the printf that traverses the binary tree to count++; but, do we want to create a count in each stack frame? So we can define a global variable count (code 1), but this will have multiple thread safety issues. So the best way is to add a pointer. (code 2)
Code 2:
void BTreeSize(BTNode* root,int* pcount)
{
if (root == NULL)
{
return;
}
(*pcount)++;
BTreeSize(root->left, pcount);
BTreeSize(root->right, pcount);
}
int main()
{
BTNode* tree = CreatBinaryTree();
int count = 0;
BTreeSize(tree, &count);
printf("Size = %d", count);
return 0;
}
Code 3:
int BTreeSize(BTNode* root)
{
return root == NULL ? 0 : (BTreeSize(root->left) + BTreeSize(root->right) + 1);
}
Divide and conquer : Divide complex problems into smaller sub-problems, and then divide the sub-problems into smaller-scale problems until the sub-problems can no longer be divided and the results can be obtained directly
Idea : subproblem (1) Empty tree, the smallest subproblem, the number of nodes returns 0, (2) non-empty, the number of nodes in the left subtree + the number of nodes in the right subtree + 1 [self] [Code 3]
That is : if you want to know how many nodes are in the tree of this node, you must first know the number of nodes in the left subtree and right subtree, and then add yourself. When this node is NULL, return 0.
3.2 Number of leaf nodes
Code 1 :
void BTreeLeafSize(BTNode* root, int* pcount)
{
if (root == NULL)
{
return;
}
if ((root->left == NULL) && (root->right == NULL))
{
(*pcount)++;
}
BTreeLeafSize(root->left, pcount);
BTreeLeafSize(root->right, pcount);
}
int main()
{
int count = 0;
BTreeLeafSize(tree, &count);
printf("%d\n", count);
return 0;
}
Idea 1: traverse + count [Code 1]
Code 2 :
int BTreeLeafSize(BTNode* root)
{
if (root == NULL)
{
return 0;
}
if (root->right == NULL && root->left == NULL)
{
return 1;
}
return BTreeLeafSize(root->right) + BTreeLeafSize(root->left);
}
int main()
{
BTreeLeafSize(tree);
printf("%d\n", BTreeLeafSize(tree));
return 0;
}
Idea 2: Divide and conquer [Code 2]
The number of leaf nodes is equal to the leaf node of the left subtree + the leaf node of the right subtree. The nodes that have been assigned to the root of this small tree are not equal to NULL, but the left and right subtrees are NULL.
3.3 The number of nodes in layer k
int BTreeKLevelSize(BTNode* root, int k)
{
assert(k >= 1);
if (root == NULL)
{
return 0;
}
if (k == 1)
{
return 1;
}
return BTreeKLevelSize(root->left, k - 1) + BTreeKLevelSize(root->right, k - 1);
}
Divide and conquer idea: (1) empty tree, return 0 (2) non-empty, and k==1, return 1 (3) non-empty and K>1, replace it with the number of nodes in the left subtree K-1 layer + The number of nodes in the right subtree k-1 layer.
That is: [First, find the number of nodes on the kth layer. First, this layer is considered full. If there are nodes, it will return 1, and if there are no nodes, it will return 0.] Secondly (1) if you are looking for the number of nodes in the first layer number, it is directly 1, (2) If you are looking for the number of nodes in the second layer, then it can be converted into the number of nodes in the first layer of the left subtree + the number of nodes in the first layer of the right subtree ( 3) If you are looking for the number of nodes in the third layer of the root, then it can be converted into the number of nodes in the second layer of the left subtree of the root + the number of nodes in the second layer of the right subtree, and then converted into the root The number of nodes at the first level of the left subtree of the left subtree + the number of nodes at the first level of the right subtree of the left subtree of the root + the number of nodes at the first level of the left subtree of the right subtree of the root+ The number of nodes in the first level of the right subtree of the root right subtree [first level (1) empty tree, return 0 (2) k==1, return 1]
3.4 Height/depth of binary tree
int BTreeDepth(BTNode* root)
{
if (root == NULL)
{
return 0;
}
return BTreeDepth(root->left) > BTreeDepth(root->right) ? (BTreeDepth(root->left) + 1) : (BTreeDepth(root->right) + 1);
}
Divide and conquer idea: the one with the greater height of the left subtree and the right subtree +1.
3.5 Find the node whose value is x
BTNode* BTreeFind(BTNode* root, BTDataType x)
{
if (root == NULL)
{
return NULL;
}
if (root->data == x)
{
return root;
}
BTNode* ret1 = BTreeFind(root->left, x);
if (ret1)
{
return ret1;
}
BTNode* ret2 = BTreeFind(root->right, x);
if (ret2)
{
return ret2;
}
return NULL;
}
Divide and conquer thinking: [If the left subtree is found, then the right subtree does not need to be searched again]
Once you find the pointer, you can change its value
Fourth, the creation and destruction of the binary tree
4.1 Building a binary tree
Link: Niuke
code:
#include <stdio.h>
#include <stdlib.h>
typedef struct BinaryTreeNode
{
char data;
struct BinaryTreeNode* left;
struct BinaryTreeNode* right;
}BTNode;
//先构建一个二叉树【前序遍历】
BTNode* CreatTree(char* a, int* pi)
{
if (a[*pi] == '#')
{
(*pi)++;
return NULL;
}
//先构建根
BTNode* root = (BTNode*)malloc(sizeof(BTNode));
root->data = a[*pi];
(*pi)++;
//再构建左子树和右子树
root->left = CreatTree(a, pi);
root->right = CreatTree(a, pi);
return root;
}
void InOrder(BTNode* root)
{
if (root == NULL)
{
return;
}
InOrder(root->left);
printf("%c ", root->data);
InOrder(root->right);
}
int main()
{
char a[100];
scanf("%s", a);
int i = 0;
BTNode* tree = CreatTree(a, &i);
InOrder(tree);
free(tree);
tree = NULL;
return 0;
}
Idea : The string of the idea of pre-order traversal, build a binary tree [when encountering '#', return NULL], and then print the idea of in-order traversal.
4.2 Binary tree destruction
void BTreeDestory(BTNode* root)
{
if (root == NULL)
{
return;
}
BTreeDestory(root->left);
BTreeDestory(root->right);
free(root);
}
int main()
{
BTNode* tree = CreatBinaryTree();
BTreeDestory(tree);//想改变谁的内容,就需要把谁的地址传递给函数。
free(tree);
tree = NULL;
return 0;
}
(1) Post-order traversal (2) Level-1 pointer, tree needs to be destroyed outside the function. (3) If a secondary pointer is passed, it can be destroyed within the function.
4.3 Determine whether a binary tree is a complete binary tree
bool BinaryTreeComplete(BTNode* root)
{
Queue q;
QueueInit(&q);
if (root)
{
QueuePush(&q, root);
}
while (!QueueEmpty(&q))
{
BTNode* front = QueueFront(&q);
if (front == NULL)
{
break;
}
QueuePop(&q);
QueuePush(&q, root->left);//不管是还是不是NULL,都进入队列
QueuePush(&q, root->right);
}
while (!QueueEmpty(&q))
{
BTNode* front = QueueFront(&q);
if (front != NULL)
{
QueueDestory(&q);
return false;
}
QueuePop(&q);
}
QueueDestory(&q);
return true;
}
Idea: The idea of layer order traversal; when a node is out of the queue, the node of the next layer of the node will be put into the queue (the NULL will also be put into the queue), complete binary tree, after the layer order traversal, there will be no NULL . If it is not a complete binary tree, NULL will appear.
Ideas: (1) Hierarchical traversal, empty nodes can also enter the queue (2) After reaching the empty node, all the data in the queue, if all NULL, is a complete binary tree, if there is non-empty, it is not a complete binary tree.
Note: Before returning the data, the queue must be destroyed [otherwise there will be a memory leak]