1. 程式人生 > >資料結構-二叉樹(binaryTree)-C語言實現

資料結構-二叉樹(binaryTree)-C語言實現

用C語言實現二叉樹基本功能

介面部分 “binaryTree.h”
/*二叉樹介面*/

#ifndef _BINARY_TREE_H_
#define _BINARY_TREE_H_
#include <stdbool.h>

#define NAME_LENGTH 20
#define MAX_TRNODES 15  //測試用定義的最大節點數量,根據情況設定

/*實體定義*/
typedef struct _item
{
    char name[NAME_LENGTH];  //名字作為主鍵
    int value;
} Item;

typedef struct _trnode
{
    struct
_trnode *left; struct _item item; struct _trnode *right; } Trnode; typedef struct _tree { struct _trnode *root; int total; } Tree; /*函式原型*/ //初始化樹 void InitializeTree(Tree *tree); //樹是空的嗎 bool TreeIsEmpty(const Tree *tree); //沒有子節點嗎 bool TreeHasEmpty(const Tree *tree); //樹是滿的嗎 bool TreeIsFull(const
Tree *tree); //有兩個子節點嗎 bool TreeHasFull(const Tree *tree); //獲得樹的項數 int TrnodesCount(const Tree *tree); //新建一個樹節點, 傳入 item 儲存在節點中 bool AddTrnode(Tree *tree, Item item); //查詢一個樹節點, 傳入 item 進行比對, 查詢的節點地址存入 pnode , 相應父節點地址存入 plast bool SearchTrnode(const Tree *tree, Item item, Trnode **pnode, Trnode **plast); //刪除一個樹節點, 傳入 item 利用 查詢函式 獲得目標地址, 對目標空間進行釋放(free)
bool DeleteTrnode(Tree *tree, Item item); //列印節點, 通過 遞迴 將資訊輸出到螢幕 void printTrnode(const Tree *tree); //刪除所有樹節點, 遞迴 釋放(free) 節點空間 void DeleteAllTrnode(Tree *tree); #endif
實現部分 “binaryTree.c”
初始化
void InitializeTree(Tree *tree)
{
    tree->root = NULL;  //根節點置空
    tree->total = 0;    //節點數歸零
}
獲得樹的狀態

bool TreeIsEmpty(const Tree *tree)
{
    if (NULL == tree->root) {  //根節點為空則樹為空
        return true;
    } else {
        return false;
    }
}

bool TreeHasEmpty(const Tree *tree)
{
    if ( NULL == (tree->root)->left && NULL == (tree->root)->right ) {
        return true;  //左右節點都為空
    } else {
        return false;
    }
}

bool TreeHasFull(const Tree *tree) 
{
    if ( NULL != (tree->root)->left && NULL != (tree->root)->right ) {
        return true;  //左右節點都不為空
    } else {
        return false;
    }
}

bool TreeIsFull(const Tree *tree)
{
    if (MAX_TRNODES == tree->total) {
        return true;  //節點樹是否達到上限
    } else {
        return false;
    }
}

int TrnodesCount(const Tree *tree)
{
    return tree->total;  返回節點數
}
增加一個節點
  • 樹滿了嗎, 滿了返回 false
  • 樹為空時, 節點作為根節點
  • 樹不為空時
    • 比較兩個字串, 判斷接下來往左走還是往右走 ( 兩邊都不對說明這個值是重複的, 返回 false)
    • 看接下來在這個方向上是空位嗎, 是則把節點加到這, 不是則繼續往下走
  • 計數器加一
/*增加節點*/
bool AddTrnode(Tree *tree, Item item)
{
    //申請一塊新記憶體
    Trnode *p = (Trnode*) malloc(sizeof(Trnode));

    //空間有效
    if (NULL != p) {
        p->item = item;
        if (TreeIsFull(tree)) {
            fprintf(stderr, "Sorry, space is full.\n");  //空間已滿
            return false;
        } else {
            if (TreeIsEmpty(tree)) {  //空樹
                tree->root = p;
                p->left = NULL;
                p->right = NULL;
            } else {
                //找到節點新增的位置
                Trnode *temp = tree->root;
                do {
                    //toLeft(toRight): 自定義函式, 比較兩個字串, 確實往左(右)走時返回 true
                    if (toLeft((temp->item).name, item.name)) {  //往左走
                        if (temp->left != NULL) {
                            temp = temp->left;  //繼續往下遍歷
                        } else {
                            temp->left = p;  //新增節點
                            p->left = NULL;
                            p->right = NULL;
                            break;
                        }
                    }
                    else if (toRight((temp->item).name, item.name)) {  //往右走
                        if (temp->right != NULL) {
                            temp = temp->right;
                        } else {
                            temp->right = p;
                            p->left = NULL;
                            p->right = NULL;
                            break;
                        }
                    }
                    else {  //主關鍵字重複
                        fprintf(stderr, "Sorry, name: %s already exists.\n", item.name);
                        return false;
                    }
                }while (temp != NULL);
            }
        }
    }
    tree->total++;
    return true;
}
查詢節點
  • 空樹返回 false
  • plast 儲存上一層迴圈的節點地址
  • pnode 根據判斷的結果不斷往下更新
  • 直到比較結果為兩節點相同時, 比較結束. 此時 plast 存的是 pnode 的父節點, pnode 為查詢的結果
  • 如果查詢到最後還是沒有找到相同的節點, 返回 false
/*查詢節點*/
bool SearchTrnode(const Tree *tree, Item item, Trnode **pnode, Trnode **plast)
{
    Trnode *temp = tree->root;
    char name[NAME_LENGTH];
    strcpy(name, item.name);

    if (TreeIsEmpty(tree)) {  //為空
        fprintf(stderr, "Empty tree, no data.\n");
        return false;
    } else {  //不為空
        do {
            if (toLeft((temp->item).name, name)) {
                *plast = temp;  //記錄上一個節點的地址
                temp = temp->left;
            }
            else if (toRight((temp->item).name, name)) {
                *plast = temp;  //記錄上一個節點的地址
                temp = temp->right;
            }
            else {
                *pnode = temp;  //返回當前節點的地址
                return true;
            }
        }while (NULL != temp);  //子節點為空說明查詢結束
    }
    return false;
}
刪除節點
  • 使用查詢函式找到待刪除節點及其父節點的位置, 找不到返回 false
  • 刪除時分三種情況考慮

    • 待刪除節點沒有子樹時

      • 將父節點對應位置(左邊/右邊)置空
    • 待刪除節點有兩個子樹時

      • 待刪除節點是父節點的左子樹
        • 將右子樹作為父節點的左子樹
        • 將左子樹接到右子樹的左支最下端的左端
      • 待刪除節點是父節點的右子樹
        • 相反
    • 待刪除節點只有一個子樹時
      • 將待刪除節點的子樹移動為父節點的子樹
    • 上面三種情況都要分別考慮待刪除節點為樹的根節點的情況 ( 即 待刪除節點的父節點為空時 )
  • 釋放待刪除節點空間
  • 計數器減一
/*刪除節點*/
bool DeleteTrnode(Tree *tree, Item item)
{
    Trnode *pnode = NULL, *plast = NULL;
    Trnode *pleft = NULL, *pright = NULL;
    if ( !SearchTrnode(tree, item, &pnode, &plast) ) {
        return false;
    }

    Tree tempTree;
    InitializeTree(&tempTree);
    tempTree.root = pnode;

    //沒有子樹時
    if (TreeHasEmpty(&tempTree)) {
        //刪除的是根節點
        if (NULL == plast) {
            tree->root = NULL;
        } else {  //父節點的相應子節點歸空
            if (atLeft(plast, pnode)) {
                plast->left = NULL;
            } else {
                plast->right = NULL;
            }
        }
    }
    //左右子樹都有
    else if (TreeHasFull(&tempTree)) {
        //儲存當前節點的左右子樹地址
        pleft = pnode->left;
        pright = pnode->right;
        //刪除的是根節點
        if (NULL == plast) {
            //將左子樹根節點作為主樹根節點
            tree->root = pleft;
            //將右子樹接到左子樹的右支最下端的最右端
            tempTree.root = pleft;
            toRightEnd(&tempTree);
            (tempTree.root)->right = pright;
        } else {
            //在左邊
            if (atLeft(plast, pnode)) {
                //將右子樹作為父節點的左子樹
                plast->left = pright;
                //將左子樹接到右子樹的左支最下端的最左端
                tempTree.root = pright;
                toLeftEnd(&tempTree);
                (tempTree.root)->left = pleft;
            } else {  //在右邊
                //將左子樹作為父節點的右子樹
                plast->right = pleft;
                //將右子樹接到左子樹的右支最下端的最右端
                tempTree.root = pleft;
                toRightEnd(&tempTree);
                (tempTree.root)->right = pright;
            }
        }
    }
    //左右子樹只有一個
    else {
        //刪除的是根節點
        if (NULL == plast) {
            if (NULL != pnode->left) {
                tree->root = pnode->left;
            } else {
                tree->root = pnode->right;
            }
        } else {
            //確定有的是左子樹還是右子樹
            if (atLeft(plast, pnode)) {
                //將其作為父節點的子樹
                if (NULL != pnode->left) {
                    plast->left = pnode->left;
                } else {
                    plast->left = pnode->right;
                }
            } else {
                if (NULL != pnode->left) {
                    plast->right = pnode->left;
                } else {
                    plast->right = pnode->right;
                }
            }
        }
    }
    free(pnode);
    (tree->total)--;
    return true;
}
列印節點到螢幕
  • 為空樹時, 不列印
  • 不為空樹
    • 有子樹嗎, 沒有則列印當前節點, 返回上一層
    • 往左往右遞迴
    • 列印當前節點, 返回上一層
/*列印節點*/
void printTrnode(const Tree *tree)
{
    if (TreeIsEmpty(tree)) {
        fprintf(stderr, "Empty tree, no data.\n");
        return;
    }

    //建立臨時樹作為引數樹的拷貝
    Tree tempTree;
    InitializeTree(&tempTree);
    tempTree = *tree;

    //記錄臨時樹的根節點
    Trnode *p = tempTree.root;

    //遞迴遍歷
    if (TreeHasEmpty(&tempTree)) {  //無路可走,返回上一層
        printf("%-20s\t %-10d\t %-15p\t %-15p\t %-15p\n",(p->item).name, (p->item).value, p, p->left, p->right);
        return ;
    } else {
        if (p->left != NULL) {  //左邊有路,往左走
            tempTree.root = p->left;
            printTrnode(&tempTree);
        }
        if (NULL != p->right) {  //右邊有路,往右走
            tempTree.root = p->right;
            printTrnode(&tempTree);
        }
    }

    //遞迴處理節點都在遞迴後(返回前)處理, 或者在遞迴前處理, 避免重複呼叫
    printf("%-20s\t %-10d\t %-15p\t %-15p\t %-15p\n",(p->item).name, (p->item).value, p, p->left, p->right);
    return ;
}
刪除所有節點
  • 與列印節點相似, 使用遞迴逐一刪除節點, 為空樹返回 false
/*刪除所有節點*/
void DeleteAllTrnode(Tree *tree)
{
    freeTrnodes(tree);
    tree->root = NULL;
    tree->total = 0;
}

static void freeTrnodes(Tree *tree)
{
    if (TreeIsEmpty(tree)) {
        fprintf(stderr, "Empty tree, no data.\n");
        return;
    }

    //建立臨時樹作為引數樹的拷貝
    Tree tempTree;
    InitializeTree(&tempTree);
    tempTree = *tree;

    //記錄臨時樹的根節點
    Trnode *p = tempTree.root;

    //遞迴遍歷
    if (TreeHasEmpty(&tempTree)) {  //無路可走,返回上一層
        free(p);
        return ;
    } else {
        if (p->left != NULL) {  //左邊有路,往左走
            tempTree.root = p->left;
            freeTrnodes(&tempTree);
        }
        if (NULL != p->right) {  //右邊有路,往右走
            tempTree.root = p->right;
            freeTrnodes(&tempTree);
        }
    }

    //遞迴處理節點都在遞迴後(返回前)處理, 或者在遞迴前處理, 避免重複呼叫
    free(p);
    return ;
}
寫一個模組測試一個模組