演算法:二叉排序樹及其圖形化(二叉樹尋找)

來源:互聯網
上載者:User

二叉排序樹(BST,Binary Sort Tree)具有這樣的性質:對於二叉樹中的任意節點,如果它有左子樹或右子樹,則該節點的資料成員大於左子樹所有節點的資料成員,且小於右子樹所有節點的資料成員。排序二叉樹的中序遍曆結果是從小到大排列的。

二叉排序樹的尋找和插入比較好理解,主要來看一下刪除時的情況。

如果需要尋找並刪除8-6-8中的37, 51, 73,93這些在二叉排序樹中是葉子的結點,那是很容易的,畢竟刪除它們對整棵樹來說,其他結點的結構並未受到影響。


對於要刪除的結點只有左子樹或只有右子樹的情況,相對也比較好解決。那就是結點刪除後,將它的左子樹或右子樹整個移動到刪除結點的位置即可,可以理解為獨子繼承父業。比8-6-9,就是先刪除35和99兩結點,再刪除58結點的變化圖,最終,整個結構還是一個二叉排序樹。


但是對於要刪除的結點既有左子樹又有右子樹的情況怎麼辦呢?比8-6-10中的47結點若要刪除了,它的兩兒子和子孫們怎麼辦呢?


前人總結的比較好的方法就是,找到需要刪除的結點p的直接前驅(或直接後繼)s,用s來替換結點p,然後再刪除此結點s,8-6-12所示。


下面來看代碼:(參考《linux c 編程一站式學習》

 C++ Code 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
/*************************************************************************
    > File Name: binarysearchtree.h
    > Author: Simba
    > Mail: dameng34@163.com
    > Created Time: Sat 29 Dec 2012 06:05:55 PM CST
 ************************************************************************/

#ifndef BST_H
#define BST_H

typedef struct node *link;
struct node
{
    unsigned char item;
    link left, right;
};

link search(link t, unsigned char key);
link insert(link t, unsigned char key);
link delete(link t, unsigned char key);
void print_tree(link t);

#endif

 C++ Code 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
/*************************************************************************
    > File Name: binarysearchtree.c
    > Author: Simba
    > Mail: dameng34@163.com
    > Created Time: Sat 29 Dec 2012 06:08:08 PM CST
 ************************************************************************/

#include<stdio.h>
#include<stdlib.h>
#include "binarysearchtree.h"

static link make_node(unsigned char item)
{
    link p = malloc(sizeof(*p));
    p->item = item;
    p->left = p->right = NULL;
    return p;
}

static void free_node(link p)
{
    free(p);
}

link search(link t, unsigned char key)
{
    if (!t)
        return NULL;
    if (t->item > key)
        return search(t->left, key);
    if (t->item < key)
        return search(t->right, key);
    /* if (t->item == key) */
    return t;
}

link insert(link t, unsigned char key)
{
    if (!t)
        return make_node(key);
    if (t->item > key) /* insert to left subtree */
        t->left = insert(t->left, key);
    else /* if (t->item <= key), insert to right subtree */
        t->right = insert(t->right, key);
    return t;
}

link delete(link t, unsigned char key)
{
    link p;
    if (!t)
        return NULL;
    if (t->item > key) /* delete from left subtree */
        t->left = delete(t->left, key);
    else if (t->item < key) /* delete from right subtree */
        t->right = delete(t->right, key);
    else   /* if (t->item == key) */
    {
        if (t->left == NULL && t->right == NULL)
        {
            /* 如果此時(遞迴)找到的節點已經是沒有子樹的樹葉則直接釋放空間
                                                          共置t為NULL並由return返回,(即將上一層指向此節點的指標置為NULL),防止野指標 */
            /* if t is a leaf node */
            free_node(t);
            t =  NULL;
        }
        else if (t->left)  /* if t has left subtree */
        {
            /* replace t with the rightmost node in left subtree */
            /* 如果想要刪除的節點有左子樹,則找到左子樹下邊最靠右的節點(已無右子樹) */
            for (p = t->left; p->right; p = p->right);
            t->item = p->item; /* 將左子樹下最靠右的節點值賦予想要刪除的節點 */
            t->left = delete(t->left, t->item); /* 以左子樹的根指標作為根節點,遞迴找到最靠右節點
                                                       若此時節點已無子樹則滿足地一個if條件,直接釋放空間
                                                       若還有左子樹,則按相同原則遞迴進行換值後刪除下一個最靠右節點 */
        }
        /* 當找到要刪除的節點時,優先考慮將左子樹下最靠右節點的值換上來
         * 如果沒有左子樹,則將右子樹下最靠左節點的值換上來 */
        else  /* if t has right subtree */
        {
            /* replace t with the leftmost node in right subtree */
            for (p = t->right; p->left; p = p->left);
            t->item = p->item;
            t->right = delete(t->right, t->item);
        }
    }
    return t;
}

void print_tree(link t)
{
    if (t)
    {
        printf("(");
        printf("%d", t->item);
        print_tree(t->left);
        print_tree(t->right);
        printf(")");
    }
    else
        printf("()");
}

 C++ Code 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
/*************************************************************************
    > File Name: main2.c
    > Author: Simba
    > Mail: dameng34@163.com
    > Created Time: Sat 29 Dec 2012 06:22:57 PM CST
 ************************************************************************/

#include<stdio.h>
#include<stdlib.h>
#include<time.h>
#include "binarysearchtree.h"

#define RANGE 100
#define N 6

void print_item(link p)
{
    printf("%d", p->item);
}

int main(void)
{
    int i, key;
    link root = NULL;
    srand(time(NULL));
    for (i = 0; i < N; i++)
    {
        root = insert(root, rand() % RANGE); /* 第一次迴圈root從NULL變成根節點值,接下去
                                                的迴圈雖然迭代root,但在插入節點過程中root的值始終不變 */
        printf("root = 0x%x\n", (unsigned int)root);
    }

    printf("\t\\tree");
    print_tree(root);
    printf("\n\n");

    while (root)
    {
        key = rand() % RANGE;
        if (search(root, key))
        {
            printf("delete %d in tree\n", key);
            root = delete(root, key); /* root雖然迭代,但返回的仍是先前的值,即根節點的值保持不變
                                         直到全部節點被刪除,root變成NULL即0x0 */
            printf("root = 0x%x\n", (unsigned int)root);

            printf("\t\\tree");
            print_tree(root); /* 傳遞給函數的一直是根節點的值,直到樹清空,root變成NULL */
            printf("\n\n");
        }
    }
    return 0;
}

輸出為:

如果我們使用了The Tree Preprocessor,可以將以括弧展示的排序二叉樹轉換成樹形展示,如


以前此工具可以在 http://www.essex.ac.uk/linguistics/clmt/latex4ling/trees/tree/ 下載,現已找不到連結,我將其上傳到csdn,需要的可以去下載。

http://download.csdn.net/detail/simba888888/5334093


最後提一下,我們希望構建出來的二叉排序樹是比較平衡的,即其深度與完全二叉樹相同,那麼尋找的時間複雜度研究度O(logn),近似於折半尋找,

但如果出現構造的樹嚴重不平衡,如完全是左斜樹或者右斜樹,那麼尋找時間複雜度為O(n),近似於順序尋找。那如何讓二叉排序樹平衡呢?

事實上還有一種平衡二叉樹(AVL樹),也是一種二叉排序樹,其中每個結點的左子樹和右子樹的高度差至多等於1。



聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.