1. 程式人生 > >白話篇:零姿勢實現排序二叉樹的建立與查詢

白話篇:零姿勢實現排序二叉樹的建立與查詢

前篇:

二叉排序樹還有很高比各的名字:二叉查詢樹,二叉搜尋樹。

為什麼這麼稱為呢?

我們來看下二叉樹的定義:

二叉排序樹或者是一棵空樹,或者是具有下列性質的二叉樹

(1)若左子樹不空,則左子樹上所有結點的值均小於它的根結點的值;

(2)若右子樹不空,則右子樹上所有結點的值均大於它的根結點的值;

(3)左、右子樹也分別為二叉排序樹;

(4)沒有鍵值相等的結點。

思想擴充套件:

那麼意思就是:對於一組資料,每個父節點的值小於右兒子的值,並且大於左兒子的值。滿足這樣要求的二叉樹被稱為二叉排序樹。被稱作二叉排序樹是因為通過構造這樣的一顆二叉樹,它上面的每個節點(除了根節點,因為根節點沒有父節點)跟父節點的值是相對有序的。

那麼可以想到的是,如果我們在這顆樹上,想要查詢某個資料是否存在,那麼我們就不必像陣列那樣一個個都比較,或者像二分查詢那樣,一次次折半比較(二分查詢還要要求資料序列有序話,這無疑是很麻煩的)。

對於一顆二叉排序樹來說,我們只需要比較一個數據與當前所訪問的節點,根據結果去確定訪問左兒子還是右兒子,這樣每步的比較操作都會剪去大量的資料,無疑是很方便的進行快速的查詢或者搜尋。從這一點看來,二叉排序樹在查詢資料方面有很大的優勢。這也是為什麼被稱作二叉查詢樹,二叉搜尋樹的原因。

中篇:

二叉排序樹的建立過程模擬:

對於一組資料:

25 18 46 2 53 39 32 4 74 67 60 11

建立相應的二叉排序樹


現在來看18,18小於25,存在於25 的左兒子:


現在來看46,46大於25,所以存在於25的右兒子:


現在來看2,2小於25,查左兒子,2比18小,所以存在於18的左兒子節點:


現在來看53,53大於25,查右兒子,53大於46,所以存在於46的右兒子處:


現在來看39 , 39 大於25 ,查右兒子,39小於46 ,放在46 的左兒子處:


現在來看32,32大於25 ,查右兒子,32小於46,查左兒子,32小於39,放在39 的左兒子處:


現在來看4,4 小於25 ,查左兒子,小於18 查左兒子,大於2,放在2的右兒子處:


對於剩下的資料按照這種方式繼續,最終得到一個二叉排序樹:


由此二叉樹如何查詢資料呢?

比如查詢:74


過程:

74 比25大,查右兒子,74 比46 大,查右兒子,74 比53 大,查右兒子74 == 74 ,查詢結束。總共比較四次。

後篇:

程式碼實現:

#include<stdio.h>
#include<string.h>
#include<malloc.h>
typedef struct T
{
    char data;
    struct T*l, *r;
}T;
int ans;
int pos;
bool flag = false;
void Init(T *&Tree, int e)             // 建立排序二叉樹
{
    if (Tree == NULL)                    //如果當前節點為空,那麼久為當前節點申請空間,並賦值為e
    {
        Tree = (T *)malloc(sizeof(T));
        Tree->data = e;
        Tree->l = Tree->r = NULL;      //賦值結束後它的子節點為空
    }
    else
    {
        if (Tree->data == e)      // 如果當前節點有值,那麼就要進行比較,如果等於當前節點的值,那麼就不做處理。因為二叉排序樹不允許重複節點的出現
            return;
        else if (Tree->data > e)    //如果小於當前節點的值,那麼肯定此資料要建立在當前節點的左節點
        {
            Init(Tree->l, e);
        }
        else if (Tree->data < e)      //如果大於當前節點的值,那麼肯定此資料要建立在當前節點的右節點。
        {
            Init(Tree->r, e);
        }
    }
}
 
void Search(T *Tree,int e)        //搜尋某個資料是否在當前的二叉排序樹中出現
{
    if (Tree != NULL)
    {
        if (Tree->data == e)       //出現
        {
            flag = true;
            ans++;
        }
        else if (Tree->data > e)     //如果當前節點的值大於此資料,那麼次資料只能出現在當前節點的左節點上
        {
            ans++;
            Search(Tree->l, e);
        }
        else if (Tree->data < e)    //如果當前節點的值小於此資料那麼此資料只可能出現在當前節點的右節點上
        {
            ans++;
            Search(Tree->r, e);
        }
    }
}
int main()
{
    T *Tree;
    Tree = NULL;
    int n;
    int a[1024];
    scanf("%d", &n);
    int i;
    for (i = 0; i < n; i++)
    {
        scanf("%d", &a[i]);
        Init(Tree, a[i]);
    }
    int num;
    scanf("%d", &num);
    Search(Tree, num);
    if (flag == false)
    {
        printf("-1");
    }
    else
    {
        printf("%d", ans);
    }
    return 0;
} 

二叉排序樹的特點 

由BST性質可得: 
(1) 二叉排序樹中任一結點x,其左(右)子樹中任一結點y(若存在)的關鍵字必小(大)於x的關鍵字。 
(2) 二叉排序樹中,各結點關鍵字是惟一的。 
注意: 
實際應用中,不能保證被查詢的資料集中各元素的關鍵字互不相同,所以可將二叉排序樹定義中BST性質(1)裡的"小於"改為"大於等於",或將BST性質(2)裡的"大於"改為"小於等於",甚至可同時修改這兩個性質。 
(3) 按中序遍歷該樹所得到的中序序列是一個遞增有序序列。