二叉查找樹(一)之 圖文解析 和 C語言的實現

出自:http://www.cnblogs.com/skywang12345/p/3576328.html

 

概要

    本章先對二叉樹的相關理論知識進行介紹,然後給出C語言的詳細實現。關於二叉樹的學習,需要說明的是:它並不難,不僅不難,而且它非常簡單。初次接觸樹的時候,我也覺得它似乎很難;而之所產生這種感覺主要是由於二叉樹有一大堆陌生的概念、性質等內容。而當我真正的實現了二叉樹再回過頭來看它的相關概念和性質的時候,覺得原來它是如此的簡單!因此,建議在學習二叉樹的時候:先對二叉樹基本的概念、性質有個基本瞭解,遇到難懂的知識點,可以畫圖來幫助理解;在有個基本的概念之後,再親自動手實現二叉查找樹(這一點至關重要!);最後再回過頭來總結一下二叉樹的理論知識時,你會發現——它的確很簡單!在代碼實踐中,我以"二叉查找樹,而不是單純的二叉樹"爲例子進行說明,單純的二叉樹非常簡單,實際使用很少。況且掌握了二叉查找樹,二叉樹也就自然掌握了。

      本篇實現的二叉查找樹是C語言版的,後面章節再分別給出C++和Java版本的實現。您可以根據自己熟悉的語言進行實踐學習!

      請務必深刻理解、實踐並掌握"二叉查找樹"!它是後面學習AVL樹、伸展樹、紅黑樹等相關樹結構的基石!

 

目錄
1. 樹的介紹
2. 二叉樹的介紹
3. 二叉查找樹的C實現
4. 二叉查找樹的C測試程序

轉載請註明出處:http://www.cnblogs.com/skywang12345/p/3576328.html


更多內容數據結構與算法系列 目錄 

(01). 二叉查找樹(一)之 圖文解析 和 C語言的實現
(02). 二叉查找樹(二)之 C++的實現
(03). 二叉查找樹(三)之 Java的實現

 

樹的介紹

1. 樹的定義

樹是一種數據結構,它是由n(n>=1)個有限節點組成一個具有層次關係的集合。

把它叫做“樹”是因爲它看起來像一棵倒掛的樹,也就是說它是根朝上,而葉朝下的。它具有以下的特點:
(01) 每個節點有零個或多個子節點;
(02) 沒有父節點的節點稱爲根節點;
(03) 每一個非根節點有且只有一個父節點;
(04) 除了根節點外,每個子節點可以分爲多個不相交的子樹。

 

2. 樹的基本術語

若一個結點有子樹,那麼該結點稱爲子樹根的"雙親",子樹的根是該結點的"孩子"。有相同雙親的結點互爲"兄弟"。一個結點的所有子樹上的任何結點都是該結點的後裔。從根結點到某個結點的路徑上的所有結點都是該結點的祖先。

結點的度:結點擁有的子樹的數目。
葉子:度爲零的結點。
分支結點:度不爲零的結點。
樹的度:樹中結點的最大的度。

層次:根結點的層次爲1,其餘結點的層次等於該結點的雙親結點的層次加1。
樹的高度:樹中結點的最大層次。
無序樹:如果樹中結點的各子樹之間的次序是不重要的,可以交換位置。
有序樹:如果樹中結點的各子樹之間的次序是重要的, 不可以交換位置。
森林:0個或多個不相交的樹組成。對森林加上一個根,森林即成爲樹;刪去根,樹即成爲森林。

 

二叉樹的介紹

1. 二叉樹的定義

二叉樹是每個節點最多有兩個子樹的樹結構。它有五種基本形態:二叉樹可以是空集;根可以有空的左子樹或右子樹;或者左、右子樹皆爲空。

 

2. 二叉樹的性質

二叉樹有以下幾個性質:TODO(上標和下標)
性質1:二叉樹第i層上的結點數目最多爲 2{i-1} (i≥1)。
性質2:深度爲k的二叉樹至多有2{k}-1個結點(k≥1)。
性質3:包含n個結點的二叉樹的高度至少爲log2 (n+1)
性質4:在任意一棵二叉樹中,若終端結點的個數爲n0,度爲2的結點數爲n2,則n0=n2+1

 

2.1 性質1:二叉樹第i層上的結點數目最多爲 2{i-1} (i≥1)

證明:下面用"數學歸納法"進行證明。
        (01) 當i=1時,第i層的節點數目爲2{i-1}=2{0}=1。因爲第1層上只有一個根結點,所以命題成立。
        (02) 假設當i>1,第i層的節點數目爲2{i-1}。這個是根據(01)推斷出來的!
               下面根據這個假設,推斷出"第(i+1)層的節點數目爲2{i}"即可。
                由於二叉樹的每個結點至多有兩個孩子,故"第(i+1)層上的結點數目" 最多是 "第i層的結點數目的2倍"。即,第(i+1)層上的結點數目最大值=2×2{i-1}=2{i}
                故假設成立,原命題得證!

 

2.2 性質2:深度爲k的二叉樹至多有2{k}-1個結點(k≥1)

證明:在具有相同深度的二叉樹中,當每一層都含有最大結點數時,其樹中結點數最多。利用"性質1"可知,深度爲k的二叉樹的結點數至多爲:
           20+21+…+2k-1=2k-1
           故原命題得證!

 

2.3 性質3:包含n個結點的二叉樹的高度至少爲log2 (n+1)

證明:根據"性質2"可知,高度爲h的二叉樹最多有2{h}–1個結點。反之,對於包含n個節點的二叉樹的高度至少爲log2(n+1)。

 

2.4 性質4:在任意一棵二叉樹中,若終端結點的個數爲n0,度爲2的結點數爲n2,則n0=n2+1

證明:因爲二叉樹中所有結點的度數均不大於2,所以結點總數(記爲n)="0度結點數(n0)" + "1度結點數(n1)" + "2度結點數(n2)"。由此,得到等式一。
         (等式一) n=n0+n1+n2
      另一方面,0度結點沒有孩子,1度結點有一個孩子,2度結點有兩個孩子,故二叉樹中孩子結點總數是:n1+2n2。此外,只有根不是任何結點的孩子。故二叉樹中的結點總數又可表示爲等式二。
         (等式二) n=n1+2n2+1
        由(等式一)和(等式二)計算得到:n0=n2+1。原命題得證!

 

3. 滿二叉樹,完全二叉樹和二叉查找樹

3.1 滿二叉樹

定義:高度爲h,並且由2{h} –1個結點的二叉樹,被稱爲滿二叉樹。

 

3.2 完全二叉樹

定義:一棵二叉樹中,只有最下面兩層結點的度可以小於2,並且最下一層的葉結點集中在靠左的若干位置上。這樣的二叉樹稱爲完全二叉樹。
特點:葉子結點只能出現在最下層和次下層,且最下層的葉子結點集中在樹的左部。顯然,一棵滿二叉樹必定是一棵完全二叉樹,而完全二叉樹未必是滿二叉樹。

 

3.3 二叉查找樹

定義:二叉查找樹(Binary Search Tree),又被稱爲二叉搜索樹。設x爲二叉查找樹中的一個結點,x節點包含關鍵字key,節點x的key值記爲key[x]。如果y是x的左子樹中的一個結點,則key[y] <= key[x];如果y是x的右子樹的一個結點,則key[y] >= key[x]。

在二叉查找樹中:
(01) 若任意節點的左子樹不空,則左子樹上所有結點的值均小於它的根結點的值;
(02) 任意節點的右子樹不空,則右子樹上所有結點的值均大於它的根結點的值;
(03) 任意節點的左、右子樹也分別爲二叉查找樹。
(04) 沒有鍵值相等的節點(no duplicate nodes)。

在實際應用中,二叉查找樹的使用比較多。下面,用C語言實現二叉查找樹。

 

二叉查找樹的C實現

1. 節點定義

1.1 節點定義

複製代碼
typedef int Type;

typedef struct BSTreeNode{
    Type   key;                    // 關鍵字(鍵值)
    struct BSTreeNode *left;    // 左孩子
    struct BSTreeNode *right;    // 右孩子
    struct BSTreeNode *parent;    // 父結點
}Node, *BSTree;
複製代碼

二叉查找樹的節點包含的基本信息:
(01) key       -- 它是關鍵字,是用來對二叉查找樹的節點進行排序的。
(02) left       -- 它指向當前節點的左孩子。
(03) right    -- 它指向當前節點的右孩子。
(04) parent -- 它指向當前節點的父結點。

 

1.2 創建節點

創建節點的代碼

複製代碼
static Node* create_bstree_node(Type key, Node *parent, Node *left, Node* right)
{
    Node* p;

    if ((p = (Node *)malloc(sizeof(Node))) == NULL)
        return NULL;
    p->key = key;
    p->left = left;
    p->right = right;
    p->parent = parent;

    return p;
}
複製代碼

 

2 遍歷

這裏講解前序遍歷中序遍歷後序遍歷3種方式。

2.1 前序遍歷

若二叉樹非空,則執行以下操作:
(01) 訪問根結點;
(02) 先序遍歷左子樹;
(03) 先序遍歷右子樹。

前序遍歷代碼

複製代碼
void preorder_bstree(BSTree tree)
{
    if(tree != NULL)
    {
        printf("%d ", tree->key);
        preorder_bstree(tree->left);
        preorder_bstree(tree->right);
    }
}
複製代碼

 

2.2 中序遍歷

若二叉樹非空,則執行以下操作:
(01) 中序遍歷左子樹;
(02) 訪問根結點;
(03) 中序遍歷右子樹。

中序遍歷代碼

複製代碼
void inorder_bstree(BSTree tree)
{
    if(tree != NULL)
    {
        inorder_bstree(tree->left);
        printf("%d ", tree->key);
        inorder_bstree(tree->right);
    }
}
複製代碼

 

2.3 後序遍歷

若二叉樹非空,則執行以下操作:
(01) 後序遍歷左子樹;
(02) 後序遍歷右子樹;
(03) 訪問根結點。

後序遍歷代碼

複製代碼
void postorder_bstree(BSTree tree)
{
    if(tree != NULL)
    {
        postorder_bstree(tree->left);
        postorder_bstree(tree->right);
        printf("%d ", tree->key);
    }
}
複製代碼

 

 

下面通過例子對這些遍歷方式進行介紹。

對於上面的二叉樹而言,
(01) 前序遍歷結果: 3 1 2 5 4 6
(02) 中序遍歷結果: 1 2 3 4 5 6 
(03) 後序遍歷結果: 2 1 4 6 5 3

 

3. 查找

遞歸版本的代碼

複製代碼
Node* bstree_search(BSTree x, Type key)
{
    if (x==NULL || x->key==key)
        return x;

    if (key < x->key)
        return bstree_search(x->left, key);
    else
        return bstree_search(x->right, key);
}
複製代碼

非遞歸版本的代碼

複製代碼
Node* iterative_bstree_search(BSTree x, Type key)
{
    while ((x!=NULL) && (x->key!=key))
    {
        if (key < x->key)
            x = x->left;
        else
            x = x->right;
    }

    return x;
}
複製代碼

 

4. 最大值和最小值

查找最大值的代碼

複製代碼
Node* bstree_maximum(BSTree tree)
{
    if (tree == NULL)
        return NULL;

    while(tree->right != NULL)
        tree = tree->right;
    return tree;
}
複製代碼

查找最小值的代碼

複製代碼
Node* bstree_minimum(BSTree tree)
{
    if (tree == NULL)
        return NULL;

    while(tree->left != NULL)
        tree = tree->left;
    return tree;
}
複製代碼


5. 前驅和後繼

節點的前驅:是該節點的左子樹中的最大節點。
節點的後繼:是該節點的右子樹中的最小節點。

查找前驅節點的代碼

複製代碼
Node* bstree_predecessor(Node *x)
{
    // 如果x存在左孩子,則"x的前驅結點"爲 "以其左孩子爲根的子樹的最大結點"。
    if (x->left != NULL)
        return bstree_maximum(x->left);

    // 如果x沒有左孩子。則x有以下兩種可能:
    // (01) x是"一個右孩子",則"x的前驅結點"爲 "它的父結點"。
    // (01) x是"一個左孩子",則查找"x的最低的父結點,並且該父結點要具有右孩子",找到的這個"最低的父結點"就是"x的前驅結點"。
    Node* y = x->parent;
    while ((y!=NULL) && (x==y->left))
    {
        x = y;
        y = y->parent;
    }

    return y;
}
複製代碼

查找後繼節點的代碼

複製代碼
Node* bstree_successor(Node *x)
{
    // 如果x存在右孩子,則"x的後繼結點"爲 "以其右孩子爲根的子樹的最小結點"。
    if (x->right != NULL)
        return bstree_minimum(x->right);

    // 如果x沒有右孩子。則x有以下兩種可能:
    // (01) x是"一個左孩子",則"x的後繼結點"爲 "它的父結點"。
    // (02) x是"一個右孩子",則查找"x的最低的父結點,並且該父結點要具有左孩子",找到的這個"最低的父結點"就是"x的後繼結點"。
    Node* y = x->parent;
    while ((y!=NULL) && (x==y->right))
    {
        x = y;
        y = y->parent;
    }

    return y;
}
複製代碼

 

6. 插入

插入節點的代碼

複製代碼
static Node* bstree_insert(BSTree tree, Node *z)
{
    Node *y = NULL;
    Node *x = tree;

    // 查找z的插入位置
    while (x != NULL)
    {
        y = x;
        if (z->key < x->key)
            x = x->left;
        else
            x = x->right;
    }

    z->parent = y;
    if (y==NULL)
        tree = z;
    else if (z->key < y->key)
        y->left = z;
    else
        y->right = z;

    return tree;
}

Node* insert_bstree(BSTree tree, Type key)
{
    Node *z;    // 新建結點

    // 如果新建結點失敗,則返回。
    if ((z=create_bstree_node(key, NULL, NULL, NULL)) == NULL)
        return tree;

    return bstree_insert(tree, z);
}
複製代碼

bstree_insert(tree, z)是內部函數,它的作用是:將結點(z)插入到二叉樹(tree)中,並返回插入節點後的根節點。
insert_bstree(tree, key)是對外接口,它的作用是:在樹中新增節點,key是節點的值;並返回插入節點後的根節點。

 

注:本文實現的二叉查找樹是允許插入相同鍵值的節點的!若用戶不希望插入相同鍵值的節點,將bstree_insert()修改爲以下代碼即可。

複製代碼
static Node* bstree_insert(BSTree tree, Node *z)
{
    Node *y = NULL;
    Node *x = tree;

    // 查找z的插入位置
    while (x != NULL)
    {
        y = x;
        if (z->key < x->key)
            x = x->left;
        else  if (z->key > x->key)
            x = x->right;
        else
        {
            free(z); // 釋放之前分配的系統。
            return tree;
        }
    }

    z->parent = y;
    if (y==NULL)
        tree = z;
    else if (z->key < y->key)
        y->left = z;
    else
        y->right = z;

    return tree;
}
複製代碼

 

7. 刪除

刪除節點的代碼

複製代碼
static Node* bstree_delete(BSTree tree, Node *z)
{
    Node *x=NULL;
    Node *y=NULL;

    if ((z->left == NULL) || (z->right == NULL) )
        y = z;
    else
        y = bstree_successor(z);

    if (y->left != NULL)
        x = y->left;
    else
        x = y->right;

    if (x != NULL)
        x->parent = y->parent;

    if (y->parent == NULL)
        tree = x;
    else if (y == y->parent->left)
        y->parent->left = x;
    else
        y->parent->right = x;

    if (y != z) 
        z->key = y->key;

    if (y!=NULL)
        free(y);

    return tree;
}

Node* delete_bstree(BSTree tree, Type key)
{
    Node *z, *node; 

    if ((z = bstree_search(tree, key)) != NULL)
        tree = bstree_delete(tree, z);

    return tree;
}
複製代碼

bstree_delete(tree, z)是內部函數,它的作用是:刪除二叉樹(tree)中的節點(z),並返回刪除節點後的根節點。
delete_bstree(tree, key)是對外接口,它的作用是:在樹中查找鍵值爲key的節點,找到的話就刪除該節點;並返回刪除節點後的根節點。


8. 打印

打印二叉樹的代碼

複製代碼
void print_bstree(BSTree tree, Type key, int direction)
{
    if(tree != NULL)
    {
        if(direction==0)    // tree是根節點
            printf("%2d is root\n", tree->key);
        else                // tree是分支節點
            printf("%2d is %2d's %6s child\n", tree->key, key, direction==1?"right" : "left");

        print_bstree(tree->left, tree->key, -1);
        print_bstree(tree->right,tree->key,  1);
    }
}
複製代碼

print_bstree(tree, key, direction)的作用是打印整顆二叉樹(tree)。其中,tree是二叉樹節點,key是二叉樹的鍵值,而direction表示該節點的類型:

direction爲 0,表示該節點是根節點;
direction爲-1,表示該節點是它的父結點的左孩子;
direction爲 1,表示該節點是它的父結點的右孩子。

 

9. 銷燬二叉樹

銷燬二叉樹的代碼

複製代碼
void destroy_bstree(BSTree tree)
{
    if (tree==NULL)
        return ;

    if (tree->left != NULL)
        destroy_bstree(tree->left);
    if (tree->right != NULL)
        destroy_bstree(tree->right);

    free(tree);
}
複製代碼

 

完整的實現代碼

二叉查找樹的頭文件(bstree.h)

複製代碼
 1 #ifndef _BINARY_SEARCH_TREE_H_
 2 #define _BINARY_SEARCH_TREE_H_
 3 
 4 typedef int Type;
 5 
 6 typedef struct BSTreeNode{
 7     Type   key;                    // 關鍵字(鍵值)
 8     struct BSTreeNode *left;    // 左孩子
 9     struct BSTreeNode *right;    // 右孩子
10     struct BSTreeNode *parent;    // 父結點
11 }Node, *BSTree;
12 
13 // 前序遍歷"二叉樹"
14 void preorder_bstree(BSTree tree);
15 // 中序遍歷"二叉樹"
16 void inorder_bstree(BSTree tree);
17 // 後序遍歷"二叉樹"
18 void postorder_bstree(BSTree tree);
19 
20 // (遞歸實現)查找"二叉樹x"中鍵值爲key的節點
21 Node* bstree_search(BSTree x, Type key);
22 // (非遞歸實現)查找"二叉樹x"中鍵值爲key的節點
23 Node* iterative_bstree_search(BSTree x, Type key);
24 
25 // 查找最小結點:返回tree爲根結點的二叉樹的最小結點。
26 Node* bstree_minimum(BSTree tree);
27 // 查找最大結點:返回tree爲根結點的二叉樹的最大結點。
28 Node* bstree_maximum(BSTree tree);
29 
30 // 找結點(x)的後繼結點。即,查找"二叉樹中數據值大於該結點"的"最小結點"。
31 Node* bstree_successor(Node *x);
32 // 找結點(x)的前驅結點。即,查找"二叉樹中數據值小於該結點"的"最大結點"。
33 Node* bstree_predecessor(Node *x);
34 
35 // 將結點插入到二叉樹中,並返回根節點
36 Node* insert_bstree(BSTree tree, Type key);
37 
38 // 刪除結點(key爲節點的值),並返回根節點
39 Node* delete_bstree(BSTree tree, Type key);
40 
41 // 銷燬二叉樹
42 void destroy_bstree(BSTree tree);
43 
44 // 打印二叉樹
45 void print_bstree(BSTree tree, Type key, int direction);
46 
47 #endif
複製代碼

二叉查找樹的實現文件(bstree.c)

複製代碼
  1 /**
  2  * 二叉搜索樹(C語言): C語言實現的二叉搜索樹。
  3  *
  4  * @author skywang
  5  * @date 2013/11/07
  6  */
  7 
  8 #include <stdio.h>
  9 #include <stdlib.h>
 10 #include "bstree.h"
 11 
 12 
 13 /*
 14  * 前序遍歷"二叉樹"
 15  */
 16 void preorder_bstree(BSTree tree)
 17 {
 18     if(tree != NULL)
 19     {
 20         printf("%d ", tree->key);
 21         preorder_bstree(tree->left);
 22         preorder_bstree(tree->right);
 23     }
 24 }
 25 
 26 /*
 27  * 中序遍歷"二叉樹"
 28  */
 29 void inorder_bstree(BSTree tree)
 30 {
 31     if(tree != NULL)
 32     {
 33         inorder_bstree(tree->left);
 34         printf("%d ", tree->key);
 35         inorder_bstree(tree->right);
 36     }
 37 }
 38 
 39 /*
 40  * 後序遍歷"二叉樹"
 41  */
 42 void postorder_bstree(BSTree tree)
 43 {
 44     if(tree != NULL)
 45     {
 46         postorder_bstree(tree->left);
 47         postorder_bstree(tree->right);
 48         printf("%d ", tree->key);
 49     }
 50 }
 51 
 52 /*
 53  * (遞歸實現)查找"二叉樹x"中鍵值爲key的節點
 54  */
 55 Node* bstree_search(BSTree x, Type key)
 56 {
 57     if (x==NULL || x->key==key)
 58         return x;
 59 
 60     if (key < x->key)
 61         return bstree_search(x->left, key);
 62     else
 63         return bstree_search(x->right, key);
 64 }
 65 
 66 /*
 67  * (非遞歸實現)查找"二叉樹x"中鍵值爲key的節點
 68  */
 69 Node* iterative_bstree_search(BSTree x, Type key)
 70 {
 71     while ((x!=NULL) && (x->key!=key))
 72     {
 73         if (key < x->key)
 74             x = x->left;
 75         else
 76             x = x->right;
 77     }
 78 
 79     return x;
 80 }
 81 
 82 /* 
 83  * 查找最小結點:返回tree爲根結點的二叉樹的最小結點。
 84  */
 85 Node* bstree_minimum(BSTree tree)
 86 {
 87     if (tree == NULL)
 88         return NULL;
 89 
 90     while(tree->left != NULL)
 91         tree = tree->left;
 92     return tree;
 93 }
 94  
 95 /* 
 96  * 查找最大結點:返回tree爲根結點的二叉樹的最大結點。
 97  */
 98 Node* bstree_maximum(BSTree tree)
 99 {
100     if (tree == NULL)
101         return NULL;
102 
103     while(tree->right != NULL)
104         tree = tree->right;
105     return tree;
106 }
107 
108 /* 
109  * 找結點(x)的後繼結點。即,查找"二叉樹中數據值大於該結點"的"最小結點"。
110  */
111 Node* bstree_successor(Node *x)
112 {
113     // 如果x存在右孩子,則"x的後繼結點"爲 "以其右孩子爲根的子樹的最小結點"。
114     if (x->right != NULL)
115         return bstree_minimum(x->right);
116 
117     // 如果x沒有右孩子。則x有以下兩種可能:
118     // (01) x是"一個左孩子",則"x的後繼結點"爲 "它的父結點"。
119     // (02) x是"一個右孩子",則查找"x的最低的父結點,並且該父結點要具有左孩子",找到的這個"最低的父結點"就是"x的後繼結點"。
120     Node* y = x->parent;
121     while ((y!=NULL) && (x==y->right))
122     {
123         x = y;
124         y = y->parent;
125     }
126 
127     return y;
128 }
129  
130 /* 
131  * 找結點(x)的前驅結點。即,查找"二叉樹中數據值小於該結點"的"最大結點"。
132  */
133 Node* bstree_predecessor(Node *x)
134 {
135     // 如果x存在左孩子,則"x的前驅結點"爲 "以其左孩子爲根的子樹的最大結點"。
136     if (x->left != NULL)
137         return bstree_maximum(x->left);
138 
139     // 如果x沒有左孩子。則x有以下兩種可能:
140     // (01) x是"一個右孩子",則"x的前驅結點"爲 "它的父結點"。
141     // (01) x是"一個左孩子",則查找"x的最低的父結點,並且該父結點要具有右孩子",找到的這個"最低的父結點"就是"x的前驅結點"。
142     Node* y = x->parent;
143     while ((y!=NULL) && (x==y->left))
144     {
145         x = y;
146         y = y->parent;
147     }
148 
149     return y;
150 }
151 
152 /*
153  * 創建並返回二叉樹結點。
154  *
155  * 參數說明:
156  *     key 是鍵值。
157  *     parent 是父結點。
158  *     left 是左孩子。
159  *     right 是右孩子。
160  */
161 static Node* create_bstree_node(Type key, Node *parent, Node *left, Node* right)
162 {
163     Node* p;
164 
165     if ((p = (Node *)malloc(sizeof(Node))) == NULL)
166         return NULL;
167     p->key = key;
168     p->left = left;
169     p->right = right;
170     p->parent = parent;
171 
172     return p;
173 }
174 
175 /* 
176  * 將結點插入到二叉樹中
177  *
178  * 參數說明:
179  *     tree 二叉樹的根結點
180  *     z 插入的結點
181  * 返回值:
182  *     根節點
183  */
184 static Node* bstree_insert(BSTree tree, Node *z)
185 {
186     Node *y = NULL;
187     Node *x = tree;
188 
189     // 查找z的插入位置
190     while (x != NULL)
191     {
192         y = x;
193         if (z->key < x->key)
194             x = x->left;
195         else
196             x = x->right;
197     }
198 
199     z->parent = y;
200     if (y==NULL)
201         tree = z;
202     else if (z->key < y->key)
203         y->left = z;
204     else
205         y->right = z;
206 
207     return tree;
208 }
209 
210 /* 
211  * 新建結點(key),並將其插入到二叉樹中
212  *
213  * 參數說明:
214  *     tree 二叉樹的根結點
215  *     key 插入結點的鍵值
216  * 返回值:
217  *     根節點
218  */
219 Node* insert_bstree(BSTree tree, Type key)
220 {
221     Node *z;    // 新建結點
222 
223     // 如果新建結點失敗,則返回。
224     if ((z=create_bstree_node(key, NULL, NULL, NULL)) == NULL)
225         return tree;
226 
227     return bstree_insert(tree, z);
228 }
229 
230 /* 
231  * 刪除結點(z),並返回根節點
232  *
233  * 參數說明:
234  *     tree 二叉樹的根結點
235  *     z 刪除的結點
236  * 返回值:
237  *     根節點
238  */
239 static Node* bstree_delete(BSTree tree, Node *z)
240 {
241     Node *x=NULL;
242     Node *y=NULL;
243 
244     if ((z->left == NULL) || (z->right == NULL) )
245         y = z;
246     else
247         y = bstree_successor(z);
248 
249     if (y->left != NULL)
250         x = y->left;
251     else
252         x = y->right;
253 
254     if (x != NULL)
255         x->parent = y->parent;
256 
257     if (y->parent == NULL)
258         tree = x;
259     else if (y == y->parent->left)
260         y->parent->left = x;
261     else
262         y->parent->right = x;
263 
264     if (y != z) 
265         z->key = y->key;
266 
267     if (y!=NULL)
268         free(y);
269 
270     return tree;
271 }
272 
273 /* 
274  * 刪除結點(key爲節點的鍵值),並返回根節點
275  *
276  * 參數說明:
277  *     tree 二叉樹的根結點
278  *     z 刪除的結點
279  * 返回值:
280  *     根節點
281  */
282 Node* delete_bstree(BSTree tree, Type key)
283 {
284     Node *z, *node; 
285 
286     if ((z = bstree_search(tree, key)) != NULL)
287         tree = bstree_delete(tree, z);
288 
289     return tree;
290 }
291 
292 /*
293  * 銷燬二叉樹
294  */
295 void destroy_bstree(BSTree tree)
296 {
297     if (tree==NULL)
298         return ;
299 
300     if (tree->left != NULL)
301         destroy_bstree(tree->left);
302     if (tree->right != NULL)
303         destroy_bstree(tree->right);
304 
305     free(tree);
306 }
307 
308 /*
309  * 打印"二叉樹"
310  *
311  * tree       -- 二叉樹的節點
312  * key        -- 節點的鍵值 
313  * direction  --  0,表示該節點是根節點;
314  *               -1,表示該節點是它的父結點的左孩子;
315  *                1,表示該節點是它的父結點的右孩子。
316  */
317 void print_bstree(BSTree tree, Type key, int direction)
318 {
319     if(tree != NULL)
320     {
321         if(direction==0)    // tree是根節點
322             printf("%2d is root\n", tree->key);
323         else                // tree是分支節點
324             printf("%2d is %2d's %6s child\n", tree->key, key, direction==1?"right" : "left");
325 
326         print_bstree(tree->left, tree->key, -1);
327         print_bstree(tree->right,tree->key,  1);
328     }
329 }
複製代碼

二叉查找樹的測試程序(btree_test.c)

複製代碼
 1 /**
 2  * C 語言: 二叉查找樹
 3  *
 4  * @author skywang
 5  * @date 2013/11/07
 6  */
 7 
 8 #include <stdio.h>
 9 #include "bstree.h"
10 
11 static int arr[]= {1,5,4,3,2,6};
12 #define TBL_SIZE(a) ( (sizeof(a)) / (sizeof(a[0])) )
13 
14 void main()
15 {
16     int i, ilen;
17     BSTree root=NULL;
18 
19     printf("== 依次添加: ");
20     ilen = TBL_SIZE(arr);
21     for(i=0; i<ilen; i++)
22     {
23         printf("%d ", arr[i]);
24         root = insert_bstree(root, arr[i]);
25     }
26 
27     printf("\n== 前序遍歷: ");
28     preorder_bstree(root);
29 
30     printf("\n== 中序遍歷: ");
31     inorder_bstree(root);
32 
33     printf("\n== 後序遍歷: ");
34     postorder_bstree(root);
35     printf("\n");
36 
37     printf("== 最小值: %d\n", bstree_minimum(root)->key);
38     printf("== 最大值: %d\n", bstree_maximum(root)->key);
39     printf("== 樹的詳細信息: \n");
40     print_bstree(root, root->key, 0);
41 
42     printf("\n== 刪除根節點: %d", arr[3]);
43     root = delete_bstree(root, arr[3]);
44 
45     printf("\n== 中序遍歷: ");
46     inorder_bstree(root);
47     printf("\n");
48 
49     // 銷燬二叉樹
50     destroy_bstree(root);
51 }
複製代碼

 

二叉查找樹的C測試程序

上面的btree_test.c是二叉查找樹的測試程序,它的運行結果如下:

複製代碼
== 依次添加: 1 5 4 3 2 6 
== 前序遍歷: 1 5 4 3 2 6 
== 中序遍歷: 1 2 3 4 5 6 
== 後序遍歷: 2 3 4 6 5 1 
== 最小值: 1
== 最大值: 6
== 樹的詳細信息: 
 1 is root
 5 is  1's  right child
 4 is  5's   left child
 3 is  4's   left child
 2 is  3's   left child
 6 is  5's  right child

== 刪除根節點: 3
== 中序遍歷: 1 2 4 5 6 
複製代碼

 

下面對測試程序的流程進行分析!

(01) 新建"二叉查找樹"root。

 

(02) 向二叉查找樹中依次插入1,5,4,3,2,6 。如下圖所示:

 

(03) 打印樹的信息
插入1,5,4,3,2,6之後,得到的二叉查找樹如下:

前序遍歷結果: 1 5 4 3 2 6 
中序遍歷結果: 1 2 3 4 5 6 
後序遍歷結果: 2 3 4 6 5 1 
最小值是1,而最大值是6。

 

 

(04) 刪除節點3。如下圖所示:

 

(05) 重新遍歷該二叉查找樹。
中序遍歷結果: 1 2 4 5 6

 


發佈了26 篇原創文章 · 獲贊 79 · 訪問量 9萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章