[Estrutura de dados e análise de algoritmo] Tree Walking explora a implementação e otimização de algoritmos de travessia de pré-ordem, ordem, pós-ordem e largura

Prefácio

  A árvore binária é uma das estruturas de dados mais básicas e tem aplicações muito importantes na ciência da computação. A travessia da árvore binária refere-se à travessia de todos os nós em uma árvore binária em uma determinada ordem, que é uma das operações mais básicas de uma árvore binária.

Como percorrer uma árvore binária

Construa uma árvore binária

  A função createNodecria um novo nó da árvore binária e retorna um ponteiro para o nó. Esta função recebe um parâmetro do tipo inteiro val, que é utilizado para representar o valor do novo nó, ambos os ponteiros do nó NULL.

// 创建新节点的函数
struct TreeNode *createNode(int val) {
    
    
	struct TreeNode *node = (struct TreeNode*) malloc(sizeof(struct TreeNode));
	node->val = val;
	node->left = NULL;
	node->right = NULL;
	return node;
}

  A buildTreefunção é construir uma árvore binária com estrutura fixa e retornar o ponteiro do nó raiz. Dentro da função, primeiro crie um nó raiz com valor 1, depois createNodecrie todos os nós da árvore binária por meio da função e defina seus valores e ponteiros de subárvore correspondentes.

// 构建一棵二叉树
struct TreeNode *buildTree() 
{
    
    
	struct TreeNode *root = createNode(1);
	root->left = createNode(2);
	root->right = createNode(3);
	root->left->left = createNode(4);
	root->left->right = createNode(5);
	root->right->left = createNode(6);
	root->right->right = createNode(7);
	root->left->left->left = createNode(8);
	root->left->left->right = createNode(9);
	return root;
}

  Depois de passar buildTreea função, obtemos a árvore binária:
Insira a descrição da imagem aqui

Percorrer recursivamente uma árvore binária

  As etapas específicas da travessia recursiva são as seguintes:

  • Primeiro determine se o nó atual está vazio e, se estiver vazio, retorne-o diretamente.
  • Percorra recursivamente a subárvore esquerda do nó atual, ou seja, chame inOrder(node->left). 【1】
  • Percorra o nó atual , ou seja, produza o valor node->val do nó node. 【2】
  • Percorra recursivamente a subárvore direita do nó atual, ou seja, chame inOrder(node->right). 【3】

  Então, o código detalhado de travessia em ordem é o seguinte:

// 递归中序遍历
void inOrder(struct TreeNode*node)
{
    
    
	// 判断节点是否为空
	if (node == NULL) return;
	// 先访问左孩子
	inOrder(node->left);
	// 访问自己
	printf(" %d ",node->val);
	// 访问右孩子
	inOrder(node->right);
}

  Nota: Mover a etapa da ideia [2] para antes de [1] é uma passagem de pré-pedido. Depois de mover a etapa da ideia [2] para [3], é a travessia da pré-ordem.

Travessia não recursiva de uma árvore binária

  A inOrder2função é realizar a travessia em ordem não recursiva da árvore binária. Aqui, uma pilha é usada para simular a operação recursiva de travessia em ordem. A ideia da função é:

  • A função aceita dois parâmetros root(o nó raiz da árvore binária) e nodeCount(o número total de nós), onde nodeCounté usado para inicializar o tamanho da pilha de travessia.

  • Primeiro, use malloca função para alocar uma nodeCountmatriz de ponteiros de tamanho datapara armazenar ponteiros de nós (simulando uma estrutura de pilha).

  • Em segundo lugar, defina um ponteiro para o topo da pilha dataLene inicialize-o com 0, e inicialize um ponteiro ppara armazenar o ponteiro do nó atual.

  • Depois disso, entre no loop de travessia. Determine se o nó atual pestá vazio. Caso contrário, coloque-o na pilha e patualize seu nó filho esquerdo. Caso contrário, retire um nó da pilha p, produza seu valor e patualize seu nó filho direito. Faça um loop até que a pilha esteja vazia e o nó atual pseja NULL.

  • Finalmente, o espaço de pilha alocado dinamicamente é liberado data.

// 非递归中序遍历
void inOrder2(struct TreeNode*root,int nodeCount)
{
    
    
	// 初始化顺序栈
	struct TreeNode* *data = (struct TreeNode**)malloc(sizeof(struct TreeNode*)*nodeCount);
	// 栈顶指针
	char dataLen = 0;
	struct TreeNode*p = root;

	// 遍历
	while (p || dataLen)
	{
    
    
		// 节点不为空
		if (p)
		{
    
    
			// 先入栈再访问下一个左孩子
			data[dataLen++] = p;
			p = p->left;
		}
		else
		{
    
    
			// 到达叶子节点后 应该先访问叶子节点再回溯到父节点最后访问兄弟
			p = data[--dataLen];
			printf(" %d ",p->val);
			p = p->right;
		}
	}

	// 注销栈空间
	free(data);
}

  Como a ideia desta função é usar a pilha para simular operações recursivas, ela economiza mais memória que o método recursivo e pode controlar melhor a ordem de execução da função.

Passagem de nível

  A função levelOrderé realizar o percurso hierárquico da árvore binária, ou seja, percorrer os nós na ordem da esquerda para a direita em cada camada e gerar o valor do nó. A função aceita dois parâmetros: o nó raiz da árvore binária roote o número total de nós nodeCount. Dentre eles, nodeCountutilizado para inicializar o tamanho da fila sequencial.

  • Dentro da função, primeiro use malloca função para alocar dinamicamente uma nodeCountmatriz de ponteiros de tamanho queuepara armazenar ponteiros de nós (simulando uma estrutura de fila). A função também define um ponteiro inicial fronte um ponteiro final da fila reare pinicializa os ponteiros para o nó raiz root.

  • Primeiro, adicione o nó raiz à fila, que é o ponteiro p.

  • Em seguida, determine se o ponteiro final da fila está na frente do ponteiro principal (a fila está vazia). Se a condição for verdadeira, ele entra no loop de travessia, retira um nó do topo da fila pe determina se o nó atual pestá vazio.

  • Dentro do loop, o valor do nó é a primeira saída p, ou seja, o nó acessado. Posteriormente, se po filho esquerdo do nó existir, seu filho esquerdo será enfileirado. Se po nó filho certo do nó existir, seu nó filho certo será enfileirado.

  • Finalmente, prossiga para o próximo ciclo e repita as etapas acima.

// 层次遍历二叉树
void levelOrder(struct TreeNode*root,int nodeCount)
{
    
    
	// 定义一个顺序队列用于辅助层序遍历
	struct TreeNode* *queue = (struct TreeNode**)malloc(sizeof(struct TreeNode*)*nodeCount);
	//  队头       队尾
	int front = 0,rear = 0;
	struct TreeNode*p = root;
	
	// 将根节点加入队列
	queue[rear++] = p;
	// 遍历
	while ((rear != 0 && rear > front))
	{
    
    
		// [1] 出队列 
		p = queue[front++];
		// [2] 访问节点
		if (p)
			printf(" %d ",p->val);
		// [3] 将左节点入队列
		if (p->left)
			queue[rear++] = p->left;
		// [4] 将右节点入队列
		if (p->right)
			queue[rear++] = p->right;
	}
}

  Nota: O design do algoritmo fornecido aqui é um percurso hierárquico de cima para baixo, da esquerda para a direita. Se você precisar alterá-lo para um percurso hierárquico de cima para baixo, da direita para a esquerda, então você só precisa alterar o loop while. trocar as etapas [3] e [4].
  Esta função usa o recurso primeiro a entrar, primeiro a sair da fila para percorrer a árvore binária em ordem hierárquica. É relativamente simples e fácil de entender e é aplicável a qualquer tipo de árvore binária.

Exemplo de resultados de árvore binária

  Resultados teóricos:
Insira a descrição da imagem aqui

  Resultados da execução do código:
Insira a descrição da imagem aqui

Resumir

  Este artigo apresenta quatro métodos de travessia de árvores binárias: travessia de pré-ordem, travessia em ordem, travessia de pós-ordem e travessia hierárquica. Entre eles, a travessia pré-ordem, a travessia em ordem e a travessia pós-ordem são coletivamente chamadas de travessia em profundidade, enquanto a travessia hierárquica é a travessia em largura.

  Tanto a travessia em profundidade quanto a travessia em largura têm suas próprias características e são frequentemente usadas para resolver problemas diferentes. O percurso em profundidade é mais adequado para situações em que toda a árvore precisa ser percorrida para obter informações globais, como resolver a profundidade da árvore, problemas de caminho e o maior diâmetro dos nós. A travessia em largura é mais adequada para encontrar nós alvo entre nós na mesma camada da árvore, como percorrer uma árvore binária por camada, resolver a profundidade mínima de uma árvore binária, etc.

Acho que você gosta

Origin blog.csdn.net/qq_53960242/article/details/131152056
Recomendado
Clasificación