[Explicação detalhada do tutorial de árvore binária e implementação da árvore binária em linguagem C/C++]

árvore binária

Uma árvore binária é uma estrutura de dados semelhante a uma árvore especial na qual cada nó tem no máximo dois nós filhos. Um nó é chamado de nó pai e dois nós filhos são chamados de nó filho esquerdo e nó filho direito, respectivamente.

1. O que é uma árvore binária

Uma árvore binária é uma estrutura de dados semelhante a uma árvore especial na qual cada nó tem no máximo dois nós filhos. Cada nó contém um elemento de dados e ponteiros para seus nós filhos esquerdo e direito. O valor do nó filho esquerdo é menor ou igual ao valor do nó pai e o valor do nó filho direito é maior que o valor do nó pai. Essa propriedade torna as árvores binárias muito eficientes para operações de pesquisa, inserção e exclusão.

     A
    / \
   B   C
  / \   \
 D   E   F

As árvores binárias são frequentemente usadas para modelar dados com uma estrutura hierárquica. Algumas de suas aplicações comuns incluem algoritmos de busca (como árvores de busca binária), árvores de expressão, árvores de codificação Huffman, etc. Em uma árvore binária, podemos usar diferentes métodos de travessia para visitar os nós, incluindo travessia de pré-ordem, travessia em ordem e travessia de pós-ordem .

Em segundo lugar, o método de travessia da árvore binária

1. Travessia de pré-encomenda

A partir do nó raiz, visite primeiro o nó raiz e, em seguida, execute recursivamente a travessia de pré-ordem nas subárvores esquerda e direita. Esta travessia pode ser usada para replicar toda a estrutura da árvore.

Suponha que temos a seguinte árvore binária:

     1
    / \
   2   3
  / \   \
 4   5   6

Primeiro, visitamos o nó raiz 1 e depois percorremos a subárvore esquerda. O nó raiz da subárvore esquerda é 2, continuamos a visitá-lo e depois percorremos sua subárvore esquerda. O nó raiz da subárvore esquerda é 4, continuamos a visitá-lo e descobrimos que não há subárvores esquerda e direita, então retornamos ao nó 2 e então percorremos sua subárvore direita.

O nó raiz da subárvore direita é 5, continuamos a visitá-lo e descobrimos que não há subárvores esquerda e direita, então retornamos ao nó 2, retornamos ao nó raiz 1 e então percorremos a subárvore direita de o nó raiz.

O nó raiz da subárvore direita é 3, continuamos a visitá-lo e depois percorremos sua subárvore esquerda. A subárvore esquerda está vazia, então retornamos ao nó 3 e então percorremos sua subárvore direita.

O nó raiz da subárvore direita é 6, continuamos a visitá-lo e descobrimos que não há subárvores esquerda e direita, então retornamos ao nó 3 e, em seguida, retornamos ao nó raiz 1 e a travessia termina.

最终的先序遍历结果是:1 2 4 5 3 6

O seguinte é um diagrama das etapas específicas da travessia de pré-ordem:

     1
    / \
   2   3
  / \   \
 4   5   6
  1. visite o nó raiz 1
  2. percorrer a subárvore esquerda
    • visite o nó raiz 2
    • percorrer a subárvore esquerda
      • visite o nó raiz 4
      • Se a subárvore esquerda estiver vazia, retorne
    • percorrer a subárvore direita
      • visite o nó raiz 5
      • Se a subárvore esquerda estiver vazia, retorne
  3. percorrer a subárvore direita
    • visite o nó raiz 3
    • Percorrer a subárvore esquerda (vazio, retornar)
    • percorrer a subárvore direita
      • visite o nó raiz 6
      • Se a subárvore esquerda estiver vazia, retorne
最终的先序遍历结果是:1 2 4 5 3 6

2. Travessia em ordem

A travessia em ordem é uma maneira de percorrer uma árvore binária. A ordem de travessia é: começando no nó raiz, primeiro execute recursivamente a travessia em ordem na subárvore esquerda, depois visite o nó raiz e, finalmente, execute recursivamente a travessia em ordem na subárvore direita. Para árvores de busca binária, a travessia em ordem pode atingir uma saída ascendente.

Aqui está um exemplo de uma árvore binária:

      1
    /   \
   2     3
  / \   / \
 4   5 6   7
中序遍历的结果为:4, 2, 5, 1, 6, 3, 7

O processo de travessia em ordem pode ser implementado usando recursão ou pilha. Os dois métodos são descritos em detalhes a seguir.

(1) Método recursivo

A recursão é a maneira mais fácil de fazer isso. As etapas específicas são as seguintes:

  1. Retorna se o nó atual estiver vazio.
  2. Atravesse recursivamente a subárvore esquerda do nó atual.
  3. Visite o nó atual.
  4. Atravesse recursivamente a subárvore direita do nó atual.

A seguir está um exemplo de código para passagem inorder usando recursão:

#include <stdio.h>
#include <stdlib.h>

struct Node {
    
    
    int val;
    struct Node* left;
    struct Node* right;
};

void inorderTraversal(struct Node* root) {
    
    
    if (root == NULL) {
    
    
        return;
    }
    inorderTraversal(root->left);
    printf("%d ", root->val);
    inorderTraversal(root->right);
}

(2) método de pilha

O método stack usa uma pilha para auxiliar no processo de passagem. As etapas específicas são as seguintes:

  1. Inicialize uma pilha vazia e um ponteiro para o nó raiz.
  2. Quando a pilha não estiver vazia ou o ponteiro não estiver vazio, faça o seguinte:
    • Se o ponteiro não for nulo, coloque o ponteiro na pilha e aponte o ponteiro para sua subárvore esquerda.
    • Se o ponteiro estiver vazio, remova o elemento superior da pilha e acesse-o e, em seguida, aponte o ponteiro para sua subárvore direita.
  3. Quando a pilha estiver vazia e o ponteiro estiver vazio, a travessia termina.

A seguir está um exemplo de código para passagem inorder usando o método stack:

#include <stdio.h>
#include <stdlib.h>

struct Node {
    
    
    int val;
    struct Node* left;
    struct Node* right;
};

void inorderTraversal(struct Node* root) {
    
    
    struct Node* stack[1000];
    int top = -1;
    struct Node* curr = root;
    while (top != -1 || curr != NULL) {
    
    
        if (curr != NULL) {
    
    
            stack[++top] = curr;
            curr = curr->left;
        } else {
    
    
            curr = stack[top--];
            printf("%d ", curr->val);
            curr = curr->right;
        }
    }
}

Seja o método recursivo ou o método de pilha, sua complexidade de tempo é O(n), onde n é o número de nós na árvore binária.

3. Traversal pós-ordem

A travessia de pós-ordem (Postorder Traversal) é um método de travessia de árvore binária.Sua ordem de travessia é percorrer primeiro a subárvore esquerda, depois percorrer a subárvore direita e finalmente visitar o nó raiz. Esse método de travessia é frequentemente usado para recuperação de memória livre e chamadas de destruidor .

Aqui está um exemplo de árvore binária:

      1
     / \
    2   3
   / \   \
  4   5   6
  

De acordo com a ordem de travessia de pós-ordem, a subárvore esquerda deve ser percorrida primeiro, depois a subárvore direita e, finalmente, o nó raiz deve ser visitado. Portanto, o resultado da travessia pós-ordem da árvore binária é:

4 -> 5 -> 2 -> 6 -> 3 -> 1

Em seguida, usamos a linguagem C para implementar a travessia pós-ordem da árvore binária.

Primeiro, precisamos definir a estrutura da árvore binária, incluindo o valor do nó e ponteiros para os nós filho esquerdo e direito.

#include <stdio.h>
#include <stdlib.h>

// 定义二叉树的结构
struct TreeNode {
    
    
    int val;
    struct TreeNode* left;
    struct TreeNode* right;
};

Então, precisamos de uma função para criar um novo nó de árvore binária.

// 创建一个新的二叉树节点
struct TreeNode* createNode(int val) {
    
    
    struct TreeNode* newNode = (struct TreeNode*)malloc(sizeof(struct TreeNode));
    newNode->val = val;
    newNode->left = NULL;
    newNode->right = NULL;
    return newNode;
}

Em seguida, vamos implementar a função de travessia pós-ordem da árvore binária.

// 后序遍历二叉树
void postorderTraversal(struct TreeNode* root) {
    
    
    if (root == NULL) {
    
    
        return;
    }
    postorderTraversal(root->left);   // 遍历左子树
    postorderTraversal(root->right);  // 遍历右子树
    printf("%d ", root->val);         // 访问根节点
}

Na função de travessia de pós-ordem, primeiro julgamos se o nó atual está vazio e o retornamos se estiver vazio. Em seguida, percorra recursivamente a subárvore esquerda e a subárvore direita sucessivamente e, finalmente, visite o nó raiz.

Por fim, podemos criar um exemplo de árvore binária na função principal e chamar a função de travessia de pós-ordem para percorrê-la.

int main() {
    
    
    // 创建示例二叉树
    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->right = createNode(6);

    // 后序遍历二叉树
    printf("后序遍历结果:");
    postorderTraversal(root);
    printf("\n");

    return 0;
}

Executando o código acima, a saída é:

后序遍历结果:4 5 2 6 3 1

3. Características da árvore binária

Além do método de passagem, a árvore binária possui algumas outras características:

  • Árvore binária completa: Em uma árvore binária completa, exceto para a última camada, os nós de cada camada são preenchidos e os nós da última camada são preenchidos da esquerda para a direita. Essa estrutura de árvore pode ser armazenada de forma eficiente em uma matriz.
  • Árvore binária completa: Uma árvore binária completa é uma árvore binária na qual todos os nós têm 0 ou 2 nós filhos. Em uma árvore binária completa, todos os nós de folha estão no mesmo nível.
  • Árvore Binária Balanceada: Em uma árvore binária balanceada, a diferença de altura entre a subárvore esquerda e a subárvore direita de qualquer nó é no máximo 1. Isso mantém a árvore balanceada e melhora a eficiência das operações de pesquisa, inserção e exclusão.
  • Árvore Binária de Pesquisa: Uma árvore binária de pesquisa (BST) é uma árvore binária com a propriedade de que, para qualquer nó, todos os nós em sua subárvore esquerda têm valores menores que ele e todos os nós em sua subárvore direita têm valores menores que maior do que é. Essa propriedade torna muito eficientes as operações de pesquisa, inserção e exclusão em uma árvore de pesquisa binária.

Em suma, uma árvore binária é uma importante estrutura de dados com uma ampla gama de aplicações. Seus nós podem ter até dois nós filhos, e diferentes métodos de travessia podem ser usados ​​para acessar e processar os nós.

Quarto, implementação de árvore binária

1. Defina a estrutura

Primeiro, definimos uma estrutura representando os nós da árvore binária. Cada nó consiste em uma parte de dados e duas partes de ponteiro, apontando para o nó filho esquerdo e o nó filho direito, respectivamente.

typedef struct Node {
    
    
    int data;
    struct Node *left;
    struct Node *right;
} Node;

2. Crie um novo nó

Em seguida, implementamos uma função para criar um novo nócreateNode , que é responsável por alocar memória e inicializar a parte de dados do nó. Se a alocação de memória falhar, uma mensagem de erro é impressa e o programa é encerrado.

Node *createNode(int data) {
    
    
    Node *newNode = (Node *)malloc(sizeof(Node));
    if (newNode == NULL) {
    
    
        printf("内存分配失败!\n");
        exit(1);
    }
    newNode->data = data;
    newNode->left = NULL;
    newNode->right = NULL;
    return newNode;
}

3. Insira um nó

Em seguida, implementamos uma função para inserir nósinsertNode . Esta função insere o novo nó na posição apropriada de acordo com o valor dos dados passados. Se a árvore estiver vazia, a função cria um novo nó e o torna o nó raiz. Caso contrário, insira recursivamente o nó na subárvore esquerda ou direita, dependendo do tamanho do valor dos dados.

Node *insertNode(Node *root, int data) {
    
    
    if (root == NULL) {
    
    
        return createNode(data);
    } else if (data < root->data) {
    
    
        root->left = insertNode(root->left, data);
    } else if (data > root->data) {
    
    
        root->right = insertNode(root->right, data);
    }
    return root;
}

Depois disso, implementamos três métodos de travessia: travessia em pré-ordem, travessia em ordem e travessia em pós-ordem. Esses métodos de passagem são para visitar os nós na árvore binária em uma ordem específica.

4. Travessia de pré-encomenda

A função de travessia de pré-ordempreOrder percorre a árvore na ordem do nó raiz, subárvore esquerda e subárvore direita e imprime os valores de dados dos nós.

void preOrder(Node *root) {
    
    
    if (root != NULL) {
    
    
        printf("%d ", root->data);
        preOrder(root->left);
        preOrder(root->right);
    }
}

5. Travessia em ordem

A função de travessia inorderinOrder percorre a árvore na ordem da subárvore esquerda, nó raiz e subárvore direita e imprime o valor dos dados do nó.

void inOrder(Node *root) {
    
    
    if (root != NULL) {
    
    
        inOrder(root->left);
        printf("%d ", root->data);
        inOrder(root->right);
    }
}

6. Traversal pós-ordem

A função de passagem de pós-ordempostOrder percorre a árvore na ordem da subárvore esquerda, subárvore direita e nó raiz e imprime o valor dos dados do nó.

void postOrder(Node *root) {
    
    
    if (root != NULL) {
    
    
        postOrder(root->left);
        postOrder(root->right);
        printf("%d ", root->data);
    }
}

7. função principal

Por fim, namain função, criamos o nó raiz de uma árvore vazia e insertNodeinserimos alguns dados por meio da função. Em seguida, chame as três funções de passagem, respectivamente, e imprima os resultados da passagem.

int main() {
    
    
    Node *root = NULL;
    root = insertNode(root, 50);
    insertNode(root, 30);
    insertNode(root, 20);
    insertNode(root, 40);
    insertNode(root, 70);
    insertNode(root, 60);
    insertNode(root, 80);

    printf("先序遍历结果:");
    preOrder(root);

    printf("\n中序遍历结果:");
    inOrder(root);

    printf("\n后序遍历结果:");
    postOrder(root);

    return 0;
}

Este é o código detalhado de uma implementação de árvore binária simples, espero que isso possa ajudá-lo a entender melhor como uma árvore binária funciona e como ela é implementada.

Cinco, código completo

#include <stdio.h>
#include <stdlib.h>

// 节点结构
typedef struct Node {
    
    
    int data;
    struct Node *left;
    struct Node *right;
} Node;

// 创建一个新节点
Node *createNode(int data) {
    
    
    Node *newNode = (Node *)malloc(sizeof(Node));
    if (newNode == NULL) {
    
    
        printf("内存分配失败!\n");
        exit(1);
    }
    newNode->data = data;
    newNode->left = NULL;
    newNode->right = NULL;
    return newNode;
}

// 插入节点
Node *insertNode(Node *root, int data) {
    
    
    if (root == NULL) {
    
    
        return createNode(data);
    } else if (data < root->data) {
    
    
        root->left = insertNode(root->left, data);
    } else if (data > root->data) {
    
    
        root->right = insertNode(root->right, data);
    }
    return root;
}

// 先序遍历
void preOrder(Node *root) {
    
    
    if (root != NULL) {
    
    
        printf("%d ", root->data);
        preOrder(root->left);
        preOrder(root->right);
    }
}

// 中序遍历
void inOrder(Node *root) {
    
    
    if (root != NULL) {
    
    
        inOrder(root->left);
        printf("%d ", root->data);
        inOrder(root->right);
    }
}

// 后序遍历
void postOrder(Node *root) {
    
    
    if (root != NULL) {
    
    
        postOrder(root->left);
        postOrder(root->right);
        printf("%d ", root->data);
    }
}

int main() {
    
    
    Node *root = NULL;
    root = insertNode(root, 50);
    insertNode(root, 30);
    insertNode(root, 20);
    insertNode(root, 40);
    insertNode(root, 70);
    insertNode(root, 60);
    insertNode(root, 80);

    printf("先序遍历结果:");
    preOrder(root);

    printf("\n中序遍历结果:");
    inOrder(root);

    printf("\n后序遍历结果:");
    postOrder(root);

    return 0;
}

Acho que você gosta

Origin blog.csdn.net/qq_43884946/article/details/131475790
Recomendado
Clasificación