[Data Structure and Algorithm] - Binary Tree

learning target:

  • Depth-first traversal of a binary tree
  • Breadth-first traversal of a binary tree
  • Copying and destroying a binary tree

Learning Content:

1. Related concepts of trees
1. Node degree: the number of subtrees contained in a node.
2. Leaf node or terminal node: a node with degree 0.
3. The degree of the tree: the degree of the largest node.
4. The level of nodes: from the root node, the root is the first level... (the height of an empty tree is 0)
5. The height or depth of the tree: the maximum level of nodes.


2. The concept of binary tree
1. Each node has at most two subtrees, that is, there is no node with a degree greater than 2 in a binary tree.
2. The subtrees of the binary tree are divided into left and right, and the order of the subtrees cannot be reversed.

Binary tree: the degree of each node does not exceed 2.
Complete binary tree: only the nodes in the last level are not full.
Full binary tree: every node has degree 2.
Search a binary tree: for each node, the value of the left subtree is less than the root node, and the value of the right subtree is greater than the root node.
Balanced binary tree: It meets the conditions for searching a binary tree, and the node data on the left and right sides are relatively uniform.
Properties of a binary tree:
1. If the number of layers of the root node is specified as 1, then there are at most 2^(i-1) nodes on the i-th layer of a non-empty binary tree .
2. The relationship between the height h of a full binary tree and the number of nodes N: 2^h-1 = N. h = log2(N+1) .
3. For any binary tree, if the number of leaf nodes with degree 0 is n0, and the number of branch nodes with degree 2 is n2, then n0 = n2+1 .
4. A full binary tree is a special complete binary tree. The first h-1 layer of a complete binary tree is full, and the nodes in the last layer must be continuous from left to right.
5. The relationship between the height h of a complete binary tree and the number of nodes N: 2h-1-X = N. h = log2 (N+1+X), assuming the number of missing nodes is x.
Note:
In a complete binary tree, a node with a degree of 1 is either 0 or 1. Then n = x0 + x1 + x2. Get n = 2x0 + x1 – 1. The number of leaf nodes x0 = (n+1)/2 or (n)/2
The relationship between the number of nodes n of a complete binary tree and the height h: n = 2^ h -1 -x. x represents the number of missing nodes in the last layer. The range of x: 0~2^(h-1)-1
注意:使用的范围是完全二叉树或满二叉树。

insert image description here

Any binary tree (a binary tree starting with each node) consists of three parts:
1. Root node
2. Left subtree
3. Right subtree
For example: the binary tree of A node is divided into left subtree, right subtree, The binary tree of B node is divided into left subtree and right subtree, and the binary tree of D node is divided into left subtree and right subtree...

Divide and conquer algorithm: Divide and conquer, the big problem is divided into similar sub-problems, and the sub-problems are divided into sub-problems...until the sub-problems can no longer be divided.
For example: the binary tree above
divides a tree, a tree with A as a node, into a root node, a left subtree with B as a node, and a right subtree with C as a node. Continue to decompose the big problem into similar sub-problems. The left subtree with B as the node is divided into the B root node, the left subtree with D as the node, and the right subtree with E as the node. Continue to decompose the big problem into similar sub-problems. Divided into the D root node, the left subtree with NULL as the node, and the right subtree with NULL as the node. Until the subproblems can no longer be divided.因此可以使用递归实现二叉树的遍历。


3. Depth-first traversal of binary tree chain structure
insert image description here
1. Preorder (root in front) : When encountering any node of a binary tree, first visit the root node, then visit the left subtree, and then visit the right subtree.
ABD NULL NULL E NULL NULL C NULL NULL
2. Inorder (root in the middle) : When encountering any node of a binary tree, first visit the left subtree, then visit the root node, and then visit the right subtree.
NULL D NULL B NULL E BULL A NULL C NULL
3. Post-order (the root is at the back) : When encountering any node of a binary tree, first visit the left subtree, then visit the right subtree, and then visit the root node.
NULL NULL D NULL NULL EB NULL NULL CA
4. Code implementation :

#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>
#include <stdlib.h>

//二叉树的前序、中序、后序遍历也叫深度优先遍历。层序遍历也叫广度优先遍历。

typedef char BTDataType;
//定义二叉树的节点
typedef struct BinaryTreeNode
{
    
    
	struct BinaryTreeNode* left;//节点的左子树
	struct BinaryTreeNode* right;//节点的右子树
	BTDataType data;//节点的值
}BTNode;

//前序遍历:根 左子树 右子树
void PrevOrder(BTNode* root)//注意:root表示节点指针,第一次调用指根节点
{
    
    
	if (root == NULL)
	{
    
    
		printf("NULL ");
		return;
	}
	printf("%c ", root->data);//当前节点
	PrevOrder(root->left);//当前节点的左子树
	PrevOrder(root->right);//当前节点的右子树
}

//中序遍历:左子树 根 右子树
void InOrder(BTNode* root)//注意:root表示节点指针,第一次调用指根节点
{
    
    
	if (root == NULL)
	{
    
    
		printf("NULL ");
		return;
	}

	InOrder(root->left);
	printf("%c ", root->data);
	InOrder(root->right);
}

//后序遍历:左子树 右子树 根
void PostOrder(BTNode* root)//注意:root表示节点指针,第一次调用指根节点
{
    
    
	if (root == NULL)
	{
    
    
		printf("NULL ");
		return;
	}

	PostOrder(root->left);
	PostOrder(root->right);
	printf("%c ", root->data);
}
int main()
{
    
    
	BTNode* A = (BTNode*)malloc(sizeof(BTNode));
	A->data = 'A';
	A->left = NULL;
	A->right = NULL;

	BTNode* B = (BTNode*)malloc(sizeof(BTNode));
	B->data = 'B';
	B->left = NULL;
	B->right = NULL;

	BTNode* C = (BTNode*)malloc(sizeof(BTNode));
	C->data = 'C';
	C->left = NULL;
	C->right = NULL;

	BTNode* D = (BTNode*)malloc(sizeof(BTNode));
	D->data = 'D';
	D->left = NULL;
	D->right = NULL;

	BTNode* E = (BTNode*)malloc(sizeof(BTNode));
	E->data = 'E';
	E->left = NULL;
	E->right = NULL;

	A->left = B;
	A->right = C;
	B->left = D;
	B->right = E;

	PrevOrder(A);
	printf("\n");
	InOrder(A);
	printf("\n");
	PostOrder(A);
	printf("\n");

	return 0;
}

5. Recursive expansion diagram :
insert image description here


4. Breadth-first traversal of binary tree chain structure

Idea: Use queues to implement layer order traversal
insert image description here

//二叉树的层序遍历,也叫广度优先遍历。即从根节点开始一层一层的遍历

typedef char BTDataType;
typedef struct BinaryTreeNode
{
    
    
	struct BinaryTreeNode* left;//节点的左子树
	struct BinaryTreeNode* right;//节点的右子树
	BTDataType data;//节点的值
}BTNode;

#include "Queue.h"//包含头文件

void LevelOrder(BTNode* root)//注意:root仅仅指根节点
{
    
    
	//核心思路:从根节点开始入队,每次当上一层节点出队(数据)的时候,将该出节点的下一层子节点入队
	Queue q;
	QueueInit(&q);
	if (root != NULL)
	{
    
    
		QueuePush(&q, root);//将根节点入队列
	}
	//遍历
	while (!QueueEmpty(&q))
	{
    
    
		BTNode* front = QueueFront(&q);//保存队头的节点(指针)
		QueuePop(&q);//从队列中取出指针(二叉树节点)元素,出队列free删除的是队列节点指针,而节点的数据域还在。
		//二叉树的节点并没有删除,删除的是队列的节点
		printf("%c ", front->data);

		//将下一层的节点入队
		if (front->left != NULL)
		{
    
    
			QueuePush(&q, front->left);
		}
		if (front->right != NULL)
		{
    
    
			QueuePush(&q, front->right);
		}
	}
	printf("\n");
	QueueDestroy(&q);
}

5. Find the number of nodes or the number of leaf nodes in a binary tree

typedef char BTDataType;
typedef struct BinaryTreeNode
{
    
    
	struct BinaryTreeNode* left;//节点的左子树
	struct BinaryTreeNode* right;//节点的右子树
	BTDataType data;//节点的值
}BTNode;

//1、求二叉树节点的个数
//方法一:遍历计数
//将size定义为全局变量
int size = 0;
void  BinaryTreeSize(BTNode* root)//注意:root表示节点指针,第一次调用指根节点
{
    
    
	if (root == NULL)
	{
    
    
		return;
	}
	else
	{
    
    
		++size;//当前节点
	}
	BinaryTreeSize(root->left);//当前节点的左子树
	BinaryTreeSize(root->right);//当前节点的右子树
}

//方法二:遍历计数
//将size定义为静态变量
int BinaryTreeSize(BTNode* root)//注意:root表示节点指针,第一次调用指根节点
{
    
    
	if (root == NULL)
	{
    
    
		return;
	}
	static int size = 0;
	++size;

	BinaryTreeSize(root->left);
	BinaryTreeSize(root->right);

	return size;
}
//注意:以上两种方法,在多线程并发执行时,并行去计算两棵树,会出现干扰
//因此以上两个方法,不是线程安全的。

//方法三:传参
void  BinaryTreeSize(BTNode* root,int *psize)//注意:root表示节点指针,第一次调用指根节点
{
    
    
	if (root == NULL)
	{
    
    
		return;
	}
	else
	{
    
    
		++(*psize);//解引用再++,相当于++size
	}
	BinaryTreeSize(root->left,psize);
	BinaryTreeSize(root->right,psize);
}

//方法四:分治算法
//将一棵树分成根,左子树,右子树
//该树的节点可以计算为:先计算左子树的节点个数,再计算右子树的节点个数,再计算根节点(1)。
//一颗树的节点个数:左子树+右子树+1
//左子树又可以继续再分成根,左子树,右子树,返回(左子树的节点个数+右子树的节点个数+1)......直到不可再分,返回0
int BinaryTreeSize(BTNode* root)//注意:root表示节点指针,第一次调用指根节点
{
    
    
	return root == NULL ? 0 : BinaryTreeSize(root->left) + BinaryTreeSize(root->right) + 1;//后序遍历的思维
}

//2、求叶子节点的个数
//方法1:遍历计数,如果左右子树都为NULL,则size++
//方法2:分治算法
//将一棵树分成根,左子树,右子树
//该树的节点可以计算为:先判断根左右子树是否为空。再计算左子树的叶子节点个数,再计算右子树的叶子节点个数。
//一颗树的叶子节点个数:左子树+右子树
//左子树又可以继续再分成根,左子树,右子树,返回(左子树的叶子节点个数+右子树的叶子节点个数)......直到不可再分,返回0
int BinaryTreeLeafSize(BTNode* root)//注意:root表示节点指针,第一次调用指根节点
{
    
    
	if (root == NULL)//节点为空
	{
    
    
		return 0;
	}
	if (root->left == NULL && root->right == NULL)//当前节点
		return 1;
	//当以上两个都不满足,也就是当前节点还有子节点。既不是空,也不是叶子节点
	return BinaryTreeLeafSize(root->left) + BinaryTreeLeafSize(root->right);//当前节点的左子树、当前节点的右子树
}

6. Copy and destroy the binary tree

struct TreeNode
{
    
    
	struct TreeNode* left;//节点的左子树
	struct TreeNode* right;//节点的右子树
	int data;//节点的值
};
//思路:先拷贝左子树,再拷贝右子树,再根节点,采用后序遍历

//拷贝二叉树
struct TreeNode* CopyTree(struct TreeNode* root)//这里是传值调用
{
    
    //注意:root表示节点指针,第一次调用指根节点
	if (root == NULL)
	{
    
    
		return;
	}
	struct TreeNode* left = CopyTree(root->left);//当前节点的左子树
	struct TreeNode* right = CopyTree(root->right);//当前节点的右子树

	//创建根结点
	struct TreeNode* newroot = (struct TreeNode*)malloc(sizeof(struct TreeNode));
	if (newroot == NULL)
		return NULL;
	newroot->data = root->data;
	newroot->left = root->left;
	newroot->right = root->right;

	return newroot;
}

//由于先销毁根节点,就无法销毁左子树,和右子树,因此消除一个树,采用后序遍历

//思想:分治算法
//1、当前树的根节点为空,直接返回
//2、当前树的根节点非空,先释放左,右子树,再释放根节点(左右子树继续分解成以上两步)
//继续分解成,判断当前树的根是否为空,释放左,右子树,再释放根节点
void DestroyTree(struct TreeNode* *proot)//或者C++改为引用DestroyTree(struct TreeNode*& root)
{
    
    //注意:root表示节点指针的地址,不仅仅指根节点的地址
	if (*proot == NULL)
	{
    
    
		return;
	}
	DestroyTree(&((*proot)->left));
	DestroyTree(&((*proot)->right));
	free(*proot);
	*proot = NULL;//改变外边的root,则用*root,改变*root,则用**root
}

Guess you like

Origin blog.csdn.net/qq_48163964/article/details/130147278