數據結構—二叉查找樹和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樹

1、概念:帶有平衡條件的二叉查找樹,即在插入和刪除操作中必須樹的平衡。

                一棵AVL樹的每個節點的左子樹和右子樹的高度最多相差不超過1(空樹的高度定義爲-1)

2、插入操作破壞AVL樹的平衡性討論論

          假設由於一次插入操作破壞了節點α的平衡,由於任意節點最多有兩棵子樹,因此當節點α不平衡時,它的兩棵子樹的高度差爲2。可能有下面四種情況:

     ①對α的左兒子的左子樹進行一次插入

     ②對α的左兒子的右子樹進行一次插入

     ③對α的右兒子的左子樹進行一次插入

     ④對α的右兒子的右子樹進行一次插入

3、調整方法

     ①單旋轉

              

            13不在4和7之間

     ②雙旋轉

                  

            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最壞

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章