資料結構-二叉樹(binaryTree)-C語言實現
阿新 • • 發佈:2019-01-06
用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 ;
}