Structure de données (implémentation du langage C) - le concept d'arbre binaire et la réalisation de la structure de séquence d'arbre binaire et de la structure de chaîne (tri en tas + problème TOP-K + opérations liées à l'arbre binaire en chaîne)

1. Introduction

Auparavant, nous avons appris plusieurs structures de structures linéaires dans les structures de données, telles que les listes séquentielles, les listes chaînées, les piles et les files d'attente, etc. Aujourd'hui, nous allons apprendre une structure arborescente de données non linéaire. Étant donné que l'arbre binaire est un point important et difficile dans la structure des données, cet article se concentre sur l'introduction des concepts et propriétés connexes de l'arbre binaire, ainsi que sur l'application de l'arbre binaire.

2. Le concept et la structure de l'arbre

2.1 Le concept d'arbre

Un arbre est une structure de données non linéaire, qui est un ensemble de relations hiérarchiques composé de n (n>=0) nœuds finis. On l'appelle un arbre parce qu'il ressemble à un arbre à l'envers, ce qui signifie qu'il a les racines pointant vers le haut et les feuilles pointant vers le bas.
Remarque :
1. L'arbre est défini de manière récursive
2. Dans l'arborescence, il ne peut y avoir d'intersection entre les sous-arbres, sinon ce n'est pas une arborescence

insérez la description de l'image ici

2.2 Concepts connexes d'arbres

S'il y a un arbre comme indiqué dans la figure ci-dessous :
insérez la description de l'image ici
alors il a les concepts suivants :

Degré d'un nœud : Le nombre de sous-arbres contenus dans un nœud est appelé le degré du nœud ; Comme le montre la figure ci-dessus : A est un
nœud à 6 feuilles ou nœud terminal : un nœud avec un degré de 0 est appelé une feuille nœud ; Comme le montre la figure ci-dessus : B, C, Les nœuds tels que H, I... sont des nœuds feuilles, des
nœuds non terminaux ou des nœuds de branche : nœuds dont le degré est différent de 0 ; comme le montre la figure ci-dessus : des nœuds tels car D, E, F, G... sont des nœuds de branche.
Nœuds parents ou nœuds parents : si un nœud contient un nœud enfant, alors ce nœud est appelé le nœud parent de son nœud enfant ; comme le montre la figure ci-dessus : A est le nœud parent de B nœud enfant
ou nœud enfant : le nœud racine du sous-arbre contenu dans un nœud est appelé le nœud enfant du nœud ; comme le montre la figure ci-dessus : B C'est un nœud enfant de A. Nœuds frères
: nœuds avec le même nœud parent sont appelés nœuds frères ; comme le montre la figure ci-dessus : B et C sont le
degré de l'arbre des nœuds frères : dans un arbre, le degré du plus grand nœud est appelé le degré de l'arbre ; comme ci-dessus Figure : Le niveau de l'arbre est de 6
nœuds : à partir de la définition de la racine, la racine est le premier niveau, les nœuds enfants de la racine sont le deuxième niveau, et ainsi de suite ; la
hauteur ou profondeur de l'arbre : le maximum niveau des nœuds dans l'arbre ; comme ci-dessus Figure : La hauteur de l'arbre est de 4
 Nœuds cousins :
les nœuds dont les parents sont sur la même couche sont des cousins ; Figure : A est l'ancêtre et le descendant de tous les nœuds
 : tout nœud dans le sous-arbre enraciné à un certain nœud est appelé le descendant du nœud. Comme le montre la figure ci-dessus : Tous les nœuds sont des descendants de A
Forest : Une collection de m (m>0) arbres disjoints est appelée une forêt ;

2.3 Représentation arborescente

Lorsque l'arbre est stocké, il est nécessaire de sauvegarder à la fois les données et la relation entre les nœuds. En pratique, il existe de nombreuses méthodes de représentation de l'arbre, telles que : la représentation parent, la représentation enfant, la représentation parent enfant et la notation des frères et sœurs enfants. etc. Ci-dessous, nous introduisons la notation de frère enfant la plus couramment utilisée.

typedef int TreeDataType;
struct TreeNode
{
    
    
	TreeDataType data;//结点的数据域
	struct TreeNode* FirstChild;//指向其第一个孩子结点
	struct TreeNode* NextBrother;//指向其下一个兄弟结点
};

insérez la description de l'image ici

3. Le concept d'arbre binaire

Un arbre binaire est un ensemble fini de nœuds, l'ensemble :
1. Ou vide
2. Il se compose d'un nœud racine plus deux arbres binaires appelés respectivement sous-arbre gauche et sous-arbre droit

Comme le montre la figure ci-dessous, il s'agit d'un arbre binaire :
insérez la description de l'image ici
sur la figure ci-dessus, nous pouvons voir que :

1. Il n'y a pas de nœud avec un degré supérieur à 2 dans un arbre binaire
2. Les sous-arbres d'un arbre binaire sont divisés en gauche et droite, et l'ordre ne peut pas être inversé, donc un arbre binaire est un arbre ordonné

3.1 Arbre binaire spécial

Il existe également deux arbres binaires spéciaux dans l'arbre binaire :

1. Arbre binaire complet : un arbre binaire, si le nombre de nœuds dans chaque couche atteint la valeur maximale, alors cet arbre binaire est un arbre binaire complet. C'est-à-dire que si un arbre binaire a K couches et que le nombre total de nœuds est 2^k - 1, alors c'est un arbre binaire complet.

insérez la description de l'image ici

2. Arbre binaire complet : Un arbre binaire complet est une structure de données très efficace, et un arbre binaire complet est dérivé d'un arbre binaire complet. Pour un arbre binaire avec une profondeur de K et n nœuds, on l'appelle un arbre binaire complet si et seulement si chaque nœud a une correspondance univoque avec les nœuds numérotés de 1 à n dans l'arbre binaire complet avec une profondeur de K Il convient de noter qu'un arbre binaire complet est un type particulier d'arbre binaire complet.

insérez la description de l'image ici

3.2 Propriétés des arbres binaires

1. Si le nombre de couches du nœud racine est spécifié à 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, la profondeur est Le nombre maximum de nœuds dans l'arbre binaire de h est 2^h-1
3. Pour tout arbre binaire, si le nombre de nœuds feuille est n0 avec degré 0, et le nombre de nœuds de branche de degré 2 est n2, alors il y a n0 = n2 + 1
4. Si le nombre de couches du nœud racine est spécifié comme 1, la profondeur d'un arbre binaire complet avec n nœuds, h = log2(n +1)
5. Pour un arbre binaire complet à n nœuds, si selon ce qui précède Tous les nœuds sont numérotés à partir de 0 dans l'ordre du tableau de gauche à droite, alors
pour le nœud avec le 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 ; Si i = 0, alors i est le numéro du nœud racine, pas de nœud parent (2) si 2i + 1 < n
, le numéro enfant gauche : 2i + 1, si 2i + 1 >= n, il n'y a pas d'enfant gauche
(3) si 2i + 2 < n, numéro enfant droit : 2i + 2, si 2i + 2 >= n, il y a pas de bon enfant

4. Stockage séquentiel des arbres binaires

Le stockage de structure séquentielle consiste à utiliser des tableaux pour le stockage.En général, les tableaux ne conviennent que pour représenter des arbres binaires complets, car des arbres binaires non complets gaspilleront de l'espace.
Le stockage séquentiel d'arborescence binaire est physiquement un tableau et logiquement un arbre binaire.
En réalité, seul le tas utilisera des tableaux pour le stockage.

4.1 Le concept de tas

Tous les éléments sont stockés dans un tableau unidimensionnel dans l'ordre d'un arbre binaire complet. Le tas avec le plus grand nœud racine est appelé le plus grand tas ou grand tas racine, et le tas avec le plus petit nœud racine est appelé le plus petit tas ou petit tas de racines.
La valeur d'un nœud dans le tas n'est toujours ni supérieure ni inférieure à la valeur de son nœud parent ;
le tas est toujours un arbre binaire complet.

4.2 Implémentation du tas

4.2.1 Définition du nœud de tas

typedef int HPDataType;

typedef struct Heap
{
    
    
	HPDataType* a;
	int size;
	int capacity;
}HP;

4.2.2 Impression et destruction de tas

Imprimer:

void HeapPrint(HP* php)
{
    
    
	assert(php);
	int i = 0;
	for (i = 0; i < php->size; i++)
	{
    
    
		printf("%d ", php->a[i]);
	}
	printf("\n");
}

détruire:

void HeapDestroy(HP* php)
{
    
    
	assert(php);
	free(php->a);
	php->a = NULL;
	php->capacity = php->size = 0;
}

4.2.3 Insertion de tas

Insérez d'abord un élément de données à la fin du tableau, puis effectuez un algorithme d'ajustement vers le haut jusqu'à ce que le grand tas racine ou le petit tas racine soit satisfait.
Algorithme d'ajustement vers le haut : prenez le petit tas racine comme exemple, commencez à trouver le nœud parent à partir de ce nœud, si le nœud est plus petit que le nœud parent, échangez le nœud avec le nœud parent et continuez à ajuster vers le haut jusqu'à ce que la petite racine est tas satisfait.

insérer:

void HeapPush(HP* php, HPDataType x)
{
    
    
	assert(php);
	if (php->size == php->capacity)
	{
    
    
		int newcapacity = php->capacity == 0 ? 4 : php->capacity * 2;
		HPDataType* tmp = (HPDataType*)malloc(sizeof(HPDataType) * newcapacity);
		if (tmp == NULL)
		{
    
    
			perror("malloc");
			exit(-1);
		}
		php->a = tmp;
		php->capacity = newcapacity;
	}
	php->a[php->size] = x;
	php->size++;
	AdjustUp(php->a, php->size - 1);
}

Algorithme pour l'ajustement vers le haut :

void AdjustUp(HPDataType* a, int child)
{
    
    
	int parent = (child - 1) / 2;
	while (child > 0)
	{
    
    
		if (a[child] < a[parent])
		{
    
    
			Swap(&a[child], &a[parent]);
			child = parent;
			parent = (child - 1) / 2;
		}
		else
		{
    
    
			break;
		}
	}
}

échange:

void Swap(HPDataType* p1, HPDataType* p2)
{
    
    
	HPDataType tmp = *p1;
	*p1 = *p2;
	*p2 = tmp;
}

4.2.4 Suppression de tas

La suppression du tas consiste à supprimer les données en haut du tas, à remplacer les données en haut du tas par les dernières données, puis à supprimer les dernières données du tableau, puis à exécuter l'algorithme d'ajustement vers le bas.
Algorithme d'ajustement vers le bas : prenez le petit tas racine comme exemple, commencez par le nœud pour trouver le nœud enfant vers le bas, si le nœud enfant est plus petit que le nœud, échangez le nœud enfant avec le nœud, puis continuez à ajuster vers le bas, jusqu'à ce que le petit tas de racines est satisfait

supprimer:

void HeapPop(HP* php)
{
    
    
	assert(php);
	assert(php->size > 0);
	Swap(&php->a[0], &php->a[php->size - 1]);
	php->size--;
	AdjustDown(php->a, php->size, 0);
}

Ajustez l'algorithme vers le bas :

void AdjustDown(HPDataType* a, int size, int parent)
{
    
    
	int child = parent * 2 + 1;
	while (child < size)
	{
    
    
		if (child + 1 < size && (a[child + 1] < a[child]))
		{
    
    
			child++;
		}
		if (a[child] < a[parent])
		{
    
    
			Swap(&a[child], &a[parent]);
			parent = child;
			child = parent * 2 + 1;
		}
		else
		{
    
    
			break;
		}
	}
}

4.2.5 Obtenir les données du haut du tas

L'élément supérieur du tas est l'élément dont l'indice est 0

HPDataType HeapTop(HP* php)
{
    
    
	assert(php);
	assert(php->size > 0);
	return php->a[0];
}

4.2.6 Jugement de tas vide

la taille est 0 est vide

bool HeapEmpty(HP* php)
{
    
    
	assert(php);
	return php->size == 0;
}

4.2.7 Nombre de données dans le tas

La taille de size est le nombre de données

int HeapSize(HP* php)
{
    
    
	assert(php);
	return php->size;
}

4.3 Application du tas

4.3.1 Tri par tas

Le tri en tas consiste à utiliser l'idée de tas pour trier. Il se décompose en deux étapes :
1. Construire un tas
Ordre croissant : construire un gros tas
Ordre décroissant : construire un petit tas
2. Utiliser le tas pour supprimer l'idée à trier

void HeapSort(int* arr, int size)
{
    
    
	//排升序建大堆,排降序建小堆
	//向上调整建堆O(N*logN)
	//int i = 0;
	//for (i = 1; i < size; i++)
	//{
    
    
	//	AdjustUp(arr, i);
	//}

	//向下调整建堆O(N)
	int i = 0;
	for (i = (size - 1 - 1) / 2; i >= 0; i--)
	{
    
    
		AdjustDown(arr, size, i);
	}

	int end = size - 1;
	while (end > 0)
	{
    
    
		Swap(&arr[0], &arr[end]);
		AdjustDown(arr, end, 0);
		end--;
	}
}

int main()
{
    
    
	int arr[10] = {
    
     23,45,48,123,12,49,80,15,5,35 };
	HeapSort(arr, 10);
	int i = 0;
	for (i = 0; i < 10; i++)
	{
    
    
		printf("%d ", arr[i]);
	}
	return 0;
}

4.3.2 Problème TOP-K

Problème TOP-K : trouvez les K éléments les plus grands ou les plus petits dans la combinaison de données. Généralement, la quantité de données est relativement importante.
Par exemple : les 10 meilleurs joueurs professionnels, les 500 meilleurs mondiaux, la liste riche, les 100 meilleurs joueurs actifs dans le jeu, etc.
Idées :
1. Utilisez les K premiers éléments de l'ensemble de données pour créer un tas. Trouvez
les k premiers éléments les plus grands, puis construisez un petit tas.
Trouvez les k premiers éléments les plus petits, puis construisez un grand tas
. 2. Utilisez les N restants. - K éléments dans l'ordre Comparer avec l'élément supérieur du tas, s'il n'est pas satisfait, remplacer l'élément supérieur du tas
Après avoir comparé les N - K éléments restants avec l'élément supérieur du tas tour à tour, les K éléments restants dans le tas sont les K premiers éléments les plus petits ou les plus grands recherchés.

void PrintTopK(int* a, int n, int k)
{
    
    
	//1.建堆--用a中前k个元素建堆
	int* kMaxHeap = (int*)malloc(sizeof(int)*k);
	assert(kMaxHeap);
	int i = 0;
	for (i = 0; i < k; i++)
	{
    
    
		kMaxHeap[i] = a[i];
	}
	for (i = (k - 1 - 1) / 2; i >= 0; i--)
	{
    
    
		AdjustDown(kMaxHeap, k, i);
	}

	//2.将剩余n-k个元素依次与堆顶元素交换,不满则则替换
	int j = 0;
	for (j = k; j < n; j++)
	{
    
    
		if (a[j] > kMaxHeap[0])
		{
    
    
			kMaxHeap[0] = a[j];
			AdjustDown(kMaxHeap, k, 0);
		}
	}
	for (i = 0; i < k; i++)
	{
    
    
		printf("%d ", kMaxHeap[i]);
	}
}

void TestTopk()
{
    
    
	int n = 10000;
	int* a = (int*)malloc(sizeof(int) * n);
	srand(time(0));
	for (int i = 0; i < n; i++)
	{
    
    
		a[i] = rand() % 1000000;
	}
	a[5] = 1000000 + 1;
	a[1231] = 1000000 + 2;
	a[531] = 1000000 + 3;
	a[5121] = 1000000 + 4;
	a[115] = 1000000 + 5;
	a[2335] = 1000000 + 6;
	a[9999] = 1000000 + 7;
	a[76] = 1000000 + 8;
	a[423] = 1000000 + 9;
	a[3144] = 1000000 + 10;
	PrintTopK(a, n, 10);
}

int main()
{
    
    
	TestTopk();
	return 0;
}

5. Stockage en chaîne de l'arbre binaire

La structure de stockage liée de l'arbre binaire signifie qu'une liste liée est utilisée pour représenter un arbre binaire, c'est-à-dire qu'un lien est utilisé 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 lien où l'enfant gauche et l'enfant droit du nœud sont localisés.
La structure de la chaîne est ensuite divisée en chaîne à deux fourches et chaîne à trois fourches.

5.1 Définition des nœuds de l'arbre binaire chaîné

typedef int BTDataType;

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

5.2 Création de nœud

BTNode* CreatBTNode(BTDataType x)
{
    
    
	BTNode* node = (BTNode*)malloc(sizeof(BTNode));
	assert(node);
	node->data = x;
	node->left = NULL;
	node->right = NULL;
	return node;
}

La création d'un arbre binaire étant plus compliquée, simulons manuellement un arbre binaire simple pour faciliter la mise en œuvre des opérations ultérieures.

5.3 Simulation pour créer un arbre binaire

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

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

	return node1;
}

5.4 Parcours d'arbre binaire

La traversée de l'arbre binaire comprend : la traversée récursive de la structure de l'ordre de préordre/inordre/postordre/niveau :
1. Traversée de préordre (traversée de préordre, également connue sous le nom de traversée de préordre) - l'opération de visite du nœud racine se produit lors de la traversée de sa gauche et de sa droite sous-arbres Avant.
2. Inorder Traversal (Inorder Traversal) - l'opération de visite du nœud racine se produit en traversant ses sous-arbres gauche et droit (entre).
3. Postorder Traversal —— L'opération de visite du nœud racine se produit après avoir traversé ses sous-arbres gauche et droit.
4. Level Traversal (Level Traversal) - En partant du nœud racine de l'arbre binaire où il se trouve, visitez d'abord le nœud racine du premier niveau, puis visitez les nœuds du deuxième niveau de gauche à droite, puis le troisième level Par analogie, le processus de visite des nœuds de l'arbre couche par couche de haut en bas et de gauche à droite est un parcours d'ordre des couches.

5.4.1 Traversée de la précommande

void PreOrder(BTNode* root)
{
    
    
	if (root == NULL)
	{
    
    
		printf("# ");
		return;
	}

	printf("%d ", root->data);
	PreOrder(root->left);
	PreOrder(root->right);
}

5.4.2 Parcours dans l'ordre

void InOrder(BTNode* root)
{
    
    
	if (root == NULL)
	{
    
    
		printf("# ");
		return;
	}

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

5.4.3 Traversée post-ordre

void PostOrder(BTNode* root)
{
    
    
	if (root == NULL)
	{
    
    
		printf("# ");
		return;
	}

	PostOrder(root->left);
	PostOrder(root->right);
	printf("%d ", root->data);
}

5.4.4 Traversée d'ordre de niveau

La traversée hiérarchique est un peu plus compliquée et nécessite une file d'attente à implémenter. Nous utiliserons ici la file d'attente créée précédemment, et ne la montrerons pas trop. L'idée de base est la suivante : jugez d'abord si le nœud est vide, sinon, entrez dans la file d'attente , puis jugez si l'équipe est vide, si vous n'imprimez pas d'abord l'élément chef d'équipe à ce moment, puis supprimez l'élément chef d'équipe, puis jugez si l'enfant gauche et l'enfant droit de l'élément chef d'équipe imprimé sont vides, et continuez à rejoindre l'équipe s'ils ne sont pas vides. De cette manière, l'itération de boucle est une traversée de séquence de couches.

void LevelOrder(BTNode* root)
{
    
    
	Queue q;
	QueueInit(&q);

	if (root)
	{
    
    
		QueuePush(&q, root);
	}
	while (!QueueEmpty(&q))
	{
    
    
		BTNode* front = QueueFront(&q);
		printf("%d ", front->data);
		QueuePop(&q);
		if (front->left)
		{
    
    
			QueuePush(&q, front->left);
		}
		if (front->right)
		{
    
    
			QueuePush(&q, front->right);
		}
	}
	QueueDestroy(&q);
}

5.5 Opérations telles que le nombre et la hauteur des nœuds d'arbre binaire

5.5.1 Le nombre de nœuds dans un arbre binaire

Parce que le sous-arbre gauche et le sous-arbre droit d'un arbre binaire peuvent être considérés comme des arbres binaires séparés, l'idée de base est de calculer de manière récursive le nombre de nœuds dans le sous-arbre gauche et le sous-arbre droit, et enfin de revenir et d'ajouter 1 au nombre de nœuds dans le numéro de l'arbre binaire.

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

5.5.2 Nombre de nœuds feuilles dans l'arbre binaire

Le nœud dont les sous-arbres gauche et droit sont vides est le nœud feuille. La pensée récursive est également utilisée ici pour diviser l'arbre binaire en sous-arbre gauche et sous-arbre droit. Le sous-arbre gauche peut également être divisé en sous-arbre gauche et sous-arbre droit. Calcul récursif Et renvoie le nombre de nœuds feuilles de l'arbre entier qui peuvent être trouvés.

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);
}

5.5.3 Le nombre de nœuds dans la kième couche de l'arbre binaire

L'idée ici est que trouver le nombre de nœuds dans la kème couche du nœud racine peut être converti en trouver le nombre de nœuds dans la k-1ème couche de l'enfant gauche du nœud + le kème de l'enfant droit du nœud - Le nombre de nœuds dans la couche 1, récursivement, et le nombre de nœuds dans la kième couche du nœud racine seront retournés après la fin.

int BinaryTreeLevelKSize(BTNode* root, int k)
{
    
    
	assert(k >= 1);
	if (root == NULL)
	{
    
    
		return 0;
	}
	if (k == 1)
	{
    
    
		return 1;
	}

	return BinaryTreeLevelKSize(root->left, k - 1) + BinaryTreeLevelKSize(root->right, k - 1);

}

5.5.4 Trouver le nœud avec la valeur x dans l'arbre binaire

L'idée ici est que si le nœud est le nœud requis, retournez le nœud directement, sinon, recherchez récursivement le sous-arbre gauche et le sous-arbre droit du nœud, et retournez s'il est trouvé.

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;
}

5.5.5 Calcul de la profondeur d'un arbre binaire

La profondeur de l'arbre binaire est la profondeur maximale du sous-arbre gauche et du sous-arbre droit de l'arbre binaire plus 1. L'idée ici est de calculer récursivement la profondeur du sous-arbre gauche et la profondeur du sous-arbre droit. Chaque retour est basé sur un certain nœud La profondeur de l'arbre binaire comme racine, et le dernier retour est la profondeur de l'arbre binaire.

int BinaryTreeDepth(BTNode* root)
{
    
    
	if (root == NULL)
	{
    
    
		return 0;
	}

	int LeftDepth = BinaryTreeDepth(root->left);
	int RightDepth = BinaryTreeDepth(root->right);

	return LeftDepth > RightDepth ? LeftDepth + 1 : RightDepth + 1;

}

5.5.6 Déterminer s'il s'agit d'un arbre binaire complet

Les files d'attente sont également utilisées ici. L'idée centrale est d'utiliser les propriétés d'un arbre binaire complet. Les nœuds non vides d'un arbre binaire complet doivent être connectés ensemble. Une fois qu'un nœud vide apparaît, tous les nœuds derrière le nœud vide doivent être vides S'il reste des nœuds non vides, ce n'est pas un arbre binaire complet.

int BinaryTreeComplete(BTNode* root)
{
    
    
	Queue q;
	QueueInit(&q);

	if (root)
	{
    
    
		QueuePush(&q, root);
	}
	while (!QueueEmpty(&q))
	{
    
    
		BTNode* front = QueueFront(&q);
		QueuePop(&q);
		if (front)
		{
    
    
			QueuePush(&q, front->left);
			QueuePush(&q, front->right);
		}
		else
		{
    
    
			break;
		}
	}
	while (!QueueEmpty(&q))
	{
    
    
		BTNode* front = QueueFront(&q);
		QueuePop(&q);
		if (front)
		{
    
    
			QueueDestroy(&q);
			return false;
		}
	}
	QueueDestroy(&q);
	return true;
}

6. Fin

Jusqu'à présent, le concept d'arbre binaire et la mise en œuvre et l'application de la structure de séquence d'arbre binaire et de la structure de chaîne ont été introduits. L'arbre binaire, en tant que points clés et difficiles de la structure de données, nécessite une étude répétée et une compréhension approfondie. C'est un test de la capacité de compréhension et les compétences de base de chacun.Je suis aussi un Pour les débutants, il y a inévitablement de nombreuses erreurs et omissions dans cet article.Cet article est uniquement destiné à votre étude et à votre référence. Si cet article est utile à tout le monde pour apprendre les arbres binaires, le blogueur en est très honoré.
Enfin, je tiens à vous remercier pour votre patiente lecture et votre soutien. Les amis qui pensent que cet article est bien écrit peuvent le suivre et le soutenir trois fois. Si vous avez des questions ou s'il y a des erreurs dans cet article, vous pouvez m'envoyer un message privé ou laissez un message dans la zone de commentaire Discussion, merci encore à tous.

Je suppose que tu aimes

Origine blog.csdn.net/qq_43188955/article/details/130223760
conseillé
Classement