[Data structure] Binary tree - chain structure

Table of contents

 1. Pre-declaration

Second, the traversal of the binary tree

2.1 Preorder, inorder and postorder traversal

2.2 Layer order traversal

3. The number and height of nodes

3.1 Number of nodes

3.2 Number of leaf 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.1 Building a binary tree

4.2 Binary tree destruction

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

Binary tree is: 1. Empty tree 2. Non-empty: root node, left subtree of root node, right subtree of root node.
The definition of a binary tree is recursive, so the basic operations in the postorder are basically implemented according to this concept.

  

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

 Binary tree traversal (Traversal) is to perform corresponding operations on the nodes in the binary tree , and each node is only operated once.

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.
Preorder traversal results: 1 2 3 4 5 6

Inorder traversal result: 3 2 1 5 4 6
Post-order traversal results: 3 2 5 6 4 1

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

    In addition to pre-order traversal, in-order traversal, and post-order traversal, level-order traversal of binary trees can also be performed. The process of visiting the nodes of the tree layer by layer from top to bottom and from left to right is 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]

Guess you like

Origin blog.csdn.net/m0_57388581/article/details/131668492