[Data structure] Binary tree -- chain structure

Ordinary binary tree additions, deletions, and changes are of no value. It is better to use a linear table simply to store data.

Learning a common binary tree is to better control its structure and lay the foundation for subsequent learning of more complex search binary trees.

Balanced binary tree: AVL tree, red-black tree.

The concept of binary tree:

1. Empty tree

2. Non-empty: the root node, the left subtree of the root node, and the right subtree of the root node.

The concept of binary tree:

1. Empty tree

2. Non-empty: the root node, the left subtree of the root node, and the right subtree of the root node.

According to the concept, the definition of a binary tree is recursive, so the subsequent basic operations are performed recursively.

traversal of binary tree

Pre-order, in-order, subsequent traversal

Simulate traversal

1. Preorder traversal (first root traversal): root left subtree right subtree 

                          1  2  3  4  5  6

2. In-order traversal (middle root traversal): left subtree, root node, right subtree

                                 3  2  1  5  4  6

3. Post-order traversal (post-root traversal): left subtree, right subtree, root

                         3  2  5  6  4  1

Level order traversal: 1 2 4 3 5 6

Simulate binary tree traversal

Build a chain structure

typedef int BTDataType;

typedef struct BinaryTreeNode
{
	struct BinaryTreeNode* left;//左树节点
	struct BinaryTreeNode* right;//右数节点
	BTDataType data;//节点值
}BTNode;
BTNode* BinaryTreeFind(BTNode* root, BTDataType x);
BTNode* BuyBTNode(BTDataType x)
{
	//malloc节点
	BTNode* tmp = (BTNode*)malloc(sizeof(BTNode));
	if (tmp == NULL)
	{
		printf("malloc failed!\n");
		exit(-1);
	}
	BTNode* node = tmp;
	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;
}

1. Preorder traversal

Basic idea: root - left number - right number

void PrevOrder(BTNode* tree)
{

	if (tree == NULL)
	{
		printf("NULL ");
		return;
	}
	printf("%d ", tree->data);
	PrevOrder(tree->left);
	PrevOrder(tree->right);
}

 

 

2. Inorder traversal

Left subtree - root node - right subtree

//中序遍历
void InOrder(BTNode* tree)
{
	if (tree == NULL)
	{
		printf("NULL ");
		return;
	}
	InOrder(tree->left);
	printf("%d ", tree->data);
	InOrder(tree->right);
}

 

 

3. Post-order traversal

Left Subtree - Right Subtree - Root

//后序遍历
void NextOrder(BTNode* tree)
{
	if (tree == NULL)
	{
		printf("NULL ");
		return;
	}
	NextOrder(tree->left);
	NextOrder(tree->right);
	printf("%d ", tree->data);
}

 

4. Find the number of nodes

a. Traverse + count

The first:

1.遍历+计数
void BTSize(BTNode* tree, int* pCount)
{
	if (tree == NULL)
		return;

	(*pCount)++;
	BTSize(tree->left,pCount);
	BTSize(tree->right, pCount);
}

The second:

Define a global traversal counter or static define a static local variable.

int count = 0;
int BTSize(BTNode* tree)
{
	
	//static int count = 0;
	if (tree == NULL)
		return count;
	count++;
	BTSize(tree->left);
	BTSize(tree->right);

	return count;
}

But this has a fatal error, the number of nodes returned by multiple calls will be wrong.

Reason: Global variables and static local variables cannot be initialized to 0.

b. Divide and conquer:

Divide and conquer thinking: Divide a complex problem into smaller sub-problems, and sub-problems into smaller sub-problems, …

Method: Calculate the number of nodes in the left subtree and add the number of right nodes.

Idea: Recursion

//分治
/*把复杂的问题,分成更小规模的子问题,子问题再分为更小的子问题……*/

//左子树算完再算右数再加上根节点
int BTSize(BTNode* tree)
{
	if (tree == NULL)
		return 0;
	return BTSize(tree->left) + BTSize(tree->right) + 1;
}

5. Find the number of leaf nodes

Divide and conquer: After computing the left tree, then computing the right tree.

Code:

//求叶子结点的个数
//分治
int BTreeLeafSize(BTNode* tree)
{
	if (tree == NULL)
		return 0;
	if (tree->left == NULL && tree->right == NULL)
		return 1;
	return BTreeLeafSize(tree->left) + BTreeLeafSize(tree->right);
}

6. Find the number of nodes in the kth layer

Idea: Use the k-1 layer as the parent node and recurse.

//第k层节点的个数

int BTreeLevelSize(BTNode* tree,int k)
{
	assert(k >= 1);
	if (tree == NULL)
		return 0;
	if (k == 1)
		return 1;
	//找到k-1层作为父亲节点,计算即可
	return BTreeLevelSize(tree->left,k-1) + BTreeLevelSize(tree->right,k-1);
}

7. Ask the tree height geometry?

Divide and conquer: Calculate the height of the left subtree and the height of the right subtree for comparison, the larger one +1

//求树高几何?
int BTreeDepth(BTNode* tree)
{
	//分治,左数算出高来,右数算出高来,比较取其大者
	if (tree == NULL)
		return 0;
	return BTreeDepth(tree->left) > BTreeDepth(tree->right) ? BTreeDepth(tree->left) + 1 : BTreeDepth(tree->right) + 1;
}

8. Binary tree find node with value x

  1. Ideas: 1) Consider the divide and conquer idea, first recurse the left tree, and then recurse the right tree.
  2. 2) Return the corresponding node address when found, and return NULL when the lotus root is fried
  3. 3) Determine whether the returned value is NULL or the corresponding node.
//二叉树查找为x的节点
BTNode* BinaryTreeFind(BTNode* root, BTDataType x)
{
	if (root == NULL)
		return NULL;
	//前序遍历
	if (root->data == x)
		return root;
	//先遍历左树
	BTNode* ret1 = BinaryTreeFind(root->left,x);
	if (ret1)
		return ret1;
	//遍历右树
	BTNode* ret2 = BinaryTreeFind(root->right, x);
	if (ret2)
		return ret2;
	return NULL;
}

9. Level order traversal

Idea: According to the characteristics of layer-order traversal, queues can be used to solve the problem.

When the data of the previous layer is taken out, the left and right corresponding to the previous layer are put into the queue.

Note: Since C does not have a library, we need to take out the file of the queue we implemented ourselves before and add it to the corresponding position of the project.

Remember to add on Queue.h

When entering the queue, a whole structure of the binary tree is entered, not only data

Code:

//层序遍历
/*根据层序遍历的特性,考虑将数据入队列,可以根据队列的性质,实现层序遍历
当取出上一层数据的时候,将left 和 right 节点依次入队列*/
void LevelOrder(BTNode* tree)
{
	//创建队列
	Queue q;
	QueueInit(&q);
	
	//树不为空,入队列
	if (tree)
	{
		QueuePush(&q,tree);
	}
	//将数据入队列。
	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);
}

 

10. Determine whether a binary tree is a complete binary tree

Idea: The judgment is still based on the queue, supplemented by layer order traversal.

When entering the queue, NULL is also entered into the queue. When NULL is encountered when dequeuing, the remaining queue data is removed from the queue. When there is only NULL in the remaining data, it means that it is a complete binary tree, otherwise it is not.

Drawing:

This tree is not a complete binary tree.

Code:

//判断二叉树是否为完全二叉树
/*以队列为基础,层序遍历为辅助,当遇到NULL时,将剩下的数据都出队列,当剩下的数据中只有NULL,
则说明是完全二叉树,否则就不是文强案二叉树*/

bool BTreeComplete(BTNode* root)
{
	Queue q;
	QueueInit(&q);
	//若不为空,入队列
	if (root)
		QueuePush(&q, root);
	while (!QueueEmpty(&q))
	{
		BTNode* front = QueueFront(&q);
		QueuePop(&q);
		//为空时跳出
		if (front == NULL)
			break;
		//入队列
		QueuePush(&q, front->left);
		QueuePush(&q, front->right);
	}
	while (!QueueEmpty(&q))
	{
		BTNode* front = QueueFront(&q);
		QueuePop(&q);
		if (front)
			return false;
	}
	return true;
}

11. Destroy the tree

Idea: recursive divide and conquer, destroy left subtree - right subtree - root

void BTreeDestroy(BTNode* root)
{
	if (root == NULL)
		return;
	BTreeDestroy(root->left);
	BTreeDestroy(root->right);
	free(root);
}

If there are any mistakes, please point them out~

Guess you like

Origin blog.csdn.net/weixin_61932507/article/details/124227575