[Detaillierte Erläuterung des Binärbaum-Tutorials und der Implementierung des Binärbaums in C-Sprache/C++]

Binärbaum

Ein Binärbaum ist eine spezielle baumartige Datenstruktur, in der jeder Knoten höchstens zwei untergeordnete Knoten hat. Ein Knoten wird als übergeordneter Knoten bezeichnet, und zwei untergeordnete Knoten werden als linker untergeordneter Knoten bzw. rechter untergeordneter Knoten bezeichnet.

1. Was ist ein Binärbaum?

Ein Binärbaum ist eine spezielle baumartige Datenstruktur, in der jeder Knoten höchstens zwei untergeordnete Knoten hat. Jeder Knoten enthält ein Datenelement und Zeiger auf seine linken und rechten untergeordneten Knoten. Der Wert des linken untergeordneten Knotens ist kleiner oder gleich dem Wert des übergeordneten Knotens, und der Wert des rechten untergeordneten Knotens ist größer als der Wert des übergeordneten Knotens. Diese Eigenschaft macht Binärbäume für Such-, Einfüge- und Löschvorgänge sehr effizient.

     A
    / \
   B   C
  / \   \
 D   E   F

Binärbäume werden häufig zur Modellierung von Daten mit einer hierarchischen Struktur verwendet. Zu seinen häufigen Anwendungen gehören Suchalgorithmen (z. B. binäre Suchbäume), Ausdrucksbäume, Huffman-Codierungsbäume usw. In einem Binärbaum können wir verschiedene Traversalmethoden verwenden, um Knoten zu besuchen, einschließlich Pre-Order-Traversal, In-Order-Traversal und Post-Order-Traversal .

Zweitens die Binärbaum-Traversal-Methode

1. Durchquerung vorbestellen

Besuchen Sie ausgehend vom Wurzelknoten zuerst den Wurzelknoten und führen Sie dann rekursiv eine Vorbestellungsdurchquerung für den linken und rechten Teilbaum durch. Diese Durchquerung kann verwendet werden, um die gesamte Baumstruktur zu replizieren.

Angenommen, wir haben den folgenden Binärbaum:

     1
    / \
   2   3
  / \   \
 4   5   6

Zuerst besuchen wir den Wurzelknoten 1 und durchqueren dann den linken Teilbaum. Der Wurzelknoten des linken Teilbaums ist 2. Wir besuchen ihn weiterhin und durchlaufen dann seinen linken Teilbaum. Der Wurzelknoten des linken Teilbaums ist 4. Wir besuchen ihn weiterhin und stellen dann fest, dass er keinen linken und rechten Teilbaum hat. Daher kehren wir zu Knoten 2 zurück und durchlaufen dann seinen rechten Teilbaum.

Der Wurzelknoten des rechten Teilbaums ist 5. Wir besuchen ihn weiterhin und stellen dann fest, dass er keinen linken und rechten Teilbaum hat. Daher kehren wir zu Knoten 2 zurück, kehren dann zum Wurzelknoten 1 zurück und durchqueren dann den rechten Teilbaum von der Wurzelknoten.

Der Wurzelknoten des rechten Teilbaums ist 3. Wir besuchen ihn weiterhin und durchlaufen dann seinen linken Teilbaum. Der linke Teilbaum ist leer, daher kehren wir zu Knoten 3 zurück und durchlaufen dann seinen rechten Teilbaum.

Der Wurzelknoten des rechten Teilbaums ist 6. Wir besuchen ihn weiterhin und stellen dann fest, dass er keinen linken und rechten Teilbaum hat. Daher kehren wir zu Knoten 3 und dann zum Wurzelknoten 1 zurück und die Durchquerung endet.

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

Das Folgende ist ein Diagramm der spezifischen Schritte der Vorbestellungsdurchquerung:

     1
    / \
   2   3
  / \   \
 4   5   6
  1. Besuchen Sie Wurzelknoten 1
  2. Durchqueren Sie den linken Teilbaum
    • Besuchen Sie Wurzelknoten 2
    • Durchqueren Sie den linken Teilbaum
      • Besuchen Sie Wurzelknoten 4
      • Wenn der linke Teilbaum leer ist, kehren Sie zurück
    • Durchqueren Sie den rechten Teilbaum
      • Besuchen Sie Wurzelknoten 5
      • Wenn der linke Teilbaum leer ist, kehren Sie zurück
  3. Durchqueren Sie den rechten Teilbaum
    • Besuchen Sie Wurzelknoten 3
    • Durchqueren Sie den linken Teilbaum (leer, zurück)
    • Durchqueren Sie den rechten Teilbaum
      • Besuchen Sie Wurzelknoten 6
      • Wenn der linke Teilbaum leer ist, kehren Sie zurück
最终的先序遍历结果是:1 2 4 5 3 6

2. Inorder-Durchquerung

Inorder-Traversal ist eine Möglichkeit, einen Binärbaum zu durchqueren. Die Traversierungsreihenfolge ist: Beginnend mit dem Wurzelknoten führen Sie zunächst rekursiv eine Inorder-Traversierung am linken Teilbaum durch, besuchen dann den Wurzelknoten und führen schließlich rekursiv eine Inorder-Traversierung am rechten Teilbaum durch. Bei binären Suchbäumen kann durch Durchlaufen in der Reihenfolge eine aufsteigende Ausgabe erzielt werden.

Hier ist ein Beispiel für einen Binärbaum:

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

Der Prozess der Inorder-Traversierung kann mithilfe von Rekursion oder Stapel implementiert werden. Die beiden Methoden werden im Folgenden ausführlich beschrieben.

(1) Rekursive Methode

Rekursion ist der einfachste Weg, dies zu tun. Konkrete Schritte sind wie folgt:

  1. Gibt zurück, wenn der aktuelle Knoten leer ist.
  2. Durchlaufen Sie rekursiv den linken Teilbaum des aktuellen Knotens.
  3. Besuchen Sie den aktuellen Knoten.
  4. Durchlaufen Sie rekursiv den rechten Teilbaum des aktuellen Knotens.

Das Folgende ist ein Beispielcode für die Inorder-Traversierung mithilfe der Rekursion:

#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) Stapelmethode

Die Stapelmethode verwendet einen Stapel, um den Durchlaufprozess zu unterstützen. Konkrete Schritte sind wie folgt:

  1. Initialisieren Sie einen leeren Stapel und einen Zeiger auf den Wurzelknoten.
  2. Wenn der Stapel nicht leer ist oder der Zeiger nicht leer ist, gehen Sie wie folgt vor:
    • Wenn der Zeiger nicht null ist, schieben Sie den Zeiger auf den Stapel und zeigen Sie mit dem Zeiger auf seinen linken Teilbaum.
    • Wenn der Zeiger leer ist, öffnen Sie das oberste Element des Stapels, greifen Sie darauf zu und zeigen Sie dann mit dem Zeiger auf seinen rechten Unterbaum.
  3. Wenn der Stapel leer ist und der Zeiger leer ist, endet die Durchquerung.

Das Folgende ist ein Beispielcode für die Inorder-Traversierung mit der Stack-Methode:

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

Unabhängig davon, ob es sich um die rekursive Methode oder die Stapelmethode handelt, beträgt ihre zeitliche Komplexität O(n), wobei n die Anzahl der Knoten im Binärbaum ist.

3. Durchquerung nach der Bestellung

Postorder-Traversal (Postorder-Traversal) ist eine Binärbaum-Traversalmethode. Die Traversierungsreihenfolge besteht darin, zuerst den linken Teilbaum zu durchlaufen, dann den rechten Teilbaum zu durchlaufen und schließlich den Wurzelknoten zu besuchen. Diese Traversal-Methode wird häufig zur Wiederherstellung des freien Speichers und für Destruktoraufrufe verwendet .

Hier ist ein Beispiel für einen Binärbaum:

      1
     / \
    2   3
   / \   \
  4   5   6
  

Gemäß der Reihenfolge der Durchquerung nach der Reihenfolge sollte zuerst der linke Teilbaum, dann der rechte Teilbaum und schließlich der Wurzelknoten durchlaufen werden. Das Post-Order-Traversal-Ergebnis des Binärbaums lautet also:

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

Als nächstes verwenden wir die C-Sprache, um die Post-Order-Traversierung des Binärbaums zu implementieren.

Zuerst müssen wir die Struktur des Binärbaums definieren, einschließlich des Knotenwerts und der Zeiger auf den linken und rechten untergeordneten Knoten.

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

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

Dann benötigen wir eine Funktion zum Erstellen eines neuen Binärbaumknotens.

// 创建一个新的二叉树节点
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;
}

Als nächstes implementieren wir die Post-Order-Traversal-Funktion des Binärbaums.

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

In der Post-Order-Traversal-Funktion beurteilen wir zunächst, ob der aktuelle Knoten leer ist, und geben ihn zurück, wenn er leer ist. Durchlaufen Sie dann nacheinander rekursiv den linken Teilbaum und den rechten Teilbaum und besuchen Sie schließlich den Wurzelknoten.

Schließlich können wir in der Hauptfunktion einen Beispiel-Binärbaum erstellen und die Postorder-Traversal-Funktion aufrufen, um ihn zu durchlaufen.

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

Wenn Sie den obigen Code ausführen, lautet die Ausgabe:

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

3. Eigenschaften des Binärbaums

Zusätzlich zur Traversalmethode weist der Binärbaum einige weitere Merkmale auf:

  • Vollständiger Binärbaum: In einem vollständigen Binärbaum werden mit Ausnahme der letzten Schicht die Knoten jeder Schicht gefüllt, und die Knoten der letzten Schicht werden in der Reihenfolge von links nach rechts gefüllt. Diese Baumstruktur kann effizient in einem Array gespeichert werden.
  • Vollständiger Binärbaum: Ein vollständiger Binärbaum ist ein Binärbaum, in dem alle Knoten entweder 0 oder 2 untergeordnete Knoten haben. In einem vollständigen Binärbaum befinden sich alle Blattknoten auf derselben Ebene.
  • Ausgeglichener Binärbaum: In einem ausgeglichenen Binärbaum beträgt der Höhenunterschied zwischen dem linken Teilbaum und dem rechten Teilbaum eines Knotens höchstens 1. Dies hält den Baum im Gleichgewicht und verbessert die Effizienz von Such-, Einfüge- und Löschvorgängen.
  • Binärer Suchbaum: Ein Binärer Suchbaum (BST) ist ein Binärbaum mit der Eigenschaft, dass für jeden Knoten alle Knoten in seinem linken Teilbaum Werte kleiner als dieser haben und alle Knoten in seinem rechten Teilbaum Werte kleiner als dieser haben größer als es ist. Diese Eigenschaft macht Such-, Einfüge- und Löschvorgänge in einem binären Suchbaum sehr effizient.

Alles in allem ist ein Binärbaum eine wichtige Datenstruktur mit einem breiten Anwendungsspektrum. Seine Knoten können bis zu zwei untergeordnete Knoten haben, und für den Zugriff auf und die Verarbeitung von Knoten können verschiedene Traversierungsmethoden verwendet werden.

Viertens die Implementierung eines Binärbaums

1. Definieren Sie die Struktur

Zuerst definieren wir eine Struktur, die die Knoten des Binärbaums darstellt. Jeder Knoten besteht aus einem Datenteil und zwei Zeigerteilen, die jeweils auf den linken untergeordneten Knoten und den rechten untergeordneten Knoten zeigen.

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

2. Erstellen Sie einen neuen Knoten

Als nächstes implementieren wir eine Funktion zum Erstellen eines neuen KnotenscreateNode , die für die Speicherzuweisung und die Initialisierung des Datenteils des Knotens verantwortlich ist. Wenn die Speicherzuweisung fehlschlägt, wird eine Fehlermeldung ausgegeben und das Programm beendet.

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. Fügen Sie einen Knoten ein

Als nächstes implementieren wir eine Funktion zum Einfügen von KnoteninsertNode . Diese Funktion fügt den neuen Knoten entsprechend dem übergebenen Datenwert an der entsprechenden Position ein. Wenn der Baum leer ist, erstellt die Funktion einen neuen Knoten und macht ihn zum Wurzelknoten. Andernfalls fügen Sie den Knoten rekursiv entweder in den linken oder rechten Teilbaum ein, abhängig von der Größe des Datenwerts.

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

Danach haben wir drei Traversal-Methoden implementiert: Pre-Order-Traversal, In-Order-Traversal und Post-Order-Traversal. Bei diesen Traversierungsmethoden werden die Knoten im Binärbaum in einer bestimmten Reihenfolge besucht.

4. Durchquerung vorbestellen

Die Vorbestellungs-TraversalfunktionpreOrder durchläuft den Baum in der Reihenfolge Wurzelknoten, linker Teilbaum und rechter Teilbaum und gibt die Datenwerte der Knoten aus.

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

5. Inorder-Durchquerung

Die Inorder-Traversal-FunktioninOrder durchläuft den Baum in der Reihenfolge linker Teilbaum, Wurzelknoten und rechter Teilbaum und gibt den Datenwert des Knotens aus.

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

6. Durchquerung nach der Bestellung

Die Post-Order-Traversal-FunktionpostOrder durchläuft den Baum in der Reihenfolge linker Teilbaum, rechter Teilbaum und Wurzelknoten und gibt den Datenwert des Knotens aus.

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

7. Hauptfunktion

Schließlichmain erstellen wir in der Funktion den Wurzelknoten eines leeren Baums und insertNodefügen über die Funktion einige Daten ein. Rufen Sie dann jeweils die drei Durchlauffunktionen auf und drucken Sie die Durchlaufergebnisse aus.

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

Dies ist der detaillierte Code einer einfachen Binärbaum-Implementierung. Ich hoffe, dass er Ihnen dabei helfen kann, besser zu verstehen, wie ein Binärbaum funktioniert und wie er implementiert wird.

Fünftens, vollständiger Code

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

Supongo que te gusta

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