Structure des données : arbre binaire et opérations associées


Préface

Avant d’implémenter l’arbre binaire, nous devons d’abord avoir une compréhension de base de l’arbre avant de pouvoir l’implémenter. L'utilisation de l'arborescence est très large, allant à l'arborescence des répertoires du système de fichiers, etc.


1. Concept et structure de l'arbre

1. Qu’est-ce qu’un arbre ?

L'arbre est une structure de données non linéaire , qui est un ensemble de relations hiérarchiques composées de n (n>=0) nœuds limités. Il existe un nœud spécial appelé nœud racine, et le nœud racine n'a pas de nœud prédécesseur. À l'exception du nœud racine, les autres nœuds sont divisés en M (M>0) ensembles disjoints T1, T2, ..., Tm, dont chacun Ti (1<= i <= m) est une structure et des sous-arbres arborescents . Le nœud racine de chaque sous-arbre a un et un seul prédécesseur et peut avoir 0 ou plusieurs successeurs. L’arbre est donc défini de manière récursive.
Comme le montre la figure :
insérer la description de l'image ici
Remarque : Dans la structure arborescente, il ne peut y avoir d'intersection entre sous-arbres, sinon ce n'est pas une structure arborescente.
Comme le montre la figure :
insérer la description de l'image ici
cela ne peut pas être appelé un arbre, cela constitue une structure en anneau, et c'est un graphique.

2. Concepts liés aux arbres

insérer la description de l'image ici

Degré de nœud : Le nombre de sous-arbres contenus dans un nœud est appelé le degré du nœud. Comme indiqué ci-dessus : Le degré de A est 3.
La profondeur de l'arbre : le niveau maximum de nœuds dans l'arbre. Comme indiqué ci-dessus : la profondeur de l'arbre est de 4
nœuds feuilles : le nœud de degré 0 est appelé nœud feuille. Comme le montre la figure ci-dessus : K est
le nœud enfant du nœud feuille : le nœud racine du sous-arbre contenu par un nœud est appelé nœud enfant du nœud. Comme le montre la figure ci-dessus : B est un nœud frère du nœud enfant de A
 : les nœuds avec le même nœud parent sont appelés nœuds frères les uns des autres. Comme le montre la figure ci-dessus : B et C sont des nœuds frères
et des nœuds cousins ​​: les nœuds dont les parents sont au même niveau sont cousins ​​l'un de l'autre. Comme le montre la figure ci-dessus : F et G sont des nœuds cousins

3. Représentation de l'arbre

La structure arborescente est plus compliquée que le tableau linéaire, et elle est plus difficile à stocker et à exprimer. Puisque la plage de valeurs est enregistrée, la relation entre les nœuds et les nœuds est également enregistrée. En pratique, il existe de nombreuses façons de représenter les arbres, telles que comme : représentation des parents, représentation des enfants, représentation des enfants-parents, représentation des enfants-frères, etc. Nous comprendrons ici brièvement la représentation la plus couramment utilisée des enfants frères.

typedef int DataType;
struct Node
{
    
    
	 struct Node* firstChild1; // 第一个孩子结点
	 struct Node* pNextBrother; // 指向其下一个兄弟结点
	 DataType data; // 结点中的数据域
};

La représentation de l'arbre ci-dessus est la suivante :
insérer la description de l'image ici

2. Concept et structure de l'arbre binaire

1. Concept d'arbre binaire

Il n’y a aucun nœud de degré supérieur à 2 dans l’arbre binaire,
comme le montre la figure :
insérer la description de l'image ici

2. Arbre binaire spécial

Arbre binaire complet : un arbre binaire. Si le nombre de nœuds dans chaque couche atteint le maximum, alors l'arbre binaire est un arbre binaire complet.
Arbre binaire complet : Pour un arbre binaire de profondeur K et n nœuds, il est appelé si et seulement si chaque nœud correspond aux nœuds numérotés de 1 à n dans un arbre binaire complet de profondeur K. Arbre binaire complet. Il convient de noter qu’un arbre binaire complet est un type particulier d’arbre binaire complet.
insérer la description de l'image ici

3. Propriétés des arbres binaires

1. Si le nombre de couches du nœud racine est spécifié comme 1, alors il y a au plus 2(i-1) nœuds sur la i-ème couche d'un arbre binaire non vide .
2. Si le nombre de couches du nœud racine est spécifié comme 1, alors le nombre maximum de nœuds dans un arbre binaire avec une profondeur de h est de 2 à la puissance h moins 1.
3. Pour tout arbre binaire, si le degré est 0, le nombre de nœuds feuilles est X et le nombre de nœuds de branche est Y, alors il y a X=Y+1
4. Pour un arbre complet avec n nœuds Dans un arbre binaire arbre, si tous les nœuds sont numérotés à partir de 0 selon l'ordre du tableau de haut en bas et de gauche à droite, alors pour le
nœud de numéro de série i :

1. Si i>0, le numéro de série parental du nœud à la position i : (i -1)/2 ; i=0, i est le numéro du nœud racine, pas de nœud parent
2. Si 2i+1<n, numéro d'enfant gauche : 2i+1, 2i+1>=n sinon pas d'enfant gauche
3. Si 2i +2<n, numéro d'enfant droit : 2i+2, 2i+2>=n sinon il n'y a pas d'enfant droit

4. Structure de stockage de l'arbre binaire

1. Stockage séquentiel Le stockage de structure séquentielle utilise des tableaux pour le stockage. Généralement, les tableaux ne conviennent que pour représenter des arbres binaires complets, car des arbres binaires non complets gaspilleront de l'espace. En réalité, seuls les tas sont stockés dans des tableaux. Le stockage séquentiel d'arbre binaire est physiquement un tableau et logiquement un arbre binaire.
2. Stockage en chaîne La structure de stockage en chaîne d'un arbre binaire signifie qu'une liste chaînée est utilisée pour représenter un arbre binaire, c'est-à-dire qu'une chaîne est utilisée pour indiquer la relation logique des éléments. La méthode habituelle est que chaque nœud de la liste chaînée est composé de trois champs, le champ de données et les champs de pointeur gauche et droit, et les pointeurs gauche et droit sont utilisés pour donner les adresses de stockage des points de liaison où l'enfant gauche et l'enfant droit du nœud est localisé.
insérer la description de l'image ici
Comme le montre la figure ci-dessus : la forme de stockage de l'arbre binaire ci-dessus est la suivante :
insérer la description de l'image ici
la structure du stockage en chaîne est la suivante :

typedef struct BinaryTreeNode
{
    
    
	BTDataType data;
	struct BinaryTreeNode* left;
	struct BinaryTreeNode* right;
}BTNode;

3. Mise en œuvre d'un arbre binaire équilibré

1. Créez l'arbre et le parcours avant, milieu et arrière de l'arbre

1. Traversée avant, milieu et arrière

La soi-disant traversée d'arbre binaire (Traversal) consiste à effectuer tour à tour les opérations correspondantes sur les nœuds de l'arbre binaire selon certaines règles spécifiques, et chaque nœud n'est opéré qu'une seule fois.
Parcours de pré-ordre : racine, sous-arbre gauche, sous-arbre droit
Parcours dans l'ordre : sous-arbre gauche, racine, sous-arbre droit
Parcours de post-ordre : sous-arbre gauche, racine du sous-arbre droit,
insérer la description de l'image ici
insérer la description de l'image ici
dans l'ordre et les parcours suivants sont les mêmes.

2. Créez un arbre et parcourez-le avant, pendant et après l'impression

Fonction à mettre en œuvre :

//创建二叉树
void TreeCreate(BTDataType* a, int n);
// 二叉树前序遍历 
void BinaryTreePrevOrder(BTNode* root);
// 二叉树中序遍历
void BinaryTreeInOrder(BTNode* root);
// 二叉树后序遍历
void BinaryTreePostOrder(BTNode* root);
// 二叉树销毁
void BinaryTreeDestory(BTNode** root);

Le code d'implémentation est le suivant :

//创建二叉树
void TreeCreate(BTNode** root ,BTDataType num)
{
    
    
	if (*root == NULL)//如果节点为空证明该位置为要插入的位置
	{
    
    
		BTNode* pos = (BTNode*)malloc(sizeof(BTNode));//开辟节点
		if (pos == NULL)//判断节点是否开辟成功
		{
    
    
			perror("malloc");
			exit(-1);
		}
		pos->data = num;
		pos->left = NULL;
		pos->right = NULL;
		*root = pos;//把该节点连接到树上
		return;
	}
	if (num < (*root)->data)//比根节点小则在左边插入
	{
    
    
		TreeCreate(&(*root)->left, num);
	}
	else//比根节点大则在右边插入
	{
    
    
		TreeCreate(&(*root)->right, num);
	}
}
// 二叉树前序遍历 
void BinaryTreePrevOrder(BTNode* root)
{
    
    
	if (root == NULL)
	{
    
    
		return;
	}
	printf("%d ", root->data);
	BinaryTreePrevOrder(root->left);
	BinaryTreePrevOrder(root->right);
}
// 二叉树中序遍历
void BinaryTreeInOrder(BTNode* root)
{
    
    
	if (root == NULL)
	{
    
    
		return;
	}
	BinaryTreeInOrder(root->left);
	printf("%d ", root->data);
	BinaryTreeInOrder(root->right);
}
// 二叉树后序遍历
void BinaryTreePostOrder(BTNode* root)
{
    
    
	if (root == NULL)
	{
    
    
		return;
	}
	BinaryTreePostOrder(root->left);
	BinaryTreePostOrder(root->right);
	printf("%d ", root->data);
}
// 二叉树销毁
void BinaryTreeDestory(BTNode** root)
{
    
    
	if (*root == NULL)
	{
    
    
		return;
	}
	BinaryTreeDestory(&(*root)->left);
	BinaryTreeDestory(&(*root)->right);
	free(*root);
}

Fonction de test :

void test()
{
    
    
	int arr[] = {
    
     2,1,3,7,6,5,9,8,4 };
	int size = sizeof(arr) / sizeof(arr[0]);
	BTNode* root = NULL;
	int i = 0;
	for (i = 0; i < size; i++)
	{
    
    
		TreeCreate(&root, arr[i]);//创建二叉树
	}
	BinaryTreePrevOrder(root);//前
	printf("\n");
	BinaryTreeInOrder(root);//中
	printf("\n");
	BinaryTreePostOrder(root);//后
	printf("\n");
	BinaryTreeDestory(&root);//销毁
}
int main()
{
    
    
	test();
	return 0;
}

L'arbre binaire construit est le suivant :
insérer la description de l'image ici
le processus partiel de récursion de pré-ordre est le suivant :
insérer la description de l'image ici
le pré-ordre doit d'abord imprimer la valeur du nœud racine avant la récursion, l'ordre intermédiaire doit d'abord effectuer une récursion à gauche, imprimer la valeur et enfin effectuer une récursivité à droite, et le suivi consiste à effectuer d'abord une récursion gauche et droite. Imprimez la valeur correspondante.
Remarque : La destruction de l'arbre binaire nécessite un parcours ultérieur. Seuls les sous-arbres gauche et droit de la racine peuvent être libérés avant que le nœud racine puisse être libéré. Si le nœud racine est libéré avant les sous-arbres gauche et droit, les nœuds gauche et droit correspondants ne seront pas trouvés, provoquant une fuite de mémoire.

2. Conversion en arbre binaire équilibré et opérations associées

L'arbre que nous venons de construire a évidemment plus de nœuds d'un côté et moins de nœuds de l'autre côté, nous convertissons donc l'arbre ci-dessus en une différence entre les nœuds des sous-arbres gauche et droit dans 1, et équilibrons la gauche et la droite.

1. Convertir en un arbre binaire équilibré

// 二叉树节点个数
int BinaryTreeSize(BTNode* root)
{
    
    
	if (root == NULL)
	{
    
    
		return 0;
	}
	return BinaryTreeSize(root->left) + 1 + BinaryTreeSize(root->right);
}
//左旋
BTNode* get_min(BTNode* root)
{
    
    
	if (root->left == NULL)
	{
    
    
		return root;
	}
	return get_min(root->left);//返回最左边的节点
}
void turn_left(BTNode** root)
{
    
    
	BTNode* pos = *root;
	(*root) = pos->right;//更换头节点
	pos->right = NULL;//避免出现野指针
	get_min(*root)->left = pos;//把最左边的节点链接此接点
}
//右旋
BTNode* get_max(BTNode* root)
{
    
    
	if (root->right == NULL)
	{
    
    
		return root;
	}
	return get_max(root->right);//返回最右边的节点
}
void turn_right(BTNode** root)
{
    
    
	BTNode* pos = *root;
	(*root) = pos->left;
	pos->left = NULL;
	get_max(*root)->right = pos;//把最右边的节点链接此接点
}
//转换为平衡二叉树
void BalanceTree(BTNode** root)
{
    
    
	if (*root == NULL)
	{
    
    
		return;
	}
	while(1)
	{
    
    
		int a = BinaryTreeSize((*root)->left);
		int b = BinaryTreeSize((*root)->right);
		int num = BinaryTreeSize((*root)->left) - BinaryTreeSize((*root)->right);
		if (num < -1)//右边多
		{
    
    
			//&(*root)中&和*抵消了,所以传一个root就可以了,但接收要用二级指针
			turn_left(root);//进行左旋
		}
		else if (num > 1)//左边多
		{
    
    
			turn_right(root);//进行右旋
		}
		else
		{
    
    
			break;
		}
	}
	BalanceTree(&(*root)->left);//开始平衡左子树
	BalanceTree(&(*root)->right);//开始平衡右子树
}

Nous devons juger si l'arbre est équilibré, nous devons donc trouver le nombre de nœuds gauche et droit. Nous devons également effectuer des rotations gauche et droite lorsque le nombre de nœuds gauches est supérieur au nombre de nœuds droits et que la droite est plus grande que la gauche.
insérer la description de l'image ici
Il s'agit d'un cycle.Lorsque la gauche et la droite du nœud racine actuel sont équilibrées, l'équilibre des sous-arbres gauche et droit commence.
insérer la description de l'image ici
De cette façon, notre arbre équilibré est construit.
Remarque : Nous devons nous connecter au dernier nœud pour la rotation à gauche et à droite, afin de pouvoir nous assurer que nous sommes toujours dans l'ordre après la rotation. Le résultat du sous-arbre gauche est plus petit que le nœud racine et le nœud du sous-arbre droit est plus grand. que le nœud racine.

2. Parcours par ordre de niveau des arbres binaires

La traversée de l'ordre des couches consiste à visiter les nœuds de l'arbre binaire en fonction de la couche

// 层序遍历
void BinaryTreeLevelOrder(BTNode* root)
{
    
    
	Queue qu;//创建队列
	QueueInit(&qu);//初始化队列
	QueuePush(&qu, root);//把根节点带入
	while (!QueueEmpty(&qu))//如果不为空则一直进行循环
	{
    
    
		BTNode*pos =  QueueFront(&qu);// 获取队列头部元素 
		printf("%d ", pos->data);
		QueuePop(&qu);//头部元素出队列
		if(pos->left != NULL)//如果左孩子不为空则进队列
			QueuePush(&qu, pos->left);
		if (pos->right != NULL)//如果右孩子不为空则进队列
			QueuePush(&qu, pos->right);
	}
	QueueDestroy(&qu);
}

Le parcours hiérarchique nous oblige à utiliser des files d'attente pour les opérations auxiliaires
insérer la description de l'image ici
Le parcours hiérarchique nous oblige à utiliser des files d'attente pour les opérations auxiliaires.Le nœud parent pilote les nœuds enfants, et lorsque les nœuds enfants ne sont pas vides, ils entrent dans la file d'attente.
Code d'essai :

void test()
{
    
    
	int arr[] = {
    
     2,1,3,7,6,5,9,8,4 };
	int size = sizeof(arr) / sizeof(arr[0]);
	BTNode* root = NULL;
	int i = 0;
	for (i = 0; i < size; i++)
	{
    
    
		TreeCreate(&root, arr[i]);//创建二叉树
	}
	BalanceTree(&root);//构建
	BinaryTreeLevelOrder(root);//层
	BinaryTreeDestory(&root);//销毁
}
int main()
{
    
    
	test();
	return 0;
}

insérer la description de l'image ici

3. Déterminez s'il s'agit d'un arbre binaire complet

// 判断二叉树是否是完全二叉树
bool BinaryTreeComplete(BTNode* root)
{
    
    
	Queue qu;//创建队列
	QueueInit(&qu);//初始化队列
	QueuePush(&qu, root);//把根节点带入
	while (!QueueEmpty(&qu))//如果不为空则一直进行循环
	{
    
    
		BTNode* pos = QueueFront(&qu);// 获取队列头部元素 
		QueuePop(&qu);//头部元素出队列
		if (pos != NULL)
		{
    
    
			QueuePush(&qu, pos->left);
			QueuePush(&qu, pos->right);
		}
		else
		{
    
    
			while (!QueueEmpty(&qu))//一直出队列,直到队列为空停止循环
			{
    
    
				pos = QueueFront(&qu);// 获取队列头部元素 
				if (pos != NULL)//如果头部不为空,则证明不为完全二叉树
				{
    
    
					QueueDestroy(&qu);//销毁队列
					return false;
				}
				QueuePop(&qu);//头部元素出队列
			}
		}
	}
	QueueDestroy(&qu);
	return true;
}

insérer la description de l'image ici
Déterminer s'il s'agit d'un arbre binaire complet nécessite également l'utilisation d'une file d'attente. Lorsqu'il s'agit d'un arbre binaire complet, la file d'attente n'aura pas de données et des files d'attente vides alternativement hors de la file d'attente, mais un arbre binaire non complet aura des données et vide alternativement hors de la file d'attente. Le code de test est le suivant
:

void test()
{
    
    
	int arr[] = {
    
     2,1,3,7,6,5,9,8,4 };
	int size = sizeof(arr) / sizeof(arr[0]);
	BTNode* root = NULL;
	int i = 0;
	for (i = 0; i < size; i++)
	{
    
    
		TreeCreate(&root, arr[i]);//创建二叉树
	}
	BalanceTree(&root);//构建
	if (BinaryTreeComplete(root))//完全
	{
    
    
		printf("YES\n");
	}
	else
	{
    
    
		printf("NO\n");
	}
	BinaryTreeDestory(&root);//销毁
}
int main()
{
    
    
	test();
	return 0;
}

insérer la description de l'image ici

4. Suppression de nœuds dans un arbre binaire équilibré

//删除头节点,可以改为按查找节点删除
void DelTree(BTNode** root)//进行左旋
{
    
    
	BTNode* del = *root;//保存要删除的元素节点
	*root = del->left;//更换头节点
	get_max(*root)->right = del->right;
	free(del);
	BalanceTree(root);//从新构建平衡树
}

Ce que nous choisissons est de supprimer les nœuds tête et racine, puis d'effectuer une rotation vers la gauche pour reconstruire l'arbre et construire un arbre équilibré. Nous pouvons également choisir de supprimer le nœud spécifié, et la suppression du nœud de l'arborescence n'a généralement aucun sens.
Le processus est le suivant :
insérer la description de l'image ici

3. Autres opérations sur les arbres binaires

// 二叉树最深节点
int DeepTree(BTNode* root)
{
    
    
	if (root == NULL)
	{
    
    
		return 0;
	}
	//三目表达式,返回左子树和右子树最大的那个
	return DeepTree(root->left) > DeepTree(root->right) ? DeepTree(root->left) + 1 : DeepTree(root->right) + 1;
}
// 二叉树叶子节点个数
int BinaryTreeLeafSize(BTNode* root)
{
    
    
	if (root == NULL)//当该节点为空时返回上一级,证明上一级左子树或右子树有一个不为空
	{
    
    
		return 0;
	}
	if (root->left == NULL && root->right == NULL)//当左右子树都为空时这个节点为叶子节点
	{
    
    
		return 1;
	}
	return BinaryTreeLeafSize(root->left) + BinaryTreeLeafSize(root->right);//返回左右子树的总和
	
}
// 二叉树第k层节点个数
int BinaryTreeLevelKSize(BTNode* root, int k)
{
    
    
	if (k == 1)//从第一层开始,所以递减到1就可以了
	{
    
    
		return 1;
	}
	return BinaryTreeLevelKSize(root->left, k - 1) + BinaryTreeLevelKSize(root->right, k - 1);//返回左右子树的总和
}
// 二叉树查找值为x的节点
//BTNode* BinaryTreeFind(BTNode* root, BTDataType x)
//{
    
    
//	if (root == NULL)//如果没找到则返回空
//	{
    
    
//		return NULL;
//	}
//	if (root->data == x)
//	{
    
    
//		return root;
//	}
//	BTNode* left = BinaryTreeFind(root->left, x);
//	BTNode* right = BinaryTreeFind(root->right, x);
//	if (left == NULL)//如果左子树没找到该值,则返回右子树的值,右子树树中也没找到返回的空,找到则返回相应的节点
//	{
    
    
//		return right;
//	}
//	return left;//要找的节点在左子树上
//}
// 二叉树查找值为x的节点
BTNode* BinaryTreeFind(BTNode* root, BTDataType x)
{
    
    
	BTNode* pos = root;
	while (pos != NULL)
	{
    
    
		if (pos->data < x)//该节点的值小于查找的值则在树的右边
		{
    
    
			pos = pos->right;
		}
		else if(pos->data > x)//该节点的值大于查找的值则在树的左边
		{
    
    
			pos = pos->left;
		}
		else
		{
    
    
			break;
		}
	}
	return pos;
}

Les nœuds de l'arbre binaire peuvent être recherchés de la manière normale, ou nous pouvons concevoir une fonction pour l'arbre binaire équilibré que nous avons construit. Les nœuds de l'arbre binaire équilibré que nous avons conçu répondent tous à la valeur de l'enfant de gauche est inférieure à la valeur du parent. nœud et la valeur du bon enfant est supérieure à la valeur du nœud parent, cette conception est pratique à trouver.
Fonction de test :

void test()
{
    
    
	int arr[] = {
    
     2,1,3,7,6,5,9,8,4 };
	int size = sizeof(arr) / sizeof(arr[0]);
	BTNode* root = NULL;
	int i = 0;
	for (i = 0; i < size; i++)
	{
    
    
		TreeCreate(&root, arr[i]);//创建二叉树
	}
	BalanceTree(&root);//构建
	printf("%d\n", DeepTree(root));//深度
	printf("%d\n", BinaryTreeLeafSize(root));//节点个数
	printf("%d\n", BinaryTreeLevelKSize(root,3));//k层
	printf("%d\n", BinaryTreeFind(root,3)->data);//查找节点
	BinaryTreeDestory(&root);//销毁
}
int main()
{
    
    
	test();
	return 0;
}

insérer la description de l'image ici

Résumer

Nous débloquerons progressivement le contenu le plus avancé de l'arbre dans une étude plus approfondie.
Voici le code complet de cette époque :
main.c :

#include"main.h"
void test()
{
    
    
	int arr[] = {
    
     2,1,3,7,6,5,9,8,4 };
	int size = sizeof(arr) / sizeof(arr[0]);
	BTNode* root = NULL;
	int i = 0;
	for (i = 0; i < size; i++)
	{
    
    
		TreeCreate(&root, arr[i]);//创建二叉树
	}
	BinaryTreePrevOrder(root);//前
	printf("\n");
	BinaryTreeInOrder(root);//中
	printf("\n");
	BinaryTreePostOrder(root);//后
	printf("\n");
	printf("%d", BinaryTreeSize(root));//节点个数
	printf("\n");
	BalanceTree(&root);//构建
	BinaryTreeLevelOrder(root);//层
	printf("\n");
	if (BinaryTreeComplete(root))//完全
	{
    
    
		printf("YES\n");
	}
	else
	{
    
    
		printf("NO\n");
	}
	printf("%d\n", DeepTree(root));//深度
	printf("%d\n", BinaryTreeLeafSize(root));//节点个数
	printf("%d\n", BinaryTreeLevelKSize(root,3));//k层
	printf("%d\n", BinaryTreeFind(root,3)->data);//查找节点
	DelTree(&root);
	BinaryTreeLevelOrder(root);//层
	BinaryTreeDestory(&root);//销毁
}
int main()
{
    
    
	test();
	return 0;
}

main.h :

#pragma once
#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
#include<stdlib.h>
#include<stdbool.h>
#include<assert.h>
typedef int BTDataType;
typedef struct BinaryTreeNode
{
    
    
	BTDataType data;
	struct BinaryTreeNode* left;
	struct BinaryTreeNode* right;
}BTNode;
// 链式结构:表示队列 
typedef BTNode* QDataType;
typedef struct QListNode
{
    
    
	struct QListNode* next;
	QDataType data;
}QNode;
// 队列的结构 
typedef struct Queue
{
    
    
	QNode* front;
	QNode* rear;
}Queue;
// 初始化队列 
void QueueInit(Queue* q);
// 队尾入队列 
void QueuePush(Queue* q, QDataType data);
// 队头出队列 
void QueuePop(Queue* q);
// 获取队列头部元素 
QDataType* QueueFront(Queue* q);
// 检测队列是否为空,如果为空返回非零结果,如果非空返回0 
bool QueueEmpty(Queue* q);
// 销毁队列 
void QueueDestroy(Queue* q);
//创建二叉树
void TreeCreate(BTDataType* a, int n);
// 二叉树前序遍历 
void BinaryTreePrevOrder(BTNode* root);
// 二叉树中序遍历
void BinaryTreeInOrder(BTNode* root);
// 二叉树后序遍历
void BinaryTreePostOrder(BTNode* root);
// 二叉树销毁
void BinaryTreeDestory(BTNode** root);
// 二叉树节点个数
int BinaryTreeSize(BTNode* root);
//转换为平衡二叉树
void BalanceTree(BTNode** root);
//删除头节点
void DelTree(BTNode** root);
// 层序遍历
void BinaryTreeLevelOrder(BTNode* root);
// 判断二叉树是否是完全二叉树
bool BinaryTreeComplete(BTNode* root);
// 二叉树最深节点
int DeepTree(BTNode* root);
// 二叉树叶子节点个数
int BinaryTreeLeafSize(BTNode* root);
// 二叉树第k层节点个数
int BinaryTreeLevelKSize(BTNode* root, int k);
// 二叉树查找值为x的节点
BTNode* BinaryTreeFind(BTNode* root, BTDataType x);

test.c :

#include"main.h"

// 初始化队列 
void QueueInit(Queue* q)
{
    
    
	assert(q);
	q->front = q->rear = NULL;
}
// 队尾入队列 
void QueuePush(Queue* q, QDataType data)
{
    
    
	assert(q);
	QNode* pos = (QNode*)malloc(sizeof(QNode));
	if (pos == NULL)
	{
    
    
		perror("malloc");
		exit(-1);
	}
	pos->data = data;
	pos->next = NULL;
	if (q->rear == NULL)
	{
    
    
		q->front = q->rear = pos;
	}
	else
	{
    
    
		q->rear->next = pos;
		q->rear = pos;
	}
}
// 队头出队列 
void QueuePop(Queue* q)
{
    
    
	assert(q);
	assert(q->front);
	if (q->front->next == NULL)
	{
    
    
		free(q->front);
		q->front = q->rear = NULL;
	}
	else
	{
    
    
		QNode* next = q->front->next;
		free(q->front);
		q->front = next;
	}
}
// 获取队列头部元素 
QDataType* QueueFront(Queue* q)
{
    
    
	assert(q);
	assert(q->front);
	return q->front->data;
}
// 检测队列是否为空,如果为空返回非零结果,如果非空返回0 
bool QueueEmpty(Queue* q)
{
    
    
	assert(q);
	return q->front == NULL;
}
// 销毁队列 
void QueueDestroy(Queue* q)
{
    
    
	assert(q);
	QNode* pos = q->front;
	while (pos)
	{
    
    
		QNode* next = pos->next;
		free(pos);
		pos = next;
	}
	q->front = q->rear = NULL;
}
//创建二叉树
void TreeCreate(BTNode** root ,BTDataType num)
{
    
    
	if (*root == NULL)//如果节点为空证明该位置为要插入的位置
	{
    
    
		BTNode* pos = (BTNode*)malloc(sizeof(BTNode));//开辟节点
		if (pos == NULL)//判断节点是否开辟成功
		{
    
    
			perror("malloc");
			exit(-1);
		}
		pos->data = num;
		pos->left = NULL;
		pos->right = NULL;
		*root = pos;//把该节点连接到树上
		return;
	}
	if (num < (*root)->data)//比根节点小则在左边插入
	{
    
    
		TreeCreate(&(*root)->left, num);
	}
	else//比根节点大则在右边插入
	{
    
    
		TreeCreate(&(*root)->right, num);
	}
}
// 二叉树前序遍历 
void BinaryTreePrevOrder(BTNode* root)
{
    
    
	if (root == NULL)
	{
    
    
		return;
	}
	printf("%d ", root->data);
	BinaryTreePrevOrder(root->left);
	BinaryTreePrevOrder(root->right);
}
// 二叉树中序遍历
void BinaryTreeInOrder(BTNode* root)
{
    
    
	if (root == NULL)
	{
    
    
		return;
	}
	BinaryTreeInOrder(root->left);
	printf("%d ", root->data);
	BinaryTreeInOrder(root->right);
}
// 二叉树后序遍历
void BinaryTreePostOrder(BTNode* root)
{
    
    
	if (root == NULL)
	{
    
    
		return;
	}
	BinaryTreePostOrder(root->left);
	BinaryTreePostOrder(root->right);
	printf("%d ", root->data);
}
// 二叉树销毁
void BinaryTreeDestory(BTNode** root)
{
    
    
	if (*root == NULL)
	{
    
    
		return;
	}
	BinaryTreeDestory(&(*root)->left);
	BinaryTreeDestory(&(*root)->right);
	free(*root);
}
// 二叉树节点个数
int BinaryTreeSize(BTNode* root)
{
    
    
	if (root == NULL)
	{
    
    
		return 0;
	}
	return BinaryTreeSize(root->left) + 1 + BinaryTreeSize(root->right);
}
//左旋
BTNode* get_min(BTNode* root)
{
    
    
	if (root->left == NULL)
	{
    
    
		return root;
	}
	return get_min(root->left);//返回最左边的节点
}
void turn_left(BTNode** root)
{
    
    
	BTNode* pos = *root;
	(*root) = pos->right;//更换头节点
	pos->right = NULL;//避免出现野指针
	get_min(*root)->left = pos;//把最左边的节点链接此接点
}
//右旋
BTNode* get_max(BTNode* root)
{
    
    
	if (root->right == NULL)
	{
    
    
		return root;
	}
	return get_max(root->right);//返回最右边的节点
}
void turn_right(BTNode** root)
{
    
    
	BTNode* pos = *root;
	(*root) = pos->left;
	pos->left = NULL;
	get_max(*root)->right = pos;//把最右边的节点链接此接点
}
//转换为平衡二叉树
void BalanceTree(BTNode** root)
{
    
    
	if (*root == NULL)
	{
    
    
		return;
	}
	while(1)
	{
    
    
		int a = BinaryTreeSize((*root)->left);
		int b = BinaryTreeSize((*root)->right);
		int num = BinaryTreeSize((*root)->left) - BinaryTreeSize((*root)->right);
		if (num < -1)//右边多
		{
    
    
			//&(*root)中&和*抵消了,所以传一个root就可以了,但接收要用二级指针
			turn_left(root);//进行左旋
		}
		else if (num > 1)//左边多
		{
    
    
			turn_right(root);//进行右旋
		}
		else
		{
    
    
			break;
		}
	}
	BalanceTree(&(*root)->left);//开始平衡左子树
	BalanceTree(&(*root)->right);//开始平衡右子树
}
//删除头节点,可以改为按查找节点删除
void DelTree(BTNode** root)//进行左旋
{
    
    
	BTNode* del = *root;//保存要删除的元素节点
	*root = del->left;//更换头节点
	get_max(*root)->right = del->right;
	free(del);
	BalanceTree(root);//从新构建平衡树
}
// 层序遍历
void BinaryTreeLevelOrder(BTNode* root)
{
    
    
	Queue qu;//创建队列
	QueueInit(&qu);//初始化队列
	QueuePush(&qu, root);//把根节点带入
	while (!QueueEmpty(&qu))//如果不为空则一直进行循环
	{
    
    
		BTNode*pos =  QueueFront(&qu);// 获取队列头部元素 
		printf("%d ", pos->data);
		QueuePop(&qu);//头部元素出队列
		if(pos->left != NULL)//如果左孩子不为空则进队列
			QueuePush(&qu, pos->left);
		if (pos->right != NULL)//如果右孩子不为空则进队列
			QueuePush(&qu, pos->right);
	}
	QueueDestroy(&qu);
}
// 判断二叉树是否是完全二叉树
bool BinaryTreeComplete(BTNode* root)
{
    
    
	Queue qu;//创建队列
	QueueInit(&qu);//初始化队列
	QueuePush(&qu, root);//把根节点带入
	while (!QueueEmpty(&qu))//如果不为空则一直进行循环
	{
    
    
		BTNode* pos = QueueFront(&qu);// 获取队列头部元素 
		QueuePop(&qu);//头部元素出队列
		if (pos != NULL)
		{
    
    
			QueuePush(&qu, pos->left);
			QueuePush(&qu, pos->right);
		}
		else
		{
    
    
			while (!QueueEmpty(&qu))//一直出队列,直到队列为空停止循环
			{
    
    
				pos = QueueFront(&qu);// 获取队列头部元素 
				if (pos != NULL)//如果头部不为空,则证明不为完全二叉树
				{
    
    
					QueueDestroy(&qu);//销毁队列
					return false;
				}
				QueuePop(&qu);//头部元素出队列
			}
		}
	}
	QueueDestroy(&qu);
	return true;
}
// 二叉树最深节点
int DeepTree(BTNode* root)
{
    
    
	if (root == NULL)
	{
    
    
		return 0;
	}
	//三目表达式,返回左子树和右子树最大的那个
	return DeepTree(root->left) > DeepTree(root->right) ? DeepTree(root->left) + 1 : DeepTree(root->right) + 1;
}
// 二叉树叶子节点个数
int BinaryTreeLeafSize(BTNode* root)
{
    
    
	if (root == NULL)//当该节点为空时返回上一级,证明上一级左子树或右子树有一个不为空
	{
    
    
		return 0;
	}
	if (root->left == NULL && root->right == NULL)//当左右子树都为空时这个节点为叶子节点
	{
    
    
		return 1;
	}
	return BinaryTreeLeafSize(root->left) + BinaryTreeLeafSize(root->right);//返回左右子树的总和
	
}
// 二叉树第k层节点个数
int BinaryTreeLevelKSize(BTNode* root, int k)
{
    
    
	if (k == 1)//从第一层开始,所以递减到1就可以了
	{
    
    
		return 1;
	}
	return BinaryTreeLevelKSize(root->left, k - 1) + BinaryTreeLevelKSize(root->right, k - 1);//返回左右子树的总和
}
// 二叉树查找值为x的节点
//BTNode* BinaryTreeFind(BTNode* root, BTDataType x)
//{
    
    
//	if (root == NULL)//如果没找到则返回空
//	{
    
    
//		return NULL;
//	}
//	if (root->data == x)
//	{
    
    
//		return root;
//	}
//	BTNode* left = BinaryTreeFind(root->left, x);
//	BTNode* right = BinaryTreeFind(root->right, x);
//	if (left == NULL)//如果左子树没找到该值,则返回右子树的值,右子树树中也没找到返回的空,找到则返回相应的节点
//	{
    
    
//		return right;
//	}
//	return left;//要找的节点在左子树上
//}
// 二叉树查找值为x的节点
BTNode* BinaryTreeFind(BTNode* root, BTDataType x)
{
    
    
	BTNode* pos = root;
	while (pos != NULL)
	{
    
    
		if (pos->data < x)//该节点的值小于查找的值则在树的右边
		{
    
    
			pos = pos->right;
		}
		else if(pos->data > x)//该节点的值大于查找的值则在树的左边
		{
    
    
			pos = pos->left;
		}
		else
		{
    
    
			break;
		}
	}
	return pos;
}

Je suppose que tu aimes

Origine blog.csdn.net/2301_76986069/article/details/132459813
conseillé
Classement