二叉排序树(Binary Sort Tree),又称二叉查找树(Binary Search Tree),亦称二叉搜索树

二叉排序树

二叉排序树(Binary Sort Tree),又称二叉查找树(Binary Search Tree),亦称二叉搜索树。

中文名

二叉排序树

外文名

Binary Sort Tree

别    称

二叉查找树、二叉搜索树

别称外文名

Binary Search Tree

目录

  1. 1 定义
  2. 2 查找
  3. 3 插入删除
  1. 4 插入算法
  2. 5 删除结点
  3. 6 性能分析
  1. 7 优化

定义

二叉排序树或者是一棵空树,或者是具有下列性质的二叉树:

(1)若左子树不空,则左子树上所有结点的值均小于或等于它的根结点的值;

(2)若右子树不空,则右子树上所有结点的值均大于或等于它的根结点的值;

(3)左、右子树也分别为二叉排序树;

查找

步骤:

二叉树 二叉树

若根结点的关键字值等于查找的关键字,成功。

否则,若小于根结点的关键字值,递归查左子树。

若大于根结点的关键字值,递归查右子树。

若子树为空,查找不成功。

平均情况分析(在成功查找两种的情况下):

在一般情况下,设 P(n,i)为它的左子树的结点个数为 i 时的平均查找长度。如图的结点个数为 n = 6 且 i = 3; 则 P(n,i)= P(6, 3) = [ 1+ ( P(3) + 1) * 3 + ( P(2) + 1) * 2 ] / 6= [ 1+ ( 5/3 + 1) * 3 + ( 3/2 + 1) * 2 ] / 6

注意:这里 P(3)、P(2) 是具有 3 个结点、2 个结点的二叉分类树的平均查找长度。 在一般情况,P(i)为具有 i 个结点二叉分类树的平均查找长度。平均查找长度= 每个结点的深度的总和 / 总结点数

(上图应为左子树P(3),右子树P(2))

P(3) = (1+2+2)/ 3 = 5/3

P(2) = (1+2)/ 2 = 3/2∴ P(n,i)= [ 1+ ( P(i) + 1) * i + ( P(n-i-1) + 1) * (n-i-1) ] / n

∴ P(n)=

P(n,i)/ n <= 2(1+I/n)lnn

因为 2(1+I/n)lnn≈1.38logn 故P(n)=O(logn)

插入删除

二叉树

与次优二叉树相对,二叉排序树是一种动态树表。其特点是:树的结构通常不是一次生成的,而是在查找过程中,当树中不存在关键字等于给定值的结点时再进行插入。新插入的结点一定是一个新添加的叶子结点,并且是查找不成功时查找路径上访问的最后一个结点的左孩子或右孩子结点。

插入算法

首先执行查找算法,找出被插结点的父亲结点。

判断被插结点是其父亲结点的左、右儿子。将被插结点作为叶子结点插入。

若二叉树为空。则首先单独生成根结点。

注意:新插入的结点总是叶子结点。

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

struct BiTree {

    int data;

    BiTree *lchild;

    BiTree *rchild;

};

 

//在二叉排序树中插入查找关键字key

BiTree* InsertBST(BiTree *t,int key)

{

    if (t == NULL)

    {

        t = new BiTree();

        t->lchild = t->rchild = NULL;

        t->data = key;

        return t;

    }

 

    if (key < t->data) 

        t->lchild = InsertBST(t->lchild, key);

    else

        t->rchild = InsertBST(t->rchild, key);

 

    return t;

}

 

//n个数据在数组d中,tree为二叉排序树根

BiTree* CreateBiTree(BiTree *tree, int d[], int n)

{

    for (int i = 0; i < n; i++)

        tree = InsertBST(tree, d[i]);

}

删除结点

在二叉排序树删去一个结点,分三种情况讨论:

  1. 若*p结点为叶子结点,即PL(左子树)和PR(右子树)均为空树。由于删去叶子结点不破坏整棵树的结构,则可以直接删除此子结点。

  2. 若*p结点只有左子树PL或右子树PR,此时只要令PL或PR直接成为其双亲结点*f的左子树(当*p是左子树)或右子树(当*p是右子树)即可,作此修改也不破坏二叉排序树的特性。

  3. 若*p结点的左子树和右子树均不空。在删去*p之后,为保持其它元素之间的相对位置不变,可按中序遍历保持有序进行调整,可以有两种做法:

    其一是令*p的左子树为*f的左/右(依*p是*f的左子树还是右子树而定)子树,*s为*p左子树的最右下的结点,而*p的右子树为*s的右子树;

    其二是令*p的直接前驱(或直接后继)替代*p,然后再从二叉排序树中删去它的直接前驱(或直接后继)-即让*f的左子树(如果有的话)成为*p左子树的最左下结点(如果有的话),再让*f成为*p的左右结点的父结点。

    在二叉排序树上删除一个结点的算法如下:

C代码

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

#define Status bool

Status Delete(BiTree*);//必须先声明

Status DeleteBST(BiTree &TParent,BiTree &T, KeyType key)//若二叉排序树T中存在关键字等于key的数据元素时,则删除该数据

                                    //元素,并返回TRUE;否则返回FALSE

{

    if(!T)//不存在关键字等于key的数据元素

        return false;

    else

    {

        if(key == T->data.key)//找到关键字等于key的数据元素                   

            return Delete(Parent_T, T);

        else if(key < T->data.key)

            return DeleteBST(T, T->lchild,key);

        else

            return DeleteBST(T, T->rchild,key);

    }

    return true;

}

Status Delete(BiTree& fp , BiTree&p)//从二叉排序树中删除结点p,并重接它的左或右子树

{

    if(!p->rchild)//右子树空则只需重接它的左子树

    {

        fp->lchild = p->lchild;

        delete(p);

     }

    else if(!p->lchild)//左子树空只需重接它的右子树

    {

        fp->rchild = p->rchild;

        delete(p);

    }

    else//左右子树均不空

    {

            q=p;

            fp->lchild = p->lchild;

            s=p->lchild;//转左

        while(s->rchild)//然后向右到尽头

        {

           q=s;

            s=s->rchild;

        //此时q是s的父结点

        s->rchild=p->rchild; //将s的左子树作为q的右子树

        delete(p);

     }

    return true;

}

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

Java代码

/**

*方法名称:delete()

*方法描述:删除结点

*@param采用递归的方式进行删除

*@returnString

*@Exception

*/

private void deleteNode(BinarySortTree p)

{

    //TODOAuto-generatedmethodstub

    if(p!=null)

    {

        //如果结点有左子树

        /*1。若p有左子树,找到其左子树的最右边的叶子结点r,用该叶子结点r来替代p,把r的左孩子

        作为r的父亲的右孩子。

        2。若p没有左子树,直接用p的右孩子取代它。

        */

        if(p.lChild!=null)

        {

            BinarySortTree r=p.lChild;

            BinarySortTree prev=p.lChild;

                while(r.rChild!=null)

               {

                    prev=r;

                    r=r.rChild;

                }

                p.data=r.data;

            //若r不是p的左子树,p的左子树不变,r的左子树作为r的父结点的右孩子结点

            if(prev!=r)

            {

                prev.rChild=r.lChild;

             }

            else

            {

             //若r是p的左子树,则p的左子树指向r的左子树

            p.lChild=r.lChild;

            }

        }

        else

        {

            p=p.rChild;

        }

    }

}

性能分析

每个结点的C(i)为该结点的层次数。最坏情况下,当先后插入的关键字有序时,构成的二叉排序树蜕变为单支树,树的深度为其平均查找长度(n+1)/2(和顺序查找相同),最好的情况是二叉排序树的形态和折半查找的判定树相同,其平均查找长度和log 2 (n)成正比。

优化

Size Balanced Tree(SBT)

AVL树

红黑树

Treap(Tree+Heap)

这些均可以使查找树的高度为O(log(n))

你可能感兴趣的:(数据结构与算法)