1. 程式人生 > >二叉排序樹C語言實現一

二叉排序樹C語言實現一

一、定義:

  二叉排序樹(二叉搜尋樹、二叉查詢樹)或者是空樹,或者滿足以下性質:`

(1)若它的左子樹非空,則左子樹上所有記錄的值均小於根記錄的值;

(2)若它的右子樹非空,則右子樹上所有記錄的值均大於根記錄的值;

(3)左、右子樹本身又各是一顆二叉排序樹。

    ——該定義源於《資料結構》李春葆

示例:

                 

二、資料結構:

使用一個連結串列資料結構來表示,節點定義如下:

typedef struct STnode
{
    int key;//資料資訊
    struct STnode *left;//指向左孩子
    struct STnode *right;//指向右孩子
    struct STnode *p;//指向父節點
} STnode;

三、插入操作:

二叉搜尋樹插入操作比較簡單,將欲插入節點my_node從根節點開始比較,用cur_node表示當前被比較的節點,如果my_node.key > cur_node.key,則比較其右孩子,否則比較其左孩子,以此類推,直到找到葉節點為止,將my_node插入(大於所找到的葉節點則作為右孩子插入,小於則作為左孩子插入)。注意:插入操作一定是在葉節點上進的

示例:插入關鍵字為9的節點,先和根節點比較(9>6),故與其右孩子節點比較(9>7),繼續與其右孩子節點比較(9>8),由於該節點為葉節點,且9>8,則將節點作為右孩子插入。

程式碼:

//將節點my_node插入到二叉搜尋樹tree
STnode* STree_Insert(STnode *tree, STnode *my_node)
{
    STnode *parent_node;//指向my_node的父節點
    STnode *cur_node;//指向當前被比較的節點
    
    //樹為空
    if(tree==NULL)
        tree=my_node;
        
    //樹不為空    
    else
    {
        parent_node=NULL;
        cur_node=tree;
        while(cur_node!=NULL) //while迴圈尋找my_node的父節點
        {
            parent_node=cur_node;
            if(my_node->key < cur_node->key)
                cur_node=cur_node->left;
            else
                cur_node=cur_node->right;
        }
        my_node->p=parent_node;
        if(my_node->key < parent_node->key)//插入到左子樹
            parent_node->left=my_node;
        else                               //插入到右子樹  
            parent_node->right=my_node;
    }
    return tree;
}


四、查詢節點

思想比較簡單,直接上程式碼:

//在tree裡查詢節點my_node
STnode *STree_Find(STnode *tree, int my_key)
{
    STnode *cur_node=tree;
    
    if(tree==NULL)
        return NULL;
    else
    {
        while(cur_node->key != my_key)
        {
            if(my_key < cur_node->key)
                cur_node=cur_node->left;
            else
                cur_node=cur_node->right;
        }
        return cur_node;
    }
}

五、返回最小關鍵位元組點

根據二叉排序樹的性質可知,關鍵字最小的節點一定是整棵樹中最左邊的節點所以只需從根節點開始一直尋找left指

針,直到找到NULL為止。

程式碼:

//返回最小關鍵字所在節點
STnode *STree_Min(STnode *tree)
{
    STnode *cur_node=tree;
    
    while(cur_node->left)
    {
        cur_node=cur_node->left;
    }
    return cur_node;
}

六、返回最大關鍵字所在節點

根據二叉排序樹的性質可知,關鍵字最大的節點一定是整棵樹中最右邊的節點所以只需從根節點開始一直尋找right

指標,直到找到NULL為止。

程式碼:

//返回最大關鍵字所在節點
STnode *STree_Max(STnode *tree)
{
    STnode *cur_node=tree;
    
    while(cur_node->right)
    {
        cur_node=cur_node->right;
    }
    return cur_node;
}

七、查詢節點後繼(中序)

中序序列為:左子樹、根、右子樹,則一個節點若存在中序後繼,則該後繼必然位於該節點右邊。

下圖所示二叉排序樹的中序為:2、3、4、6、7、13、15、17、18、20


——該圖源於《演算法導論》

如上所述,節點的後繼一定位於節點右邊,分兩種情況:

1、該節點右孩子不為空:其後繼必然是右子樹上的最左節點(如節點6,後繼為節點9);

2、該節點右孩子為NULL且該節點為其父節點的左孩子:後繼為其父節點(如節點2,後繼為節點3);

3、該節點右孩子為NULL且該節點為其父節點的右孩子:後繼必為該節點所在“子樹”根節點T的父節點,該根節點T

必為其父節點的左孩子,否則繼續往上尋找(如節點13,滿足該子樹根節點為其父節點左孩子的子樹根節點為6,

該節點為其父節點15的左孩子,故該節點後繼為15)

程式碼:

STnode *STree_Successor(STnode *my_node)
{
    STnode *cur_node;
    STnode *successor;
    if(my_node->right)//右孩子不為空
        return STree_Min(my_node->right);
    else              //右孩子為空
    {
        if(my_node==my_node->p->left)//該節點為父節點左孩子
            return my_node->p;
        else//該節點為父節點右孩子
        {
            cur_node=my_node;
            successor=cur_node->p;
            while(successor!=NULL && cur_node!=successor->left)
            {
                cur_node=cur_node->p;
                successor=cur_node->p;
            }
            return successor;
        }
    }
}
八、查詢節點前驅(中序)

查詢節點前驅的演算法和查詢節點後繼的演算法對稱,也分為三種情況,分析分發一樣,直接上程式碼。

程式碼:

STnode *STree_Predecessor(STnode *my_node)
{
    STnode *cur_node;
    STnode *predecessor;
    if(my_node->left)//左孩子不為空
        return STree_Max(my_node->left);
    else              //左孩子為空
    {
        if(my_node==my_node->p->right)//該節點為父節點右孩子
            return my_node->p;
        else//該節點為父節點左孩子
        {
            cur_node=my_node;
            predecessor=cur_node->p;
            while(predecessor!=NULL && cur_node!=predecessor->right)
            {
                cur_node=cur_node->p;
                predecessor=cur_node->p;
            }
            return predecessor;
        }
    }
}

七、刪除節點

刪除節點操作是二叉排序樹所有操作中最複雜最難理解的操作,在下一篇博文

(連結地址http://blog.csdn.net/xiaowang627/article/details/51336272 )中對其進行詳細的圖文描述

同時對所有函式進行測試。