[Data structure] Chained structure of binary tree

Implementation of chained binary tree

1. Understand the three traversal methods

To learn chained binary trees, you need to know three traversal methods to facilitate operations on the nodes of the binary tree as well as the left and right subtrees.

Pre-order traversal: root, left subtree, right subtree
In-order traversal: left subtree, root, right subtree
After Order traversal: left subtree, right subtree, root

The following picture is an example:
Insert image description here
The result obtained:

Preorder traversal: 1 2 3 4 5 6
Inorder traversal: 3 2 1 5 4 6
Postorder traversal: 3 2 5 6 4 1

2. Build a binary tree

There are two ways to build a binary tree, one is manual construction, the other is preorder traversal construction

(1) Manual build

Create the required nodes and then connect them according to the structure you want.

BTNode* CreatBinaryTree()
{
    
    
 BTNode* node1 = BuyNode(1);
 BTNode* node2 = BuyNode(2);
 BTNode* node3 = BuyNode(3);
 BTNode* node4 = BuyNode(4);
 BTNode* node5 = BuyNode(5);
 BTNode* node6 = BuyNode(6);

 node1->left = node2;
 node1->right = node4;
 node2->left = node3;
 node4->left = node5;
 node4->right = node6;
 return node1;
}

(2) Preorder traversal construction

First, the function parameter is passed an array. The array can be either numbers or characters. Here, a character array is used for demonstration.

char arr[] = “ABD##E#H##CF##G##”; // # represents empty

We also need a variable i to record the position of the array subscript, i is initialized to 0.

Note: When passing variable i, you must pass its address, because the value of i will also change later when the function is recursive. To change the value of a function parameter, you need its address.

Entering the function, first determine whether the position pointed by the current i subscript is #. If so, return a null pointer and move the i position backward; if not, open up space to create a node (remember to check for null) and assign the character at the current position to the node. The value of i moves backward.

The above is the root part of the preorder traversalroot left and right, that is, after the current node is created, its child nodes need to be connected. Use function recursion to make the current node enter its child node, and the child node can return (connect) to the previous node. Finally return to the root node.

Tree shape:
Insert image description here

BTNode* TreeCreate(char* a, int* pi)
{
    
    
	//如果是 # 说明为空返回上一层函数
	if (a[*pi] == '#')
	{
    
    
		(*pi)++;
		return NULL;
	}
	//不是空,开辟一个节点大小的空间,创建一个节点
	BTNode* root = (BTNode*)malloc(sizeof(BTNode));
	if (root == NULL)
	{
    
    
		perror("malloc fail");
		exit(-1);
	}
	//赋值
	root->val = a[*pi];
	(*pi)++;
	//当前节点连接孩子节点
	root->left = TreeCreate(a, pi);
	root->right = TreeCreate(a, pi);
	//当前节点连接上一个节点 || 返回根节点
	return root;
}

3.Destruction of binary tree

The binary tree is destroyed starting from the leaf nodes, using the idea of ​​subsequent traversal. Recurse to the lowest level of the tree, that is, the leaf node. When the recursion reaches the null pointer, return to the previous layer function. If the left subtree and right subtree of the current node are recursed, destroy the current node, and then return to the previous layer function. , and so on.

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

4. Number of binary tree nodes

If the current node is a null pointer, 0 is returned; if it is not a null pointer, enter its left function (left subtree) first. In the left function, first determine whether it is a null pointer. If it is, return 0, if not, then enter it first. The left function of , if the node of its left function is empty at the time, it returns 0, and then enters its right function, which is also empty, and returns 0. If the left and right sides of the current node are empty but this node is not empty, return 1 and go to the previous layer function, and so on, adding 1 for each node returned.
Insert image description here

int TreeSize(BTNode* root)
{
    
    
	if (root == NULL)
	{
    
    
		return 0;
	}
	return TreeSize(root->left) + TreeSize(root->right) + 1;
}

5. Number of leaf nodes of binary tree

The characteristic of a leaf node is that its left and right subtrees are both null pointers, and the recursive idea is still used. When the node recurses to the null pointer, 0 is returned; if the left and right subtrees of the current node are both null pointers, it means that the current node is a leaf node. , return 1
Insert image description here

int TreeLeafSize(BTNode* root)
{
    
    
	if (root == NULL)
	{
    
    
		return 0;
	}
	if (root->left == NULL && root->right == NULL)
	{
    
    
		return 1;
	}
	else
	{
    
    
		return TreeLeafSize(root->left) + TreeLeafSize(root->right);
	}
}

6. Number of nodes in the kth level of the binary tree

Pass in a parameter k, and the value of k is a certain level of the binary tree (the root node is the first level). When k is equal to 1, it is on the first layer; when k is equal to 2, it is on the second layer; when k is equal to 3, it is on the third layer... You can use a method. When k is equal to 1, it is on the kth layer. Use Function recursion, each time it recurses to the next level, the value of k is reduced by 1 until k equals 1; when the current node k is 1, 1 is returned, and finally the number of nodes in the kth layer can be counted.
Insert image description here

int TreeLevelKSize(BTNode* root, int k)
{
    
    
	if (root == NULL)
	{
    
    
		return 0;
	}
	if (k == 1)
	{
    
    
		return 1;
	}
	return TreeLevelKSize(root->left, k - 1) + TreeLevelKSize(root->right, k - 1);
}

7. Find the node with value x in a binary tree

Search for the node with value x, and return this node if found, otherwise return empty. If the current node is a null pointer, return null to the previous function. If the current node is not empty, determine whether the value of this node is x. If so, return this node to the previous function; if not, create a variable and let this variable receive the return value of the next recursive function, and then determine Whether this variable is empty, it means entering another function recursively, instead of returning this variable to the previous function (this variable is the found node). If there is no node to be found, return null.

BTNode* TreeFind(BTNode* root, BTDataType x)
{
    
    
	if (root == NULL)
	{
    
    
		return NULL;
	}
	if (root->val == x)
	{
    
    
		return root;
	}
	BTNode* ret = NULL;
	ret = TreeFind(root->left, x);
	if (ret)
	{
    
    
		return ret;
	}
	ret = TreeFind(root->right, x);
	if (ret)
	{
    
    
		return ret;
	}
	return NULL;
}

8. Pre-order, mid-order and post-order traversal of binary trees

Print nodes according to the three traversal methods of the binary tree, and the printing order of each method is different.

//二叉树前序遍历 
void TreePrevOrder(BTNode* root)
{
    
    
	if (root == NULL)
	{
    
    
		return;
	}
	printf("%c ", root->val);
	TreePrevOrder(root->left);
	TreePrevOrder(root->right);
}
//二叉树中序遍历
void TreeInOrder(BTNode* root)
{
    
    
	if (root == NULL)
	{
    
    
		return;
	}
	TreeInOrder(root->left);
	printf("%c ", root->val);
	TreeInOrder(root->right);
}
//二叉树后序遍历
void TreePostOrder(BTNode* root)
{
    
    
	if (root == NULL)
	{
    
    
		return;
	}
	TreePostOrder(root->left);
	TreePostOrder(root->right);
	printf("%c ", root->val);
}

9. Level-order traversal of binary trees

Layer-order traversal prints node data layer by layer and requires the use of a queue. The characteristic of the queue is first-in, first-out. We can first put the root node into the queue. At this time, the queue is not empty, and then use a temporary variable to get the head element of the queue (which is the root node at this time), determine whether its left child is empty, and then go to the queue. Put the left child node and skip it if it is empty; the same goes for the right child. Then print the value of this temporary variable node, and then delete it from the queue. Because nodes have been put in before, so the queue is not empty, so continue the previous steps, so that the value of each node can be printed layer by layer until the queue is empty and ends.
Insert image description here

void TreeLevelOrder(BTNode* root)
{
    
    
	Que q;
	QueInit(&q);
	QuePush(&q, root);
	while (!QueEmpty(&q))
	{
    
    
		BTNode* front = QueFront(&q);
		if (front->left)
		{
    
    
			QuePush(&q, front->left);
		}
		if (front->right)
		{
    
    
			QuePush(&q, front->right);
		}
		printf("%c ", front->val);
		QuePop(&q);
	}
}

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

To determine whether it is a complete binary tree or a queue structure, the queue can be traversed in layer order and data can be entered layer by layer. The characteristic of a complete binary tree is that the first h-1 level is full, the last level may be full or unsatisfied, and its last level must be continuous from left to right.

There are two loop controls here. In the first loop, the layer sequence puts nodes (including empty ones) into the queue, defines a temporary variable to get the head element of the queue, and judges whether it is empty. If it is empty, jump out of the first one. Loop, go to the second loop; if it is not empty, put its left and right child nodes into the queue (empty ones are also put).

In the second cycle, if the binary tree is a complete binary tree, it will not enter the second cycle, because if it is a complete binary tree, it will not jump out until it encounters an empty space in the first cycle. At this time, there will be no node behind the empty tree. , by the time of the second loop, since there is no node behind the empty queue, the queue is already empty and will not enter the second loop. It returns 0 directly, which is a complete binary tree.

If this binary tree is not a complete binary tree, then after the first loop encounters an empty jump, there are still nodes behind it. At this time, the queue is not empty. Continue to use the previous method and define a temporary variable to obtain the queue head element. The judgment is It is not empty, it is empty. Delete this empty and continue to judge (no matter how much distance is between the empty and the node, they are all in the queue, so the queue is not empty and continue to judge and delete until the queue is empty); if it is not empty, explain There are also nodes, which directly return 1, not a complete binary tree.

int TreeComplete(BTNode* root)
{
    
    
	Que q;
	QueInit(&q);
	QuePush(&q, root);
	while (!QueEmpty(&q))
	{
    
    
		BTNode* front = QueFront(&q);
		if (front == NULL)
		{
    
    
			break;
		}
		QuePush(&q, front->left);
		QuePush(&q, front->right);
		QuePop(&q);
	}
	while (!QueEmpty(&q))
	{
    
    
		BTNode* front = QueFront(&q);
		if (front != NULL)
		{
    
    
			return 1;
		}
		QuePop(&q);
	}
	return 0;
}

All code

1.BinaryTree.h

#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
#include <string.h>
#include <stdbool.h>
typedef char BTDataType;
typedef struct BinaryTreeNode
{
    
    
	BTDataType val;
	struct BinaryTreeNode* left;
	struct BinaryTreeNode* right;
}BTNode;
typedef BTNode* QDataType;
typedef struct QueueNode
{
    
    
	QDataType data;
	struct QueueNode* next;
}QNode;
typedef struct Queue
{
    
    
	QNode* head;
	QNode* tail;
	int size;
}Que;

//构建二叉树
BTNode* TreeCreate(char* a, int* pi);
//二叉树销毁
void TreeDestroy(BTNode* root);
//二叉树节点个数
int TreeSize(BTNode* root);
//二叉树叶子节点个数
int TreeLeafSize(BTNode* root);
//二叉树第k层节点个数
int TreeLevelKSize(BTNode* root, int k);
//二叉树查找值为x的节点
BTNode* TreeFind(BTNode* root, BTDataType x);
//二叉树前序遍历 
void TreePrevOrder(BTNode* root);
//二叉树中序遍历
void TreeInOrder(BTNode* root);
//二叉树后序遍历
void TreePostOrder(BTNode* root);
//层序遍历
void TreeLevelOrder(BTNode* root);
//判断二叉树是否是完全二叉树
int TreeComplete(BTNode* root);

//初始化
void QueInit(Que* pq);
//销毁
void QueDestroy(Que* pq);
//入队
void QuePush(Que* pq, QDataType x);
//出队
void QuePop(Que* pq);
//获取头部元素
QDataType QueFront(Que* pq);
//获取队尾元素
QDataType QueBack(Que* pq);
//获取元素个数
int QueSize(Que* pq);
//检查是否为空
bool QueEmpty(Que* pq);

2.BinaryTree.c

#include "BinaryTree.h"
//构建二叉树
BTNode* TreeCreate(char* a, int* pi)
{
    
    
	//如果是 # 说明为空返回上一层函数
	if (a[*pi] == '#')
	{
    
    
		(*pi)++;
		return NULL;
	}
	//不是空,开辟一个节点大小的空间,创建一个节点
	BTNode* root = (BTNode*)malloc(sizeof(BTNode));
	if (root == NULL)
	{
    
    
		perror("malloc fail");
		exit(-1);
	}
	root->val = a[*pi];
	(*pi)++;
	//当前节点连接孩子节点
	root->left = TreeCreate(a, pi);
	root->right = TreeCreate(a, pi);
	//当前节点连接上一个节点 || 返回根节点
	return root;
}
//二叉树销毁
void TreeDestroy(BTNode* root)
{
    
    
	if (root == NULL)
	{
    
    
		return;
	}
	TreeDestroy(root->left);
	TreeDestroy(root->right);
	free(root);
}
//二叉树节点个数
int TreeSize(BTNode* root)
{
    
    
	if (root == NULL)
	{
    
    
		return 0;
	}
	return TreeSize(root->left) + TreeSize(root->right) + 1;
}
//二叉树叶子节点个数
int TreeLeafSize(BTNode* root)
{
    
    
	if (root == NULL)
	{
    
    
		return 0;
	}
	if (root->left == NULL && root->right == NULL)
	{
    
    
		return 1;
	}
	else
	{
    
    
		return TreeLeafSize(root->left) + TreeLeafSize(root->right);
	}
}
//二叉树第k层节点个数
int TreeLevelKSize(BTNode* root, int k)
{
    
    
	if (root == NULL)
	{
    
    
		return 0;
	}
	if (k == 1)
	{
    
    
		return 1;
	}
	return TreeLevelKSize(root->left, k - 1) + TreeLevelKSize(root->right, k - 1);
}
//二叉树查找值为x的节点
BTNode* TreeFind(BTNode* root, BTDataType x)
{
    
    
	if (root == NULL)
	{
    
    
		return NULL;
	}
	if (root->val == x)
	{
    
    
		return root;
	}
	BTNode* ret = NULL;
	ret = TreeFind(root->left, x);
	if (ret)
	{
    
    
		return ret;
	}
	ret = TreeFind(root->right, x);
	if (ret)
	{
    
    
		return ret;
	}
	return NULL;
}
//二叉树前序遍历 
void TreePrevOrder(BTNode* root)
{
    
    
	if (root == NULL)
	{
    
    
		return;
	}
	printf("%c ", root->val);
	TreePrevOrder(root->left);
	TreePrevOrder(root->right);
}
//二叉树中序遍历
void TreeInOrder(BTNode* root)
{
    
    
	if (root == NULL)
	{
    
    
		return;
	}
	TreeInOrder(root->left);
	printf("%c ", root->val);
	TreeInOrder(root->right);
}
//二叉树后序遍历
void TreePostOrder(BTNode* root)
{
    
    
	if (root == NULL)
	{
    
    
		return;
	}
	TreePostOrder(root->left);
	TreePostOrder(root->right);
	printf("%c ", root->val);
}
//层序遍历
void TreeLevelOrder(BTNode* root)
{
    
    
	Que q;
	QueInit(&q);
	QuePush(&q, root);
	while (!QueEmpty(&q))
	{
    
    
		BTNode* front = QueFront(&q);
		if (front->left)
		{
    
    
			QuePush(&q, front->left);
		}
		if (front->right)
		{
    
    
			QuePush(&q, front->right);
		}
		printf("%c ", front->val);
		QuePop(&q);
	}
}
//判断二叉树是否是完全二叉树
int TreeComplete(BTNode* root)
{
    
    
	Que q;
	QueInit(&q);
	QuePush(&q, root);
	while (!QueEmpty(&q))
	{
    
    
		BTNode* front = QueFront(&q);
		if (front == NULL)
		{
    
    
			break;
		}
		QuePush(&q, front->left);
		QuePush(&q, front->right);
		QuePop(&q);
	}
	while (!QueEmpty(&q))
	{
    
    
		BTNode* front = QueFront(&q);
		if (front != NULL)
		{
    
    
			return 1;
		}
		QuePop(&q);
	}
	return 0;
}

//初始化
void QueInit(Que* pq)
{
    
    
	assert(pq);
	pq->head = NULL;
	pq->tail = NULL;
	pq->size = 0;
}
//销毁
void QueDestroy(Que* pq)
{
    
    
	assert(pq);
	QNode* cur = pq->head;
	while (cur)
	{
    
    
		QNode* next = cur->next;
		free(cur);
		cur = next;
	}
	pq->head = pq->tail = NULL;
	pq->size = 0;
}
//入队
void QuePush(Que* pq, QDataType x)
{
    
    
	assert(pq);
	QNode* newnode = (QNode*)malloc(sizeof(QNode));
	if (newnode == NULL)
	{
    
    
		perror("malloc fail");
		exit(-1);
	}
	newnode->data = x;
	newnode->next = NULL;
	if (pq->tail == NULL)
	{
    
    
		pq->head = pq->tail = newnode;
	}
	else
	{
    
    
		pq->tail->next = newnode;
		pq->tail = newnode;
	}
	pq->size++;
}
//出队
void QuePop(Que* pq)
{
    
    
	assert(pq);
	assert(!QueEmpty(pq));
	if (pq->head->next == NULL)
	{
    
    
		free(pq->head);
		pq->head = pq->tail = NULL;
	}
	else
	{
    
    
		QNode* next = pq->head->next;
		free(pq->head);
		pq->head = next;
	}
	pq->size--;
}
//获取头部元素
QDataType QueFront(Que* pq)
{
    
    
	assert(pq);
	assert(!QueEmpty(pq));
	return pq->head->data;
}
//获取队尾元素
QDataType QueBack(Que* pq)
{
    
    
	assert(pq);
	assert(!QueEmpty(pq));
	return pq->tail->data;
}
//获取元素个数
int QueSize(Que* pq)
{
    
    
	assert(pq);
	return pq->size;
}
//检查是否为空
bool QueEmpty(Que* pq)
{
    
    
	assert(pq);
	return pq->head == NULL;
}

3.test.c

#include "BinaryTree.h"
int main()
{
    
    
	char arr[] = "ABD##E#H##CF##G##";
	int i = 0;
	//构建二叉树
	BTNode* root = TreeCreate(arr, &i);
	//二叉树节点个数
	printf("二叉树节点个数:");
	printf("%d ", TreeSize(root));
	printf("\n");
	//二叉树叶子节点个数
	printf("二叉树叶子节点个数:");
	printf("%d ", TreeLeafSize(root));
	printf("\n");
	//二叉树第k层节点个数
	printf("二叉树第k层节点个数:");
	int k = 3;
	printf("%d ", TreeLevelKSize(root, k));
	printf("\n");
	//二叉树查找值为x的节点
	char x = 'G';
	BTNode* rec = TreeFind(root, x);
	if (rec)
	{
    
    
		printf("找到了\n");
	}
	else
	{
    
    
		printf("没找到\n");
	}
	//二叉树前序遍历
	printf("前:");
	TreePrevOrder(root);
	printf("\n");
	//二叉树中序遍历
	printf("中:");
	TreeInOrder(root);
	printf("\n");
	//二叉树后序遍历
	printf("后:");
	TreePostOrder(root);
	printf("\n");
	//层序遍历
	printf("层序:");
	TreeLevelOrder(root);
	printf("\n");
	//判断二叉树是否是完全二叉树
	if (0 == TreeComplete(root))
	{
    
    
		printf("是完全二叉树\n");
	}
	else
		printf("不是完全二叉树\n");

	//二叉树销毁
	TreeDestroy(root);
	return 0;
}

Insert image description here
Thanks for watching~

おすすめ

転載: blog.csdn.net/2301_77459845/article/details/133419488