码迷,mamicode.com
首页 > 其他好文 > 详细

AVL树

时间:2016-05-12 12:05:27      阅读:297      评论:0      收藏:0      [点我收藏+]

标签:

平衡二叉树:

       AVL树属于二叉查找树,在AVL树中任何节点的两个子树的高度最大差别为一,所以它也被称为高度平衡树。查找、插入和删除在平均和最坏情况下都是Ologn),增加和删除可能需要通过一次或多次树旋转来重新平衡这个树。

二叉查找树的查找和插入操作在最坏情况下复杂度为O(N),而AVL树最坏时仍然为O(lgN)

   平衡二叉树(Self-Balancing Binary Search Tree Height-Balancing Binary Search Tree,是一种二叉排序树,其中每一个结点的左子树和右子树的高度差至多等于1

   从平衡二叉树的英文名字,你也可以体会到,它是一种高度平衡的二叉排序树,那什么叫做高度平衡呢?意思是说,要么它是一棵空树,要么它的左子树和右子树都是平衡二叉树,且左子树和右子树的深度之差的绝对值不超过1。我们将二叉树上结点的左子树深度减去右子树深度的值称为平衡因子BFBalanceFactor),那么平衡二叉树上所有结点的平衡因子只可能是-101。只要二叉树上有一个结点的平衡因子的绝对值大于1,则该二叉树就是不平衡的。

       看图8.7.2,为什么图1是平衡二叉树,而图2却不是呢?这里就是考查我们对平衡二叉树的定义的理解,它的前提首先是一棵二叉排序树,右上图的5958大,却是58的左子树,这是不符合二叉排序树的定义的。图3不是平衡二叉树的原因就在于,结点58的左子树高度为2,而右子树为空,一者差大于了绝对值1,因此它也不是平衡的。而经过适当的调整后的图4,它就符合了定义,因此它是平衡二叉树。

技术分享

 

       距离插入结点最近的,且平衡因子的绝对值大于1的结点为根的子树,我们称为最小不平衡子树。图873,当新插入结点37时,距离它最近的平衡因子绝对值超过1的结点是58(即它的左子树高度2减去右子树高度0),所以从58开始以下的子树为最小不平衡子树。

 

 

技术分享

 

        以58为结点,左子树深度与右子树相差大于1,需要旋转平衡,在以58的左子树47为结点,右边深度不大于左边深度,所以只需要左单旋就能是二叉树重新平衡,平衡步骤,将47的右子树保存在58的左子树中,47的右子树指向58,原本指向58的指针指向47。

 

平衡二叉树实现原理

        平衡二又树构建的基本思想就是在构建二叉排序树的过程中,每当插入一个结点时,先检查是否因插入而破坏了树的平衡性,若是,则找出最小不平衡子树。在保持二叉排序树特性的前提下,调整最小不平衡子树中各结点之间的链接关系,进行相应的旋转,使之成为新的平衡子树。

1.左单旋:(左左旋转)

         左子树的左边节点插入新结点导致左子树失衡就需要左左旋转即左单旋,将2的右子树保存在3的左子树中(此处没有),将2的右子树指针指向3结点,将原本指向3结点的指针指向2

        什么时候需要左单旋?当插入的新结点或删除结点,使得左边子树失衡的情况,且以失衡点的左子树为结点,其左边高度大于或等于右边高度,就需要左单旋,否则就转化为情况4,需要右左旋(先以失衡点的左子树为结点右旋,在以失衡点为结点左旋)

技术分享

 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

//************************************

//左单旋

//        50(K2)                40(K1)

//       /                     /  \

//      40(K1)         ->  (N)30  50(K2)

//     /  \                       /

// (N)30   44(N2)                44(N2)

//  1.K1的右子树保存在K2的左子树中

//  2.K1的右子树置为K2

//  3.将原本指向K2的指针指向K1

//************************************

// 函数名称:    SingRotateLeft

// 功能:       左单旋

// FullName:    CBalanceBinaryTree::SingRotateLeft

// 继承关系:    private

// 返回值:      void

// 参数:        PTREE_NODE & pNodeK2

//************************************

void  CBalanceBinaryTree::SingRotateLeft(PTREE_NODE &pNodeK2)   //左单旋

{

    PTREE_NODE pNodeK1 = pNodeK2->pLchild;  //定义临时指针变量K1指向K2的左子树

    pNodeK2->pLchild = pNodeK1->pRchild;    //将指向K2左子树的指针指向K1的右子树

    pNodeK1->pRchild = pNodeK2;             //将指向K1右子树的指针指向K2

    pNodeK2 = pNodeK1;                      //将原本指向K2的指针指向K1

}

 

2.右单旋(右右旋)

      

       右子树的右边结点失衡,插入新结点导致结点处失衡,左边深度为0,右边深度为2因此结点处左右子树高度差大于1失去平衡,因此需要以做结点旋转,此种情况需要右单旋,将的左子树保存在的右子树中(此处为空),将结点的左子树指向结点,将原本指向结点的指针指向结点。

        什么时候需要做右单旋,当插入值大于根结点,往右子树中插入值时,新插入结点造成右子树失衡的情况,以失衡点做判断其右边高度与左边相差大于1时,就需要旋转平衡,在以失衡点的右子树为结点,如果其右边高度大于或等于左边,仅需要以失衡点为结点做有单旋;否则就转换成情况3,失衡点右子树的左边高度大于右边的情况,就需做左右旋(首先以失衡点的右子树为结点左旋,再以失衡点为结点右旋)。

 

技术分享

 

 C++ Code 原理

1

2

3

4

5

6

7

8

9

10

//************************************

//右单旋

//        40(K2)                  50(K1)

//         \                     / \

//          50(K1)       -> (K2)40  70(N2)

//         / \                   \

//     (N)49  70(N2)             49(N)

//  1.K1的左子树保存在K2的右子树中

//  2.K1的左子树置为K2

//  3.将原本指向K2的指针指向K1

//************************************

 C++ Code 代码

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

//************************************

// 函数名称:      SingRotateRight

// 功能:          右单旋

// FullName:      CBalanceBinaryTree::SingRotateRight

// 继承关系:      private 

// 返回值:         void

// 参数:          PTREE_NODE & pNodeK2

//************************************

void  CBalanceBinaryTree::SingRotateRight(PTREE_NODE &pNodeK2)   //右单旋

{

    PTREE_NODE pNodeK1 = pNodeK2->pRchild; //定义零时指针变量K1指向k2的右子树

      pNodeK2->pRchild = pNodeK1->pLchild; //指向K2右子树的指针指向K1的左子树

      pNodeK1->pLchild = pNodeK2;          //指向K1左子树的指针指向K2

               pNodeK2 = pNodeK1;          //指向K2的指针指向K1???

}

3.左右旋(右子树的左边结点)

       右子树的左边插入新结点造成右子树失衡,如下图,新插入结点18,造成右子树结点15的左子树高度为0右子树高度为2,高度差大于1了,故造成结点15失衡,此种情况就需要左右旋。

        首先以15结点的右子树20做结点左单旋,左单旋步骤为:将18结点的右子树保存在20结点的左子树中(此处为空),再将18结点的右子树指针指向20结点,最后将原本指向20结点的指针指向18结点完成左单旋。

        其次将15结点右单旋,右单旋步骤为:将18结点的左子树保存在15结点的右子树中,再将18结点的左子树指针指向15结点(此处为空),最后将原本指向15结点的指针指向18结点,完成右单旋至此整棵树重新平衡。

 

技术分享

代码实现:

 C++ Code 

1

2

3

4

5

6

7

8

9

10

11

12

13

void  CBalanceBinaryTree::DoubleRotateLR(PTREE_NODE &pNodeK3)   //左右旋

{

    //左右旋

    //    90 (K3)             90 (K3)                 95

    //     \                   \                     /  \

    //     100      左旋       95        右旋    90(K3)  100

    //     /       ——>    /  \      ——>       \   

    //   95                  N   100                   N      

    //   /

    //  N

    SingRotateLeft(pNodeK3->pRchild);//以右孩子树(100)为结点左旋

    SingRotateRight(pNodeK3);//以当前结点右旋

}

 

4.右左旋(左子树的右边结点)

      左子树的右边结点新插入结点导致左子树失衡,如下图新插入结点3导致结结点5失衡,所以先要将失衡点的左子树2作为结点右单旋,再将失衡结点5左单旋。

      结点2右单旋步骤为:将结点3的左子树保存在结点2的右子树中(此处为空),然后将3结点的左子树指向2结点,最后将原本指向2结点的指针指向3结点完成右单旋

       然后在将结点5做左单旋步骤为:将结点3的右子树保存在结点5的左子树中(此处为空),然后将结点将结点3的右子树指针指向结点5,最后将结点原本指向结点5的指针指向结点3完成左单旋,至此整棵树重新平衡。

 

 

技术分享

代码实现:

 C++ Code 

1

2

3

4

5

6

7

8

9

10

11

12

13

void  CBalanceBinaryTree::DoubleRotateRL(PTREE_NODE &pNodeK3)   //右左旋

{

    //右左旋

    //    90(K3)             90(K3)                  85

    //    /                  /                      /  \

    //   80       右旋       85        左旋        80   90(K3)

    //    \      ——>    /  \      ——>           /

    //    85              80   N                      N    

    //      \

    //       N

    SingRotateRight(pNodeK3->pLchild);//以左孩子树(80)为结点右单旋旋

    SingRotateLeft(pNodeK3);//以当前结点左单旋

}

        理解了什么是单旋和双旋就需要判断什么时候做旋转,选择什么旋转来是二叉树重新平衡,我们由以上的理解首先可以分两种情况。第一种是左子树失衡,第二种是右子树失衡。下面分两种情况做判断选择合理的旋转方式使二叉树重新平衡。

左子树失衡(相对根结点):左子树失衡又分为两种情况(左单旋和右左旋),下面再细分两种情况,首先是以左子树为结点,其左子树的与右子树的高度差大于1,则该结点失衡需要旋转平衡,再判断,如果该左子树的左边深度大于或等于右边深度,则只需要左单旋(上述情况1);如果右边深度大于左边深度则需要右左旋(上述情况4)。

右子树失衡(相对根结点):右子树失衡也分为两种情况(右单旋和左右旋),下面分两种情况判断;

首先以右子树为结点,其右子树与左子树的高度差大于1,该结点失衡需要旋转平衡,再判断其右子树

往下的如果右边的深度大于或等于左边深度,则需要右单旋(上述情况2);如果左边深度大于或等于右边深度则需要左右旋(上述情况3)。

有以上理论就可以进行二叉树的插入数据和删除数据操作了:

 

数据插入:

跟二叉排序树的插入基本一样,不过再插入数据以后需要做判断,是否平衡,下面看代码。

 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

//************************************

// 函数名称:    Insert

// 功能:       插入数据(对外接口)

// FullName:    CBalanceBinaryTree::Insert

// 继承关系:    public

// 返回值:       bool

// 参数:        int nData(插入数据)

//************************************

bool CBalanceBinaryTree::Insert(int nData)//插入数据(对外接口)

{

    if (!Insert(m_TreeNode, nData))

    {

        //插入失败

        return false;

    }

    //插入成功

    return true;

}

//************************************

// 函数名称:    Insert

// 功能:       插入数据

// FullName:    CBalanceBinaryTree::Insert

// 继承关系:    private

// 返回值:       bool

// 参数:        PTREE_NODE & TreeNode()

// 参数:        int nData(数据)

//************************************

bool CBalanceBinaryTree::Insert(PTREE_NODE &TreeNode, int nData)//插入数据

{

    if (!TreeNode)//查找到结点为空就插入结点

    {

        //新添加结点

        TreeNode = new TREE_NODE;

        memset(TreeNode, 0sizeof(TREE_NODE));

        TreeNode->Data = nData;

        m_nCount++;

        return true;

    }

    //判断是否小于根若小于根则从左孩子树开始查找寻找插入点

    if (nData < TreeNode->Data)

    {

        Insert(TreeNode->pLchild, nData);

        //插入递归返回判断是否符合平衡二叉树条件(优点能找到最小的不平衡结点)

        if(2 == (GetDepth(TreeNode->pLchild) - GetDepth(TreeNode->pRchild)))

        {

            //左子树与右子树深度差大于1的情况需要旋转(分左单旋和右左旋)

 

            if (GetDepth(TreeNode->pLchild->pRchild) > GetDepth(TreeNode->pLchild->pLchild))

            {

                //如果以左孩子树为结点,其右孩子树深度大于左孩子树深度则需要右左旋(情况4

                DoubleRotateRL(TreeNode);  //右左旋

            }

            else

            {

                //如果其右孩子树不大于左孩子树深度则仅需要左单旋就行了(情况1)

                SingRotateLeft(TreeNode); //左单旋

            }

        }

    }

    //判断是否大于根如果成立就从右孩子树一侧开始查找插入点

    else if (nData > TreeNode->Data)

    {

        Insert(TreeNode->pRchild, nData);

        //插入递归返回判断新插入叶子后是否符合平衡二叉树条件(优点能找到最小不平衡点)

        if(2 == (GetDepth(TreeNode->pRchild) - GetDepth(TreeNode->pLchild)))

        {

            //右子树与左子树的深度差大于1的情况就需要旋转(分右单旋和左右旋)

 

            if (GetDepth(TreeNode->pRchild->pLchild) >  GetDepth(TreeNode->pRchild->pRchild))

            {

                //以右子树为结点,其左子树深度若大于右子树需要右左旋(情况3

                DoubleRotateLR(TreeNode);  //左右旋

            }

            else

            {

                //以右子树为结点,其左子树深度不大于右子树仅需要右旋(情况2

                SingRotateRight(TreeNode); //右单旋

            }

        }

    }

    else   //等于根(即有和插入新结点的值相等的情况就直接返回)

        return false;

}

数据删除:

        俗话说请神容易送神难,平衡二叉树的真正难点是在数据的删除。基本原理是在数据删除,数据的删除方法跟添加方法也类似,当删除一个结点的时候,可能会引起祖先结点的失衡,所以在每次“结点”回退的时候计算结点高度,判断是否需要旋转平衡。

此处注意两种情况,如果删除的是叶子结点,返回后也要判断,是否平衡;另外一种情况是删除的是非叶子结点,就要用左子树的最大值,或者右子树的最小值替换需要删除的结点,如果用左子树的最大值替换,就要以原本删除结点的左子树为结点向下查找删除原有的最大值结点;如果用右子树最小值替换就需要以原本删除结点的右子树为结点向下查找删除原有的最小值结点。删除结点后,逐层递归返回判断二叉树的平衡性。

 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

108

109

110

111

112

113

114

115

116

117

118

119

120

121

122

123

124

125

126

127

128

129

130

131

132

133

134

135

136

137

138

139

140

141

142

143

144

145

146

147

148

149

150

151

152

153

//************************************

// 函数名称:    DeleteNode

// 功能:       删除数据(对外接口)

// FullName:    CBalanceBinaryTree::DeleteNode

// 继承关系:    public

// 返回值:      bool

// 参数:       int nData(需要删除的数据)

//************************************

bool CBalanceBinaryTree::DeleteNode(int nData)

{

    if (!DeleteNode(m_TreeNode, nData))

    {

        return false;

    }

    m_nCount--;

    return true;

}

//************************************

// 函数名称:    DeleteNode

// 功能:      数据删除

// FullName:  CBalanceBinaryTree::DeleteNode

// 继承关系:  private

// 返回值:    bool

// 参数:      PTREE_NODE & TreeNode(树)

// 参数:      int nData(需要删除的数据)

//************************************

bool CBalanceBinaryTree::DeleteNode(PTREE_NODE &TreeNode, int nData)

{

    //用来判断是否删除成功(例如,在树中查找不到数据就返回false

    bool bSuccess = true;

    if (!TreeNode)//先决条件

    {

        //如果树为空就直接返回

        return false;

    }

    //是否找到数据

    if (nData == TreeNode->Data)

    {

        //1.删除的是叶子节点就直接删除

        if (!TreeNode->pLchild && !TreeNode->pRchild)//递归出口

        {

            delete TreeNode;

            TreeNode = nullptr;

            return true;

        }

        //2.有左子树(默认获取左子树中的最大值来替换当前结点)

        else if (TreeNode->pLchild)

        {

            //定义一个变量指向左子树中的最大值结点

            PTREE_NODE pMaxData = GetMaxpLchild(TreeNode->pLchild);

            //把当前结点的值替换为最大值结点的值

            TreeNode->Data = pMaxData->Data;

            //再以当前结点的左子树为结点,向下删除最大值结点

            bSuccess = DeleteNode(TreeNode->pLchild, pMaxData->Data);

            //删除结点后逐层递归返回判断二叉树是否平衡

            if(2 == (GetDepth(TreeNode->pLchild) -

                     GetDepth(TreeNode->pRchild)))

            {

                //如果结点的左子树的高度比右子树的高度大于1的情况就需要旋转平衡(右左旋或左单旋)

                if (GetDepth(TreeNode->pLchild->pRchild) >

                        GetDepth(TreeNode->pLchild->pLchild))

                {

                    //第一种情况,以左子树为结点,其右边高度大于左边就需要右左旋

                    DoubleRotateRL(TreeNode);  //右左旋

                }

                else

                {

                    //第二种情况,以左子树为结点,如果右边高度不大于左边高度仅左单旋就可以了

                    SingRotateLeft(TreeNode); //左单旋

                }

            }

        }

        //3.没有左子树有右子树(获取右子树中的最小值,以右子树为结点向左查找到尽头就是最小值)

        else if(TreeNode->pRchild)

        {

            //定义一个变量指向右子树中的最小值

            PTREE_NODE pMinData = GetMinpRchild(TreeNode->pRchild);

            //用右子树中的最小值替换需要删除结点的值

            TreeNode->Data = pMinData->Data;

            //再以右子树为结点删除原有最小值

            bSuccess = DeleteNode(TreeNode->pRchild, pMinData->Data);

 

            //删除结点后逐层递归返回二叉树平衡条件判断判断

            if(2 == (GetDepth(TreeNode->pRchild) -

                     GetDepth(TreeNode->pLchild)))

            {

                //如果结点的右子树与左子树的高度差大于1就需要旋转平衡(左右旋和右单旋)

                if (GetDepth(TreeNode->pRchild->pLchild)  >

                        GetDepth(TreeNode->pRchild->pRchild))

                {

                    //第一种情况,以右子树的结点,其左边的高度大于右边就需要左右旋

                    DoubleRotateLR(TreeNode);  //左右旋

                }

                else

                {

                    //第二种情况,以右子树为结点,其左边高度不大于右边高度时仅右旋就可以了

                    SingRotateRight(TreeNode); //右单旋

                }

            }

        }

    }

    else if (nData < TreeNode->Data)

    {

        //如果删除的数据小于当前结点的数据就,从当前结点的左子树向下递归查找删除

        bSuccess = DeleteNode(TreeNode->pLchild, nData);

        //删除的是左子树的叶子结点,只能造成右子树不平衡,所以需要判断右子树失衡,左右旋和右单旋的情况

        if(2 == (GetDepth(TreeNode->pRchild) -

                 GetDepth(TreeNode->pLchild)))

        {

            //如果结点的右子树与左子树的高度差大于1就需要旋转平衡(左右旋和右单旋)

            if (GetDepth(TreeNode->pRchild->pLchild)  >

                    GetDepth(TreeNode->pRchild->pRchild))

            {

                //第一种情况,以右子树的结点,其左边的高度大于右边就需要左右旋

                DoubleRotateLR(TreeNode);  //左右旋

            }

            else

            {

                //第二种情况,以右子树为结点,其左边高度不大于右边高度时仅右旋就可以了

                SingRotateRight(TreeNode); //右单旋

            }

        }

    }

    else if (nData > TreeNode->Data)

    {

        //如果删除的数据大于当前结点的数据就,从当前结点的右子树向下递归查找删除

        bSuccess = DeleteNode(TreeNode->pRchild, nData);

 

        //删除的是右子树的叶子结点,只能造成左子树不平衡,所以需要判断左子树失衡,右左旋和左单旋的情况

        if(2 == (GetDepth(TreeNode->pLchild) -

                 GetDepth(TreeNode->pRchild)))

        {

            //如果结点的左子树的高度比右子树的高度大于1的情况就需要旋转平衡(右左旋或左单旋)

            if (GetDepth(TreeNode->pLchild->pRchild) >

                    GetDepth(TreeNode->pLchild->pLchild))

            {

                //第一种情况,以左子树为结点,其右边高度大于左边就需要右左旋

                DoubleRotateRL(TreeNode);  //右左旋

            }

            else

            {

                //第二种情况,以左子树为结点,如果右边高度不大于左边高度仅左单旋就可以了

                SingRotateLeft(TreeNode); //左单旋

            }

        }

    }

    if (bSuccess == false)//判断是否删除成功

        return bSuccess;

    else

        return true;

}

获取左子树最大值结点:

 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

//************************************

// 函数名称:   GetMaxpLchild

// 功能:       获取左子树的最大值节点

// FullName:   CBalanceBinaryTree::GetMaxpLchild

// 继承关系:    private

// 返回值:     CBalanceBinaryTree::PTREE_NODE

// 参数:       PTREE_NODE TreeNode

//************************************

CBalanceBinaryTree::PTREE_NODE CBalanceBinaryTree::GetMaxpLchild(PTREE_NODE TreeNode)       

{

    //左子树向右右到尽头就是最大值结点

    ////1.(递归方法)

    //if (!TreeNode->pRchild)//直到右子树为空结束递归

    //{

    //  return TreeNode;

    //}

    //GetMaxpLchild(TreeNode->pRchild);

    //

 

    //2.(非递归方法)

    while(TreeNode->pRchild != NULL)//直到右子树为空结束

    {

        TreeNode = TreeNode->pRchild;

    }

    return TreeNode;

}

获取右子树最大值结点:

 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

//************************************

// 函数名称:    GetMinpRchild

// 功能:      //获取右子树的最小值节点

// FullName: CTreeClass::GetMinpRchild

// 继承关系:    private

// 返回值:     CTreeClass::PTREE_NODE

// 参数:      PTREE_NODE TreeNode

//************************************

CBalanceBinaryTree::PTREE_NODE CBalanceBinaryTree::GetMinpRchild(PTREE_NODE TreeNode)       

//获取右子树的最小值节点

{

    //右子树向左左到尽头就是最小值结点

    //1.(递归方法)

    //if (!TreeNode->pLchild)//直到左子树为空结束递归

    //{

    //  return TreeNode;

    //}

    //GetMinpRchild(TreeNode->pLchild);

    //2.(非递归方法)

    while (TreeNode->pLchild != NULL)

    {

        TreeNode = TreeNode->pLchild;

    }

    return TreeNode;

}

获取树的深度:

 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

//************************************

// 函数名称: GetDepth

// 功能:      //获得树的深度

// FullName:CTreeClass::GetDepth

// 继承关系: private

// 返回值 int

// 参数:    PTREE_NODE TreeNode

//************************************

int CBalanceBinaryTree::GetDepth(PTREE_NODE TreeNode)    //获得树的深度

{

    if (!TreeNode)//空树就直接返回(做递归返回条件)

    {

        return 0;

    }

    //获取左子树深度

    int LChildDepth = GetDepth(TreeNode->pLchild);

    //获取右子树深度

    int RChildDepth = GetDepth(TreeNode->pRchild);

    //判断最大深度

    int MaxDepth = LChildDepth > RChildDepth ? LChildDepth : RChildDepth;

    //如果左或右子树有元素则MaxDepth自增1

    MaxDepth++;

    return MaxDepth;

}

其他功能基本与二叉排序树的的代码一致。下面附上完整代码:

 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

108

109

110

111

112

113

114

115

116

117

118

119

120

121

122

123

124

125

126

127

128

129

130

131

132

133

134

135

136

137

138

139

140

141

142

143

144

145

146

147

148

149

150

151

152

153

154

155

156

157

158

159

160

161

162

163

164

165

166

167

168

169

170

171

172

173

174

175

176

177

178

179

180

181

182

183

184

185

186

187

188

189

190

191

192

193

194

195

196

197

198

199

200

201

202

203

204

205

206

207

208

209

210

211

212

213

214

215

216

217

218

219

220

221

222

223

224

225

226

227

228

229

230

231

232

233

234

235

236

237

238

239

240

241

242

243

244

245

246

247

248

249

250

251

252

253

254

255

256

257

258

259

260

261

262

263

264

265

266

267

268

269

270

271

272

273

274

275

276

277

278

279

280

281

282

283

284

285

286

287

288

289

290

291

292

293

294

295

296

297

298

299

300

301

302

303

304

305

306

307

308

309

310

311

312

313

314

315

316

317

318

319

320

321

322

323

324

325

326

327

328

329

330

331

332

333

334

335

336

337

338

339

340

341

342

343

344

345

346

347

348

349

350

351

352

353

354

355

356

357

358

359

360

361

362

363

364

365

366

367

368

369

370

371

372

373

374

375

376

377

378

379

380

381

382

383

384

385

386

387

388

389

390

391

392

393

394

395

396

397

398

399

400

401

402

403

404

405

406

407

408

409

410

411

412

413

414

415

416

417

418

419

420

421

422

423

424

425

426

427

428

429

430

431

432

433

434

435

436

437

438

439

440

441

442

443

444

445

446

447

448

 

#pragma once

#include"memory.h"

class CBalanceBinaryTree

{

    typedef struct _TREE_NODE

    {

         int Data;                   //数据

        _TREE_NODE * pLchild;        //左子树

        _TREE_NODE * pRchild;        //右子树

    }TREE_NODE, *PTREE_NODE;

 

public:

    CBalanceBinaryTree()

    {

        m_TreeNode=NULL;

        m_nCount = 0;

    };

    ~CBalanceBinaryTree(){};

 

public:

    

    bool IsEmpty();                 //判断树是否为空

    bool Insert(int nData);         //插入数据

    bool DeleteNode(int nData);     //删除数据

    bool DestroyTree();             //销毁一颗树

    void ClearTree();               //清空一棵树 

    int  GetDepth();                //获得树的深度        

    void PreOrderTraverse();        //前序遍历整棵树   

    void InOrderTraverse();         //中序遍历整棵树   

    void PostOrderTraverse();       //后序遍历整棵树

 

private:

    

    bool  Insert(PTREE_NODE &pNode, int nData);     //插入数据  

    bool  AddNode(PTREE_NODE &pNode, int nData);    //添加节点  

    bool  DeleteNode(PTREE_NODE &pNode, int nData); //删除数据  

    bool  DestroyTree(PTREE_NODE pNode);            //销毁一颗树 

    void  ClearTree(PTREE_NODE &pNode);             //清空一棵树                     

    int   GetDepth(PTREE_NODE pNode);               //获得树的深度    

    void  PreOrderTraverse(PTREE_NODE pNode);       //前序遍历整棵树   

    void  InOrderTraverse(PTREE_NODE pNode);        //中序遍历整棵树   

    void  PostOrderTraverse(PTREE_NODE pNode);      //后序遍历整棵树   

    PTREE_NODE GetMaxpLchild(PTREE_NODE pNode);     //获取左子树的最大值节点   

    PTREE_NODE GetMinpRchild(PTREE_NODE pNode);     //获取右子树的最小值节点

 

    //////////////////////////////////////////////////////////////////////////

    

    void LChildRotate(PTREE_NODE& pNode);   //添加/删除左子树时先平衡  

    void RChildRotate(PTREE_NODE& pNode);   //添加/删除右子树时先平衡  

    void SingRotateLeft(PTREE_NODE &pK2);   //左单旋   

    void SingRotateRight(PTREE_NODE &pK2);  //右单旋   

    void DoubleRotateLR(PTREE_NODE &pK);    //左右旋   

    void DoubleRotateRL(PTREE_NODE &pK);    //右左旋

 

private:

    PTREE_NODE m_TreeNode;       //根节点

    int        m_nCount;         //无素个数

};

//************************************

// 函数名称:    SingRotateLeft

// 功能:      左单旋

// FullName:CBalanceBinaryTree::SingRotateRight

// 继承关系:    private 

// 返回值:     void

// 参数:      PTREE_NODE & pNodeK2

//************************************

//左单旋

//        50(K2)                40(K1)

//       /                     /  \

//      40(K1)         ->  (N)30  50(K2) 

//     /  \                      /

// (N)30   44(N2)               44(N2)

//  1.K1的右子树保存在K2的左子树中

//  2.K1的右子树置为K2

//************************************

void  CBalanceBinaryTree::SingRotateLeft(PTREE_NODE &pNodeK2)   //左单旋

{

    PTREE_NODE pNodeK1 = pNodeK2->pLchild;//定义临时指针变量指向K2的左子树

      pNodeK2->pLchild = pNodeK1->pRchild;//将指向K2左子树的指针指向K1的右子树

      pNodeK1->pRchild = pNodeK2;         //将指向K1右子树的指针指向K2

               pNodeK2 = pNodeK1;         //将原本指向K2的指针指向K1

}

//************************************

// 函数名称:    SingRotateRight

// 功能:      右单旋

// FullName:CBalanceBinaryTree::SingRotateLeft

// 继承关系:    private 

// 返回值:     void

// 参数:      PTREE_NODE & pNodeK2

//************************************

//右单旋

//        40(K2)                  50(K1)

//         \                     / \

//          50(K1)       -> (K2)40  70(N2)

//         / \                   \

//     (N)49  70(N2)             49(N)

//  1.K1的左子树保存在K2的右子树中

//  2.K1的左子树置为K2

//************************************

void  CBalanceBinaryTree::SingRotateRight(PTREE_NODE &pNodeK2)  //右单旋

{

    PTREE_NODE pNodeK1 = pNodeK2->pRchild; //定义零时指针变量指向k2的右子树

      pNodeK2->pRchild = pNodeK1->pLchild; //指向K2右子树的指针指向K1的左子树

      pNodeK1->pLchild = pNodeK2;          //指向K1左子树的指针指向K2

               pNodeK2 = pNodeK1;          //指向K2的指针指向K1

}

void  CBalanceBinaryTree::DoubleRotateRL(PTREE_NODE &pNodeK3)   //右左旋

{

    //右左旋

    //    90                 90                     85

    //    /                  /                     /  \

    //   80       右旋       85        左旋        80   90

    //    \      ——>    /  \      ——>           /

    //    85              80   N                      N    

    //      \

    //       N

    SingRotateRight(pNodeK3->pLchild);//以左孩子树为结点右旋

    SingRotateLeft(pNodeK3);//以当前结点左旋

}

void  CBalanceBinaryTree::DoubleRotateLR(PTREE_NODE &pNodeK3)   //左右旋

{

    //左右旋

    //    90                   90                     95

    //     \                   /                     /  \

    //     100      左旋       95        右旋        90   100

    //     /       ——>    /  \      ——>       \   

    //   95                 N   100                   N      

    //   /

    //  N

    SingRotateLeft(pNodeK3->pRchild);//以右孩子树为结点左旋

    SingRotateRight(pNodeK3);//以当前结点右旋

}

//************************************

// 函数名称:    Insert

// 功能:      插入数据(对外接口)

// FullName:CBalanceBinaryTree::Insert

// 继承关系:    public 

// 返回值:     bool

// 参数:      int nData(插入数据)

//************************************

bool CBalanceBinaryTree::Insert(int nData)      //插入数据(对外接口)

{

    if (!Insert(m_TreeNode,nData))

    {//插入失败

        return false;

    }

    return true;

}

//************************************

// 函数名称:    Insert

// 功能:      插入数据

// FullName:CBalanceBinaryTree::Insert

// 继承关系:    private 

// 返回值:     bool

// 参数:      PTREE_NODE & TreeNode()

// 参数:      int nData(数据)

//************************************

bool CBalanceBinaryTree::Insert(PTREE_NODE &TreeNode,int nData)         //插入数据

{

    if (!TreeNode)//查找到结点为空就插入结点

    {

        //新添加结点

        TreeNode = new TREE_NODE;

        memset(TreeNode,0,sizeof(TREE_NODE));

        TreeNode->Data = nData;

        //插入成功 ,计数+1

        m_nCount++;

        return true;

    }

    //判断是否小于根若小于根则从左孩子树开始查找寻找插入点

    if (nData < TreeNode->Data)

    {

        Insert(TreeNode->pLchild,nData);

        //判断是否符合平衡二叉树条件

        if(2 == (GetDepth(TreeNode->pLchild) -

            GetDepth(TreeNode->pRchild)))

        {//左子树与右子树深度差大于1的情况需要旋转

            if (GetDepth(TreeNode->pLchild->pRchild)  >

                GetDepth(TreeNode->pLchild->pLchild))

            {//如果以左孩子树为结点,其右孩子树深度大于左孩子树深度则需要右左旋

                DoubleRotateLR(TreeNode);  //右左旋

            }

            else

            {//如果其右孩子树不大于左孩子树深度则仅需要左单旋就行了

                SingRotateLeft(TreeNode); //左单旋

            }

        }

    }

    //判断是否大于根如果成立就从右孩子树一侧开始查找插入点

    else if (nData > TreeNode->Data)

    {

        Insert(TreeNode->pRchild,nData);

        //判断新插入叶子后是否符合平衡二叉树条件

        if(2 == (GetDepth(TreeNode->pRchild) -

            GetDepth(TreeNode->pLchild)))

        {//右子树与左子树的深度差大于1的情况就需要旋转

            if (GetDepth(TreeNode->pRchild->pLchild)>

                GetDepth(TreeNode->pRchild->pRchild))

            {//以右子树为结点,其左子树深度若大于右子树需要左右旋

                DoubleRotateLR(TreeNode);  //左右旋

            }

            else

            {//以右子树为结点,其左子树深度小于右子树仅需要右旋

                SingRotateRight(TreeNode); //右单旋

            }

        }

    }

    else   //等于根(即有和插入新结点的值相等的情况就直接返回)

        return false;

    return true;

}

//************************************

// 函数名称:    DeleteNode

// 功能:      删除数据

// FullName:CBalanceBinaryTree::DeleteNode

// 继承关系:    private 

// 返回值:     bool

// 参数:      int nData(需要删除的数据)

//************************************

bool CBalanceBinaryTree::DeleteNode(int nData)  

{

    if (!DeleteNode(m_TreeNode,nData))

    {

        return false;

    }

    m_nCount--;

    return true;

}

//************************************

// 函数名称:    DeleteNode

// 功能:      数据删除    

// FullName:CBalanceBinaryTree::DeleteNode

// 继承关系:    private 

// 返回值:     bool

// 参数:      PTREE_NODE & TreeNode(树)

// 参数:      int nData(需要删除的数据)

//************************************

bool CBalanceBinaryTree::DeleteNode(PTREE_NODE &TreeNode,int nData)

{

    //用来判断是否删除成功(例如,在树中查找不到数据就返回false

    bool bSuccess = true;

    if (!TreeNode)//先决条件

    {//如果树为空就直接返回       

        return false;

    }

    //是否找到数据

    if (nData == TreeNode->Data)

    {

        //1.删除的是叶子节点就直接删除

        if (!TreeNode->pLchild && !TreeNode->pRchild)//递归出口

        {

            delete TreeNode;

            TreeNode = nullptr;

            return true;

        }

        //2.有左子树(默认获取左子树中的最大值来替换当前结点)

        else if (TreeNode->pLchild)

        {   //定义一个变量指向左子树中的最大值结点

            PTREE_NODE pMaxData = GetMaxpLchild(TreeNode->pLchild);

            //把当前结点的值替换为最大值结点的值

            TreeNode->Data = pMaxData->Data;

            //再以当前结点的左子树为结点,向下删除最大值结点

            bSuccess = DeleteNode(TreeNode->pLchild,pMaxData->Data);

            //删除结点后逐层递归返回判断二叉树是否平衡

            if(2 == (GetDepth(TreeNode->pLchild) - 

                GetDepth(TreeNode->pRchild)))

            {//如果结点的左子树的高度比右子树的高度大于1的情况就需要旋转平衡(右左旋或左单旋)

                if (GetDepth(TreeNode->pLchild->pRchild)>

                    GetDepth(TreeNode->pLchild->pLchild))

                {//第一种情况,以左子树为结点,其右边高度大于左边就需要右左旋

                    DoubleRotateRL(TreeNode);  //右左旋

                }

                else

                {//第二种情况,以左子树为结点,如果右边高度不大于左边高度仅左单旋就可以了

                    SingRotateLeft(TreeNode); //左单旋

                }

            }

        }

        //3.没有左子树有右子树(获取右子树中的最小值,以右子树为结点向左查找到尽头就是最小值)

        else if(TreeNode->pRchild)

        {   //定义一个变量指向右子树中的最小值

            PTREE_NODE pMinData = GetMinpRchild(TreeNode->pRchild);

            //用右子树中的最小值替换需要删除结点的值

            TreeNode->Data = pMinData->Data;

            //再以右子树为结点删除原有最小值

            bSuccess = DeleteNode(TreeNode->pRchild,pMinData->Data);

 

            //删除结点后逐层递归返回二叉树平衡条件判断判断

            if(2 == (GetDepth(TreeNode->pRchild) -

                GetDepth(TreeNode->pLchild)))

            {//如果结点的右子树与左子树的高度差大于1就需要旋转平衡(左右旋和右单旋)

                if (GetDepth(TreeNode->pRchild->pLchild)  >

                    GetDepth(TreeNode->pRchild->pRchild))

                {//第一种情况,以右子树的结点,其左边的高度大于右边就需要左右旋

                    DoubleRotateLR(TreeNode);  //左右旋

                }

                else

                {//第二种情况,以右子树为结点,其左边高度不大于右边高度时仅右旋就可以了

                    SingRotateRight(TreeNode); //右单旋

                }

            }

        }

    }

    else if (nData < TreeNode->Data)

    {//如果删除的数据小于当前结点的数据就,从当前结点的左子树向下递归查找删除

        bSuccess = DeleteNode(TreeNode->pLchild,nData);

 

        //删除的是左子树的叶子结点,只能造成右子树不平衡,所以需要判断右子树失衡,左右旋和右单旋的情况

        if(2 == (GetDepth(TreeNode->pRchild) -

            GetDepth(TreeNode->pLchild)))

        {//如果结点的右子树与左子树的高度差大于1就需要旋转平衡(左右旋和右单旋)

            if (GetDepth(TreeNode->pRchild->pLchild)  >

                GetDepth(TreeNode->pRchild->pRchild))

            {//第一种情况,以右子树的结点,其左边的高度大于右边就需要左右旋

                DoubleRotateLR(TreeNode);  //左右旋

            }

            else

            {//第二种情况,以右子树为结点,其左边高度不大于右边高度时仅右旋就可以了

                SingRotateRight(TreeNode); //右单旋

            }

        }

    }

    else if (nData > TreeNode->Data)

    {//如果删除的数据大于当前结点的数据就,从当前结点的右子树向下递归查找删除

        bSuccess = DeleteNode(TreeNode->pRchild,nData);

 

        //删除的是右子树的叶子结点,只能造成左子树不平衡,所以需要判断左子树失衡,右左旋和左单旋的情况

        if(2 == (GetDepth(TreeNode->pLchild) - 

            GetDepth(TreeNode->pRchild)))

        {//如果结点的左子树的高度比右子树的高度大于1的情况就需要旋转平衡(右左旋或左单旋)

            if (GetDepth(TreeNode->pLchild->pRchild)>

                GetDepth(TreeNode->pLchild->pLchild))

            {//第一种情况,以左子树为结点,其右边高度大于左边就需要右左旋

                DoubleRotateRL(TreeNode);  //右左旋

            }

            else

            {//第二种情况,以左子树为结点,如果右边高度不大于左边高度仅左单旋就可以了

                SingRotateLeft(TreeNode); //左单旋

            }

        }

    }

    if (bSuccess == false)

     return bSuccess;

    else    

    return true;

}

//************************************

// 函数名称:    GetMaxpLchild

// 功能:      //获取左子树的最大值节点

// FullName:CBalanceBinaryTree::GetMaxpLchild

// 继承关系:    private 

// 返回值:     CBalanceBinaryTree::PTREE_NODE

// 参数:      PTREE_NODE TreeNode

//************************************

CBalanceBinaryTree::PTREE_NODE CBalanceBinaryTree::GetMaxpLchild(PTREE_NODE TreeNode)      

 //获取左子树的最大值节点   

{//左子树向右右到尽头就是最大值结点

    ////1.(递归方法)

    //if (!TreeNode->pRchild)//直到右子树为空结束递归

    //{

    //  return TreeNode;

    //}

    //GetMaxpLchild(TreeNode->pRchild);

    //    //2.(非递归方法)

    while(TreeNode->pRchild != NULL)//直到右子树为空结束

    {

        TreeNode = TreeNode->pRchild;

    }

    return TreeNode;

}

//************************************

// 函数名称:    GetMinpRchild

// 功能:      //获取右子树的最小值节点

// FullName:CTreeClass::GetMinpRchild

// 继承关系:    private 

// 返回值:     CTreeClass::PTREE_NODE

// 功能:      //获取右子树的最小值节点

// 参数:      PTREE_NODE TreeNode

//************************************

CBalanceBinaryTree::PTREE_NODE CBalanceBinaryTree::GetMinpRchild(PTREE_NODE TreeNode)       

//获取右子树的最小值节点

{//右子树向左左到尽头就是最小值结点

    //1.(递归方法)

    //if (!TreeNode->pLchild)//直到左子树为空结束递归

    //{

    //  return TreeNode;

    //}

    //GetMinpRchild(TreeNode->pLchild);

 

    //2.(非递归方法)

    while (TreeNode->pLchild != NULL)

    {

        TreeNode = TreeNode->pLchild;

    }

    return TreeNode;

}

//************************************

// 函数名称: GetDepth

// 功能:      //获得树的深度对外接口

// FullName:CTreeClass::GetDepth

// 继承关系: public

// 返回值: int

// 功能:   //获得树的深度对外接口

//************************************

int CBalanceBinaryTree::GetDepth()   //获得树的深度对外接口

{

    return GetDepth(m_TreeNode);

}

//************************************

// 函数名称: GetDepth

// 功能:      //获得树的深度

// FullName:CTreeClass::GetDepth

// 继承关系: private

// 返回值: int

// 参数:   PTREE_NODE TreeNode

//************************************

int CBalanceBinaryTree::GetDepth(PTREE_NODE TreeNode)    //获得树的深度

{

    if (!TreeNode)//空树就直接返回(做递归终止条件)

    {

        return 0;

    }

    //获取左子树深度

    int LChildDepth = GetDepth(TreeNode->pLchild);

    //获取右子树深度

    int RChildDepth = GetDepth(TreeNode->pRchild);

    //判断最大深度

    int MaxDepth = LChildDepth > RChildDepth ? LChildDepth : RChildDepth;

    //如果左或右子树有元素则MaxDepth自增1

    MaxDepth++;

    return MaxDepth;

}

主函数:

 C++ Code 

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

// 平衡二叉树.cpp : 定义控制台应用程序的入口点。

//

 

#include "stdafx.h"

#include "BalanceBinaryTree.h"

 

int _tmain(int argc, _TCHAR *argv[])

{

    CBalanceBinaryTree obj;

    obj.Insert(10);

    obj.Insert(5);

    obj.Insert(15);

    obj.Insert(20);

    obj.Insert(18);

    obj.DeleteNode(100);

    obj.DeleteNode(15);

    return 0;

}

 


AVL树

标签:

原文地址:http://blog.csdn.net/victerymars/article/details/51372714

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!