二叉樹 & 二叉查詢樹 ADT [資料結構與演算法分析 c 語言描述]

jerrkill發表於2018-12-28

1. 引言

主要涉及知識點

  • 二叉樹的概念。
  • 二叉查詢樹的概念跟常用操作。
  • 二叉樹的遍歷(先序遍歷、中序遍歷、後序遍歷、層序遍歷)。

其他

  • 佇列(層序遍歷用到)。

最後留了兩個思考的點

  • delete 為什麼要 return t。
  • 為什麼是 t->right = delete(x, t->right) 而不直接 delete(x, t->right)。

2. 概念

樹是一種非線性資料格式,計算機裡面非常有用的資料格式之一,最常用的其中就有二叉樹、二叉查詢樹,

二叉樹:每個節點都不能有多餘兩個兒子的一棵樹
二叉查詢樹:對於樹種的每一個節點 X ,都滿足它的左子樹的關鍵字小於 X 的關鍵字,右子樹的關鍵字大於 X 的關鍵字並且 X 的關鍵字是不重複的。

二叉查詢樹是基礎性資料結構,用於構建更為抽象的資料結構,如集合、multiset、關聯陣列等,二叉樹的優點是插入、查詢時間複雜度低 O(logN)

3. 二叉查詢樹的思路

定義一顆二叉查詢樹的結構體

typedef struct tree_node
{
    element_type element; //節點關鍵字
    struct tree_node *left; //左子樹
    struct tree_node *right; //右子樹
} *search_tree;
typedef search_tree position;

一棵標準二叉查詢樹需要實現的方法

search_tree make_empty(search_tree t);
position find(element_type x, search_tree t);
position find_min(search_tree t);
position find_max(search_tree t);
search_tree insert(element_type x, search_tree t);
search_tree delete(element_type x, search_tree t);
element_type retrieve(position p); //檢索值

麻煩一點兒的在刪除。
刪除的節點有三種情況:

  • 是樹葉——直接 delete。
  • 有一個兒子:父節點直接指向子節點。
  • 有兩個兒子:右子樹最小的資料代替該節點,並刪除那個節點(右子樹最小節點、直接遞迴刪)。

4. 二叉樹遍歷

  • 先序遍歷:root 節點->左節點->右節點。
  • 中序遍歷:左節點->root 節點->右節點。
  • 後序遍歷:左節點->右節點->root 節點。
  • 層序遍歷:逐層遍歷。
    重點說層序遍歷。

5. 具體實現

queue.h標頭檔案,就是一個佇列,具體實現程式碼參照佇列那篇文章

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

#define error(str) fatal_error(str)
#define fatal_error(str) fprintf(stderr, "%s\n", str),exit(1)

struct node;
typedef search_tree queue_element_type;
typedef struct node *ptr_to_node;
typedef ptr_to_node queue;

queue create_queue();
void queue_make_empty(queue q);
int is_empty(queue q);
void enqueue(queue_element_type x, queue q);
void dequeue(queue q);
queue_element_type front(queue q);
queue_element_type front_and_dequeue(queue q);
void dispose_queue(queue *q);

bitree.c 檔案

/**
 * 二叉查詢樹
 */

#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include "queue.h"
#include "bitree.h"

struct tree_node
{
    element_type element; //節點關鍵字
    struct tree_node *left; //左子樹
    struct tree_node *right; //右子樹
};

search_tree make_empty(search_tree t)
{
    if (NULL != t) {
        make_empty(t->left);
        make_empty(t->right);
        free(t);
    }
    return NULL;
}

position find(element_type x, search_tree t)
{
    if (NULL == t)
        return NULL;

    if (t->element > x)
        return find(x, t->left);
    else if(t->element < x)
        return find(x, t->right);
    else
        return t;
}

position find_min(search_tree t)
{
    /*非遞迴*/
    /*if (NULL != t)
        while (t->left != NULL)
            t = t->left;
    return t;*/
    //遞迴
    if (NULL == t)
        return NULL;

    if (NULL == t->left)
        return t;
    else
        return find_min(t->left);
}

position find_max(search_tree t)
{
    /*遞迴*/
    /*if (NULL == t)
        return NULL;

    if (NULL == t->right)
        return t;
    else
        return find_max(t->right);*/
    /*非遞迴*/
    if (NULL != t)
        while (t->right != NULL)
            t = t->right;
    return t;
}

search_tree insert(element_type x, search_tree t)
{
    if (NULL == t) {
        t = (search_tree)malloc(sizeof(struct tree_node));
        if (NULL == t)
            fatal_error("out of space");
        else {
            t->element = x;
            t->left = t->right = NULL;
        }
    } else if (x < t->element) {
        t->left = insert(x, t->left);
    }
    else if (x > t->element) {
        t->right = insert(x, t->right);
    }
    return t;
}

search_tree delete(element_type x, search_tree t)
{
    position temp_cell;
    if ( NULL == t)
        error("empty tree");
    else if(x < t->element) 
        t->left = delete(x, t->left);
        // delete(x, t->left); /*思考:為什麼不直接 delete*/
    else if (x > t->element)
        t->right = delete(x, t->right);
        // delete(x, t->right); /*思考:為什麼不直接 delete*/
    else if (t->left && t->right) { // 有兩個兒子 找右子樹最小節點
        temp_cell = find_min(t->right);
        t->element = temp_cell->element; // 右子樹最小關鍵字替換上來
        // delete right child
        delete(temp_cell->element, t->right);
    } else { // 一個 or 0 個節點,不左還是右都是直接跨過 t 直接指向該節點
        temp_cell = t;
        if (t->left == NULL)
            t = t->right;
        else if (t->right == NULL)
            t = t->left;
        free(temp_cell);
        temp_cell = NULL;
    }
    return t; //是何用意?
}

element_type retrieve(position p)
{
    return p->element;
}

void print_tree(search_tree t, int type)
{
    if (NULL == t) {
        fatal_error("empty tree");
    }
    // 先序遍歷
    if (type == 1)
        printf("%d ", t->element);

    if (t->left)
        print_tree(t->left, type);
    // 中序遍歷
    if (type == 2)
        printf("%d ", t->element);

    if (t->right)
        print_tree(t->right, type);
    // 後序遍歷
    if (type == 3)
        printf("%d ", t->element);
}

void print_layer(search_tree t)
{
    queue q;
    position p;
    q = create_queue();
    enqueue(t, q);
    while (is_empty(q) != 1) {
        p = front(q);
        printf("%d ", p->element);
        if (NULL != p->left)
            enqueue(p->left, q);
        if (NULL != p->right)
            enqueue(p->right, q);
        dequeue(q);
    }
}

void test()
{
    search_tree t;
    int i, x;
    t = make_empty(NULL);
    srand( (unsigned)time(NULL) );
    for( i = 0; i < 10; i++ )
        t = insert(rand() % 50, t);
    printf("\n先序遍歷:");
    print_tree(t, 1);
    printf("\n中序遍歷:");
    print_tree(t, 2);
    printf("\n後序遍歷:");
    print_tree(t, 3);
    printf("\n層次遍歷:");
    print_layer(t);
    printf("\n");
    printf("max:%d\n", find_max(t)->element);
    printf("min:%d\n", find_min(t)->element);
}

int main(int argc, char const *argv[])
{
    test();
    return 0;
}

7. 執行截圖

file

本作品採用《CC 協議》,轉載必須註明作者和本文連結

高度自律,深度思考,以勤補拙

相關文章