1. 程式人生 > >c語言實現二叉樹常用演算法

c語言實現二叉樹常用演算法

構造二叉樹結點結構

typedef struct BT
{
    char data;
    struct BT *l_chrild;
    struct BT *r_chrild;
}BT;
  • 建立二叉樹
BT* Create_tree()// 建立二叉樹
{
    BT *bt;
    char x;
    scanf("%c",&x);
    getchar();

    if (x == '0')
    {
        bt = NULL;
    }
    else
    {
        bt = (BT *)malloc(sizeof(BT));
        bt->
data = x; printf("請輸入 %c 的左子樹\n", bt->data); bt->l_chrild = Create_tree(); // printf("請輸入 %c 的右子樹\n",bt->data); bt->r_chrild = Create_tree(); } return bt; }

先序遍歷二叉樹:思路,

  • 當二叉樹不為空時
  • 訪問根節點
  • 遍歷根節點左子樹
  • 遍歷根節點右子樹
  • 其他遍歷類似
void pre_order(BT *bt)  // 先序遍歷
{
    // 體會遞迴思想:是如何執行的分兩次結束,一次是左右孩子為空,或者此程式執行結束。
if (bt == NULL) return; else { printf("%c ", bt->data); // 根 pre_order(bt->l_chrild); // 左 pre_order(bt->r_chrild); // 右 } }

中序遍歷:

void ln_order(BT *bt)  // 中序遍歷
{
    if (bt == NULL)
    return ;
    else
    {
        ln_order(bt->l_chrild);   // 左
printf("%c ", bt->data); // 根 ln_order(bt->r_chrild); // 右 } }

後序遍歷:

void post_order(BT *bt)  // 後序遍歷
{
    if (bt == NULL)
        return;
    else
    {
        post_order(bt->l_chrild);  // 左
        post_order(bt->r_chrild); // 右
        printf("%c ", bt->data); // 根
    }
}

層次遍歷:
遍歷從二叉樹的根節點開始,首先將根節點指標入隊,然後從隊頭取出一個元素,每取一個元素,執行下面的操作
1>訪問該元素所指結點(就是輸出)
2> 若該元素所指結點的,左,右孩子節點非空,則將該元素所指結點的左孩子指標和右孩子指標一次入隊

void lever_order(BT * bt)  // 層次遍歷
{
    //入隊順序,跟,左,右,左子樹的左孩子,左子樹的右孩子,右子樹的左孩子,右子樹的右孩子,出對順序就是這
    int i, j;
    BT *q[100], *p;
    p = bt;

    if (p != NULL)    // 若二叉樹非空,則跟根結點地址入隊
    {
        i = 1;      // i指向對頭
        q[i] = p;  
        j = 2;    // j指向對尾
    }

    while (i != j)  // 當佇列不為空時執行迴圈
    {
        p = q[i];
        printf("%c ",p->data);    // 訪問首結點的資料域
        if (p->l_chrild != NULL)  // 將出隊結點的左孩子的地址入隊
        {
            q[j] = p->l_chrild;
            j++;
        }
        if (p->r_chrild != NULL)
        {
            q[j] = p->r_chrild;  // 將出隊結點的右孩子的地址入隊
            j++;
        }
        i++;
    }
}

求葉子結點數:

// 思想:從根節點開始,當根節點的左右孩子都為空,則count+1,遞迴便歷
void leaf_num(BT *bt, int *count)  // 求葉子結點數
{
    if (bt != NULL)  // 當結點為空時返回呼叫處
    {
        //當左右孩子都為空時,說明已該節點為葉子節點
        if (bt->l_chrild == NULL && bt->r_chrild == NULL)
        {
        //  (*count)++; // 計數器
            ++*count;  // 兩個都行注意運算子優先順序
        }

        leaf_num(bt->l_chrild, count);
        leaf_num(bt->r_chrild, count);

    }
}

求結點個數:

// 與葉子節點類似,如果根節點不為空,node+1 遞迴遍歷
void node_num(BT  *bt, int *node) //結點個數
{
    if (bt != NULL) // 當該節點為空時,返回呼叫處
    {
        (*node)++;
        node_num(bt->l_chrild, node); // 便歷左右子樹
        node_num(bt->r_chrild, node);
    }
}

求二叉樹深度:
這個一定要好好想想
思路:

  • 從二叉樹的根節點開始:
  • 若二叉樹根節點為空,返回0,
  • 否則:
  • 遞迴統計左子樹的深度,
  • 遞迴統計右子樹的深度。
  • 遞迴結束,返回左右子樹深度的較大值,即二叉樹的深度
int tree_depth(BT *bt) // 二叉樹深度,就是最大層數
{
    int l_dep, r_dep;  //定義兩個變數,存放左,右子樹的深度

    if (bt == NULL)
        return 0;
    else
    {
        l_dep = tree_depth(bt->l_chrild); //左右子樹的深度標記
        r_dep = tree_depth(bt->r_chrild);

        if (l_dep > r_dep)  // 比較來個深度,較大的加1返回,值得注意的是當此程式呢每一次呼叫自然執行到最後(而不是bt==NULL)返回的值到呼叫處 進行自增
            return l_dep+1;
        else
            return r_dep+1;
    }

}
  • 映象二叉樹,又稱翻轉二叉樹:
// 就是所有節點對換, 也可以用非遞迴用棧實現,與此類似
//這裡是遞迴實現
void reversal(BT *bt) // 映象二叉樹
{
    BT *p;
    if (bt == NULL) 
    {
        return ;
    }
    //交換兩個節點,相當於t=a;a=b;b=t;
    p = bt->l_chrild;
    bt->l_chrild = bt->r_chrild;
    bt->r_chrild = p;

    if (bt->l_chrild)  // 遍歷左子樹,此時的左子樹應給是,原來的右子樹(原來左右都不為空時)
        reversal(bt->l_chrild);
    if (bt->r_chrild)
        reversal(bt->r_chrild);
}

括號二叉樹:

void kuohao(BT *bt) //括號顯示二叉樹
{
    if (bt != NULL)
    {
        printf("%c", bt->data);
        if (bt->l_chrild || bt->r_chrild)
        {
            printf("(");
            kuohao(bt->l_chrild);
            if (bt->r_chrild)
                printf(",");
            kuohao(bt->r_chrild);
                printf(")");
        }
    }
}

凹入法顯示二叉樹:

void print_space(BT *bt, int t)  // 凹入法顯示二叉樹,利用中序遍歷,也可以先,後序遍歷,就是在輸出時加上一個迴圈
{
    int i;
    if (bt)
    {
        print_space(bt->l_chrild, t+3); 
        for (i = 0; i < t; i++)
        {
            printf("*");
        }
        printf("%10c\n",bt->data);
        print_space(bt->r_chrild, t+3);
    }
}

原始碼:

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

typedef struct BT
{
    char data;
    struct BT *l_chrild;
    struct BT *r_chrild;
}BT;
void show_fun()
{
        printf("                 二叉樹子系統            \n");
        printf("*******************************************\n");
        printf("*         1-------建二叉樹                *\n");
        printf("*         2-------先序遍歷                *\n");
        printf("*         3-------中序遍歷                *\n");
        printf("*         4-------後序遍歷                *\n");
        printf("*         5-------層次遍歷                *\n");
        printf("*         6-------求葉子數                *\n");
        printf("*         7-------求結點數                *\n");
        printf("*         8-------求深度                  *\n");
        printf("*         9-------映象二叉樹              *\n");
        printf("*         10-------括號顯示               *\n");
        printf("*         11-------凹入顯示               *\n");
        printf("*         0-------返回                    *\n");
        printf("*******************************************\n");

}
BT* Create_tree()// 建立二叉樹
{
    BT *bt;
    char x;
    scanf("%c",&x);
    getchar();

    if (x == '0')
    {
        bt = NULL;
    }
    else
    {
        bt = (BT *)malloc(sizeof(BT));
        bt->data = x;

        printf("請輸入 %c 的左子樹\n", bt->data);
        bt->l_chrild = Create_tree();   // 

        printf("請輸入 %c 的右子樹\n",bt->data);
        bt->r_chrild = Create_tree();
    }
    return bt;

}

void pre_order(BT *bt)  // 先序遍歷
{
    // 體會遞迴思想:是如何執行的分兩次結束,一次是左右孩子為空,或者此程式執行結束。
    if (bt == NULL)
        return;
    else
    {
        printf("%c ", bt->data);   // 根
        pre_order(bt->l_chrild);   // 左
        pre_order(bt->r_chrild);  // 右
    }

}

void ln_order(BT *bt)  // 中序遍歷
{
    if (bt == NULL)
    return ;
    else
    {
        ln_order(bt->l_chrild);   // 左
        printf("%c ", bt->data); // 根
        ln_order(bt->r_chrild); // 右
    }
}

void post_order(BT *bt)  // 後序遍歷
{
    if (bt == NULL)
        return;
    else
    {
        post_order(bt->l_chrild);  // 左
        post_order(bt->r_chrild); // 右
        printf("%c ", bt->data); // 根
    }
}
// 
void lever_order(BT * bt)  // 層次遍歷
{
    //入隊順序,跟,左,右,左子樹的左孩子,左子樹的右孩子,右子樹的左孩子,右子樹的右孩子,出對順序就是這
    int i, j;
    BT *q[100], *p;
    p = bt;

    if (p != NULL)    // 若二叉樹非空,則跟根結點地址入隊
    {
        i = 1;      // i指向對頭
        q[i] = p;  
        j = 2;    // j指向對尾
    }

    while (i != j)  // 當佇列不為空時執行迴圈
    {
        p = q[i];
        printf("%c ",p->data);    // 訪問首結點的資料域
        if (p->l_chrild != NULL)  // 將出隊結點的左孩子的地址入隊
        {
            q[j] = p->l_chrild;
            j++;
        }
        if (p->r_chrild != NULL)
        {
            q[j] = p->r_chrild;  // 將出隊結點的右孩子的地址入隊
            j++;
        }
        i++;
    }
}

// 思想:從根節點開始,當根節點的左右孩子都為空,則count+1,遞迴便歷
void leaf_num(BT *bt, int *count)  // 求葉子結點數
{
    if (bt != NULL)  // 當結點為空時返回呼叫處
    {
        //當左右孩子都為空時,說明已該節點為葉子節點
        if (bt->l_chrild == NULL && bt->r_chrild == NULL)
        {
        //  (*count)++; // 計數器
            ++*count;  // 兩個都行注意運算子優先順序
        }

        leaf_num(bt->l_chrild, count);
        leaf_num(bt->r_chrild, count);

    }

}

// 與葉子節點類似,如果根節點不為空,node+1 遞迴遍歷
void node_num(BT  *bt, int *node) //結點個數
{
    if (bt != NULL) // 當該節點為空時,返回呼叫處
    {
        (*node)++;
        node_num(bt->l_chrild, node); // 便歷左右子樹
        node_num(bt->r_chrild, node);
    }

}

int tree_depth(BT *bt) // 二叉樹深度,就是最大層數
{
    int l_dep, r_dep;  //定義兩個變數,存放左,右子樹的深度

    if (bt == NULL)
        return 0;
    else
    {
        l_dep = tree_depth(bt->l_chrild); //左右子樹的深度標記
        r_dep = tree_depth(bt->r_chrild);

        if (l_dep > r_dep)  // 比較來個深度,較大的加1返回,值得注意的是當此程式呢每一次呼叫自然執行到最後(而不是bt==NULL)返回的值到呼叫處 進行自增
            return l_dep+1;
        else
            return r_dep+1;
    }

}
// 就是所有節點對換, 也可以用非遞迴用棧實現,與此類似
//這裡是遞迴實現
void reversal(BT *bt) // 映象二叉樹
{
    BT *p;
    if (bt == NULL) 
    {
        return ;
    }
    //交換兩個節點,相當於t=a;a=b;b=t;
    p = bt->l_chrild;
    bt->l_chrild = bt->r_chrild;
    bt->r_chrild = p;

    if (bt->l_chrild)  // 遍歷左子樹,此時的左子樹應給是,原來的右子樹(原來左右都不為空時)
        reversal(bt->l_chrild);
    if (bt->r_chrild)
        reversal(bt->r_chrild);
}
void kuohao(BT *bt) //括號顯示二叉樹
{
    if (bt != NULL)
    {
        printf("%c", bt->data);
        if (bt->l_chrild || bt->r_chrild)
        {
            printf("(");
            kuohao(bt->l_chrild);

            if (bt->r_chrild)
                printf(",");
            kuohao(bt->r_chrild);
                printf(")");
        }
    }
}
void print_space(BT *bt, int t)  // 凹入法顯示二叉樹,利用中序遍歷,也可以先,後序遍歷,就是在輸出時加上一個迴圈
{
    int i;
    if (bt)
    {
        print_space(bt->l_chrild, t+3); 
        for (i = 0; i < t; i++)
        {
            printf("*");
        }
        printf("%10c\n",bt->data);
        print_space(bt->r_chrild, t+3);
    }
}

int main()
{
    int i;
    BT * bt = NULL;
    while (1)
    {
        show_fun();
        printf("請輸入一個數字\n");
        scanf("%d",&i);
        getchar();
        if (i == 1)
        {
            printf("請輸入一課二叉樹\n");
            bt = Create_tree();
            printf("二叉樹建立成功\n");
        }
        else if (i == 2)
        {
            printf("該二叉樹先序遍歷為:\n");
            pre_order(bt);  
            printf("\n");
        }
        else if (i == 3)
        {
            printf("改二叉樹中序遍歷為:\n");
            ln_order(bt);
            printf("\n");

        }
        else if (i == 4)
        {
            printf("該二叉樹後序遍歷為:\n");
            post_order(bt);
            printf("\n");
        }
        else if (i == 5)
        {
            printf("該二叉樹層次遍歷為:\n");
            lever_order(bt);
            printf("\n");
        }
        else if (i == 6)
        {
        int count = 0;
            leaf_num(bt, &count);
            printf("該二叉樹葉子數為:%d\n", count);
        }
        else if (i == 7)
        {
            int node = 0;
            node_num(bt, &node);
            printf("該二叉樹結點個數為: %d\n", node);
        }
        else if (i == 8)
        {
            int depth;
            depth = tree_depth(bt);
            printf("該二叉樹深度為:%d\n",depth);

        }
        else if (i == 9)
        {
            reversal(bt);
            printf("已轉換成映象二叉樹\n");

        }
        else if (i == 10)
        {
            printf("括號顯示二叉樹: \n");
            kuohao(bt);
            printf("\n");
        }
        else if (i == 11)
        {
            printf("空格顯示二叉樹: \n");
            print_space(bt, 3); // 傳多少都可以
        }
        else if (i == 0)
            return 0;

        else 
            return 0;

    }

    return 0;
}