[Estructura de datos] Árbol binario -- estructura de cadena

Las adiciones, eliminaciones y cambios ordinarios del árbol binario no tienen ningún valor.Es mejor usar una tabla lineal simplemente para almacenar datos.

Aprender un árbol binario común es controlar mejor su estructura y sentar las bases para el aprendizaje posterior de árboles binarios de búsqueda más complejos.

Árbol binario equilibrado: árbol AVL, árbol rojo-negro.

El concepto de árbol binario:

1. Árbol vacío

2. No vacío: el nodo raíz, el subárbol izquierdo del nodo raíz y el subárbol derecho del nodo raíz.

El concepto de árbol binario:

1. Árbol vacío

2. No vacío: el nodo raíz, el subárbol izquierdo del nodo raíz y el subárbol derecho del nodo raíz.

Según el concepto, la definición de un árbol binario es recursiva, por lo que las operaciones básicas posteriores se realizan de forma recursiva.

recorrido del árbol binario

Pre-pedido, en orden, recorrido posterior

Simular recorrido

1. Recorrido de pedido anticipado (primer recorrido de raíz): raíz subárbol izquierdo subárbol derecho 

                          1 2 3 4 5 6

2. Recorrido en orden (recorrido de raíz central): subárbol izquierdo, nodo raíz, subárbol derecho

                                 3 2 1 5 4 6

3. Recorrido posterior al orden (recorrido posterior a la raíz): subárbol izquierdo, subárbol derecho, raíz

                         3 2 5 6 4 1

Recorrido de orden de nivel: 1 2 4 3 5 6

Simular el recorrido del árbol binario

Construir una estructura de cadena

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. Recorrido de pedido anticipado

Idea básica: raíz - número de la izquierda - número de la derecha

void PrevOrder(BTNode* tree)
{

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

 

 

2. Recorrido en orden

Subárbol izquierdo - nodo raíz - subárbol derecho

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

 

 

3. Recorrido posterior al pedido

Subárbol izquierdo - Subárbol derecho - Raíz

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

 

4. Encuentra el número de nodos

A. Poligonal + conteo

El primero:

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

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

El segundo:

Defina un contador transversal global o estático defina una variable local estática.

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

Pero esto tiene un error fatal, la cantidad de nodos devueltos por varias llamadas será incorrecta.

Motivo: las variables globales y las variables locales estáticas no se pueden inicializar en 0.

b. Divide y vencerás:

Divida y conquiste el pensamiento: divida un problema complejo en subproblemas más pequeños y subproblemas en subproblemas más pequeños,...

Método: Calcule el número de nodos en el subárbol izquierdo y agregue el número de nodos derechos.

Idea: recursividad

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

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

5. Encuentra el número de nodos hoja

Divide y vencerás: después de calcular el árbol de la izquierda, luego calcula el árbol de la derecha.

Código:

//求叶子结点的个数
//分治
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. Encuentra el número de nodos en la k-ésima capa

Idea: use la capa k-1 como nodo principal y 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. Pregunte la geometría de la altura del árbol?

Divide y vencerás: Calcula la altura del subárbol izquierdo y la altura del subárbol derecho para comparar, el más grande +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. Nodo de búsqueda de árbol binario con valor x

  1. Ideas: 1) Considere la idea de dividir y vencer, primero recurra al árbol de la izquierda y luego recurra al árbol de la derecha.
  2. 2) Devuelva la dirección de nodo correspondiente cuando la encuentre y devuelva NULL cuando la raíz de loto esté frita.
  3. 3) Determinar si el valor devuelto es NULL o el nodo correspondiente.
//二叉树查找为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. Travesía de orden de nivel

Idea: De acuerdo con las características del recorrido de orden de capas, las colas se pueden usar para resolver el problema.

Cuando se sacan los datos de la capa anterior, se ponen en cola los datos izquierdo y derecho correspondientes a la capa anterior.

Nota: Dado que C no tiene una biblioteca, debemos sacar el archivo de la cola que implementamos nosotros mismos antes y agregarlo a la posición correspondiente del proyecto.

Recuerde agregar en Queue.h

Al ingresar a la cola, se ingresa toda una estructura del árbol binario, no solo datos

Código:

//层序遍历
/*根据层序遍历的特性,考虑将数据入队列,可以根据队列的性质,实现层序遍历
当取出上一层数据的时候,将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. Determinar si un árbol binario es un árbol binario completo

Idea: El juicio todavía se basa en la cola, complementado con el recorrido del orden de las capas.

Al ingresar a la cola, NULL también se ingresa en la cola. Cuando se encuentra NULL al salir de la cola, los datos restantes de la cola se eliminan de la cola. Cuando solo hay NULL en los datos restantes, significa que es un binario completo árbol, de lo contrario no lo es.

Dibujo:

Este árbol no es un árbol binario completo.

Código:

//判断二叉树是否为完全二叉树
/*以队列为基础,层序遍历为辅助,当遇到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. Destruye el árbol

Idea: dividir y vencer recursivamente, destruir subárbol izquierdo - subárbol derecho - raíz

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

Si hay algún error, por favor indíquelo ~

Supongo que te gusta

Origin blog.csdn.net/weixin_61932507/article/details/124227575
Recomendado
Clasificación