c語言實現二叉樹常用演算法
阿新 • • 發佈:2019-01-22
構造二叉樹結點結構
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;
}