二叉查找树和AVL树

一、二叉查找树(有序树)

1、概念:对于树中每个节点X,它左子树中的数据项小于X的数据项,X的数据项小于它右子树的数据项。

struct TreeNode;
tepedef struct TreeNode* Position;     // 节点位置
tepedef struct TreeNode* SearchTree;   // 表示整棵树

2、基本操作

    ①初始化

SearchTree MakeEmpty(SearchTree T)
{
     if (T != NULL)
     {
         MakeEmpty(T->left);
         MakeEmpty(T->right);
         free(T);
     }
     return    NULL;
}

    ②查找指定元素

 Position Find(ElementType E, SearchTree T);

    ③搜索最小元素

 Position FindMin(SearchTree T);

    ④搜索最大元素

 Position FindMax(SearchTree T);

    ⑤插入

 Position Insert(ElementType E, SearchTree T);

    ⑥删除

 Position Delete(ElementType E, SearchTree T);

    ⑦获取指定位置的元素

 ElementType Retrieve(Position P);

3、性能分析

     二叉查找树的查找、插入、删除操作在平均情况下的时间复杂度为O(㏒N)。但是,随着大量的插入删除操作有可能使得树严重倾斜,这就使得查询插入等操作的性能严重下降,最坏情况下会达到O(N)。

像下面这些情况:

                 《二叉查找树和AVL树》              《二叉查找树和AVL树》

二、AVL树

1、概念:带有平衡条件的二叉查找树,即在插入和删除操作中必须树的平衡。

                一棵AVL树的每个节点的左子树和右子树的高度最多相差不超过1(空树的高度定义为-1)

2、插入操作破坏AVL树的平衡性讨论论

          假设由于一次插入操作破坏了节点α的平衡,由于任意节点最多有两棵子树,因此当节点α不平衡时,它的两棵子树的高度差为2。可能有下面四种情况:

     ①对α的左儿子的左子树进行一次插入

     ②对α的左儿子的右子树进行一次插入

     ③对α的右儿子的左子树进行一次插入

     ④对α的右儿子的右子树进行一次插入

3、调整方法

     ①单旋转

              《二叉查找树和AVL树》

            13不在4和7之间

     ②双旋转

                  《二叉查找树和AVL树》

            14在6和15之间

要点:新插入的元素是否处在平衡性被破坏的α节点和左/右儿子之间

          如果不在,则采用单旋转;如果在,则采用双旋转

4、基本操作

     ①初始化

     ②查找指定元素

     ③搜索最小元素

     ④搜索最大元素

     ⑤插入

     ⑥删除

5、部分代码的实现

#ifndef _AVLTREE_H
#define _AVLTREE_H

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

typedef int ElementType;

struct AvlNode;
typedef struct AvlNode* Position;
typedef struct AvlNode* AvlTree;

struct AvlNode 
{
	ElementType Element;
	AvlTree left;
	AvlTree right;
	int 	Height;
};

AvlTree MakeEmpty(AvlTree T);
Position Find(ElementType X, AvlTree T);
Position FindMin(AvlTree T);
Position FindMax(AvlTree T);
AvlTree Insert(ElementType X, AvlTree T);
AvlTree Delete(ElementType X, AvlTree T);
ElementType Retrieve(Position P);

#endif // _AVLTREE_H
#include "AVLTree.h"

inline void FatalError(const char* msg)
{
	printf("\tFatal Error : %s\n", msg);
}

inline int Height(Position P)
{
	if (P == NULL)
		return -1; // 空树的高度定义为-1
	else
		return P->Height;
}

inline int Max(int x, int y)
{
	return x > y ? x : y;
}

// 此版本只能用于在T的左儿子的左子树插入的情况
static Position SingleRotateWithLeft(Position K2)
{
	Position K1; // 新的根
	
	K1 = K2->left;
	K2->left = K1->right;
	K1->right = K2;

	K2->Height = Max(Height(K2->left), Height(K2->right)) + 1;
	K1->Height = Max(Height(K1->left), K2->Height) + 1;

	return K1;
}

// 此版本只能用于在T的左儿子的右子树插入的情况
static Position DoubleRotateWithLeft(Position K3)
{
	// 先使K3的左子树进行一次单旋转
	K3->left = SingleRotateWithLeft(K3->left);
	// 再使K3进行一次单旋转
	return SingleRotateWithLeft(K3);
}

AvlTree Insert(ElementType X, AvlTree T)
{
	// T是空树,则构造一棵单节点树
	if (T == NULL)
	{
		T = (AvlTree)malloc(sizeof(AvlNode));
		if (T == NULL)
		{
			FatalError("Out of space!!");
		}
		else
		{
			T->Element = X;
			T->left = NULL;
			T->right = NULL;
			T->Height = 0;
		}
	}
	// 在左子树插入
	else if (X < T->Element)
	{
		T->left = Insert(X, T->left);
		// 如果T的平衡条件被破坏
		if (Height(T->left) - Height(T->right) == 2)
		{
			if (X < T->left->Element) // 在T的左儿子的左子树插入,单旋转
				T = SingleRotateWithLeft(T);
			else // 在T的左儿子的右子树插入,双旋转
				T = DoubleRotateWithLeft(T);
		}
	}
	// 在右子树插入
	else if (X > T->Element)
	{
		T->right = Insert(X, T->right);
		// 如果T的平衡条件被破坏
		if (Height(T->right) - Height(T->left) == 2)
		{
			if (X > T->left->Element) // 在T的右儿子的右子树插入,单旋转
				T = SingleRotateWithLeft(T);
			else // 在T的右儿子的左子树插入,双旋转
				T = DoubleRotateWithLeft(T);
		}
	}
	// else X已经在树中,什么也不做
	// 更新T的高度
	T->Height = Max(Height(T->left), Height(T->right)) + 1;
}

5、性能分析

     AVL树是一种高度平衡的二叉树,它保证了查找插入删除操作在最坏情况下都能达到O(㏒N)。它付出的代价就是在插入删除操作中要增加额外的操作保证树的平衡性。

是ad


4最坏

    原文作者:AVL树
    原文地址: https://blog.csdn.net/weixin_37853880/article/details/80379796
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞