1. 程式人生 > >AVL樹原理及實現(C語言實現以及Java語言實現)

AVL樹原理及實現(C語言實現以及Java語言實現)

歡迎探討,如有錯誤敬請指正

如需轉載,請註明出處http://www.cnblogs.com/nullzx/

1. AVL定義

AVL樹是一種改進版的搜尋二叉樹。對於一般的搜尋二叉樹而言,如果資料恰好是按照從小到大的順序或者從大到小的順序插入的,那麼搜尋二叉樹就對退化成連結串列,這個時候查詢,插入和刪除的時間都會上升到O(n),而這對於海量資料而言,是我們無法忍受的。即使是一顆由完全隨機的資料構造成的搜尋二叉樹,從統計角度去分析,在進行若甘次的插入和刪除操作,這個搜尋二叉樹的高度也不能令人滿意。這個時候大家就希望能有一種二叉樹解決上述問題。這個時候就出現平衡搜尋二叉樹,它的基本原理就是在插入和刪除的時候,根據情況進行調整,以降低二叉樹的高度。平衡搜尋二叉樹典型代表就是AVL樹和紅黑樹。

AVL樹:任何一個節點的左子支高度與右子支高度之差的絕對值不超過1。需要我們注意的是,AVL樹定義不是說從根節點到葉子節點的最短距離比最長短距離大1。

image

上圖就是一顆AVL樹,從根節點到葉子節點的最短距離是5,最長距離是9。

2. 旋轉的定義

因為每種書中對旋轉的定義不一致,所以我們有必要在這裡特此說明一下

以某一個節點為軸,它的左子枝順時針旋轉,作為新子樹的根,我們稱之為順時針旋轉(clockwise)或者右旋轉

同理,以某一個節點為軸,它的右子枝逆針旋轉,作為新子樹的根,我們稱之為逆時針旋轉(anticlockwise)或者左旋轉

3. AVL插入操作

AVL樹的插入操作首先會按照普通搜尋二叉樹的插入操作進行,當插入一個數據後,我們會沿著插入資料時所經過的的節點回溯,回溯的過程中會判回溯路徑中的每個節點的左子支高度與右子支高度之差的絕對值是否超過1,如果超過1我們就進行調整,調整的目的是使得該節點滿足AVL樹的定義。調整的情況可以分為以下四旋轉操作,旋轉操作可以降低樹的高度,同時不改變搜尋二叉樹的性質(即任何一個節點左子支中的全部節點小於該節點,右子支的全部節點大於該節點)。

3.1 情況1

節點X左子支比右子支高度大2,且插入的節點位於X的左孩子節點XL的左子支上

image

3.2 情況2

節點X右子支比左子支高度大2,且插入的節點位於節點X右孩子節點XR的右子支上

image

3.3 情況3

節點X左子支比右子支高度大2,且插入的節點位於節點X左孩子節點XL的右子支上

image

3.4 情況4

節點X左子支比右子支高度大2,且插入的節點位於節點X左孩子節點XL的右子支上

image

4. AVL刪除操作

AVL樹的刪除操作和插入操作一樣,首先會按照普通搜尋二叉樹的刪除操作進行,當刪除一個數據後,和插入操作一樣,我們通常採取的策略是沿著刪除資料時所經過的的節點回溯,回溯的過程中會判斷該節點的左子支高度與右子支高度之差的絕對值是否超過1(或者說大2),如果超過1,我們就進行調整,調整的目的是使得該節點滿足AVL樹的定義。調整的情況可以分為四種,和插入過程完全一樣,這裡不在贅述。

5. C語言實現

5.1節點定義

AVLtree.h檔案中的內容

#ifndef __AVLTREE_H__
#define __AVLTREE_H__
typedef struct Node{
	int height; //該節點作為子樹時的高度
	int data;   //表示每個節點存貯的資料
	Node* left;
	Node* right;
}Node, *AVLtree; 
//AVLtree  表示Node*
//AVLtree* 就表示Node**

int Insert(AVLtree* T, int D);
int Delete(AVLtree* T, int D);
int Find(AVLtree T, int x);
int Destroy(AVLtree* T);

//下面兩個遍歷函式主要用於測試
void InOredrTraverse(AVLtree T);
void PreOredrTraverse(AVLtree T);
#endif

5.2程式碼實現

AVLtree.cpp檔案中的內容

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

#define MAX(x1,x2) ((x1) > (x2) ? (x1) : (x2))

//一下函式用於輔助實現插入刪除操作,作用域於僅限於AVLtree.cpp
static void PostOrderTraverse(AVLtree T);
static int GetHeight(AVLtree T);
static void LeftRotate(AVLtree* T);
static void RightRotate(AVLtree* T);
static int FindMin(AVLtree T);

//返回值用於表示插入是否成功,-1表示失敗(說明樹中已包含該資料),0表示成功
int Insert(AVLtree* T,int D){
	//引數檢查
	if(T == NULL){
		return -1;
	}

	//找到插入的位置
	if(*T == NULL){
		*T = (Node*)malloc(sizeof(Node));
		(*T)->data = D;
		(*T)->height = 1;
		(*T)->left = NULL;
		(*T)->right = NULL;
		return 0;
	}else{
		//樹中已存在該資料
		if(D == (*T)->data){
			return -1;
		}else
		if(D > (*T)->data){//在右子樹中插入
			if(Insert(&(*T)->right,D) == -1){
				return -1;
			}

			//插入後,當回溯到該節點進行檢查,如果不滿足平衡條件,則調整
			//因為是在右子支中插入,如果高度只差等於2,只可能是右子支比左子支高
			if(GetHeight((*T)->right) - GetHeight((*T)->left) == 2){
				if(D > (*T)->right->data){
					LeftRotate(T);//對應情況2,左旋
				}else{//對應情況4,先右旋再左旋
					RightRotate(&(*T)->right);
					LeftRotate(T);
				}
			}
		}else
		if(D < (*T)->data){//在左子樹中插入
			if(Insert(&(*T)->left,D)){
				return -1;
			}

			if(GetHeight((*T)->left) - GetHeight((*T)->right) == 2){
				if(D < (*T)->left->data){
					RightRotate(T);//對應情況1,左旋
				}else{//對應情況3,先右旋再左旋
					LeftRotate(&(*T)->left);
					RightRotate(T);
				}
			}
		}
	}

	//更新當前節點的高度
	(*T)->height = MAX(GetHeight((*T)->left),GetHeight((*T)->right))+1;
	return 0;
}

//返回-1表示,樹中沒有該資料,刪除失敗,
int Delete(AVLtree* T,int D){
	static Node* tmp;
	if(T == NULL){
		return -1;
	}

	if(*T == NULL){//樹為空,或者樹中沒有該資料
		return -1;
	}else{
		//找到要刪除的節點
		if(D == (*T)->data){
			//刪除的節點左右子支都不為空,一定存在前驅節點
			if((*T)->left != NULL && (*T)->right != NULL){
				D = FindMin((*T)->right);//找後繼替換
				(*T)->data = D;
				Delete(&(*T)->right,D);//然後刪除後繼節點,一定成功
				//在右子支中刪除,刪除後有可能左子支比右子支高度大2
				if(GetHeight((*T)->left)-GetHeight((*T)->right) ==  2){
					//判斷哪一個左子支的的兩個子支哪個比較高
					if(GetHeight((*T)->left->left) >= GetHeight((*T)->left->right)){
						RightRotate(T);
					}else{
						LeftRotate(&(*T)->left);
						RightRotate(T);
					}
				}
			}else
			if((*T)->left == NULL){//左子支為空
				tmp = (*T);
				(*T) = tmp->right;
				free(tmp);
				return 0;
			}else
			if((*T)->right == NULL){//右子支為空
				tmp = (*T);
				(*T) = tmp->left;
				free(tmp);
				return 0;
			}			
		}else
		if(D > (*T)->data){//在右子支中尋找待刪除的節點
			if(Delete(&(*T)->right,D) == -1){
				return -1;//刪除失敗,不需要調整,直接返回
			}
			if(GetHeight((*T)->left)-GetHeight((*T)->right) ==  2){
				if(GetHeight((*T)->left->left) >= GetHeight((*T)->left->right)){
					RightRotate(T);
				}else{
					LeftRotate(&(*T)->left);
					RightRotate(T);
				}
			}
		}else
		if(D < (*T)->data){//在左子支中尋找待刪除的節點
			if(Delete(&(*T)->left,D) == -1){
				return -1;
			}
			if(GetHeight((*T)->right) - GetHeight((*T)->left) == 2){
				if(GetHeight((*T)->right->right) >= GetHeight((*T)->right->left)){
					LeftRotate(T);
				}else{
					RightRotate(&(*T)->right);
					LeftRotate(T);
				}
			}
		}
	}
	//更新當前節點的高度
	(*T)->height = MAX(GetHeight((*T)->left),GetHeight((*T)->right))+1;
	return 0;
}

int Find(AVLtree T,int x){
	while(T != NULL){
		if(T->data == x){
			return 0;
		}else
		if(x > T->data){
			T = T->right;	
		}else{
			T = T->left;	
		}
	}
	return -1;
}

int Destroy(AVLtree* T){
	if(T == NULL){
		return -1;
	}

	PostOrderTraverse(*T);
	*T = NULL;
	return 0;
}

void InOredrTraverse(AVLtree T){
	if(T != NULL){
		InOredrTraverse(T->left);
		printf("%3d ",T->data);
		InOredrTraverse(T->right);;
	}
}

void PreOredrTraverse(AVLtree T){
	if(T != NULL){
		printf("%3d:%2d(%3d,%3d)\n",T->data,T->height,
			   T->left == NULL?-1:T->left->data,
			   T->right == NULL?-1:T->right->data
			  );
		PreOredrTraverse(T->left);
		PreOredrTraverse(T->right);
	}
}


static void PostOrderTraverse(AVLtree T){
	if(T != NULL){
		PostOrderTraverse(T->left);
		PostOrderTraverse(T->right);
		free(T);
	}
}

//空數的高度為0
static int GetHeight(AVLtree T){
	if(T == NULL){
		return 0;
	}else{
		return T->height;
	}
}

static void LeftRotate(AVLtree* T){
	Node *P,*R;
	P = *T;
	R = P->right;
	P->right = R->left;
	R->left = P;
	*T = R;
	
	//旋轉以後要更新節點的高度
	P->height = MAX(GetHeight(P->left),GetHeight(P->right))+1;
	R->height = MAX(GetHeight(R->left),GetHeight(R->right))+1;
}

static void RightRotate(AVLtree* T){
	Node *P,*L;
	P = *T;
	L = P->left;
	P->left = L->right;
	L->right = P;
	*T = L;
	//旋轉以後要更新節點的高度
	P->height = MAX(GetHeight(P->left),GetHeight(P->right))+1;
	L->height = MAX(GetHeight(L->left),GetHeight(L->right))+1;
}

static int FindMin(AVLtree T){
	if(T == NULL){
		return -1;
	}

	while(T->left != NULL){
		T = T->left;
	}
	return T->data;
}

6. Java語言泛型實現

package datastruct;

import java.util.Comparator;

public class AVLtree <E>{
	private static class Node<E>{
		int h;
		E element;
		Node<E> left;
		Node<E> right;
		//由於java中不像C語言那樣有二級指標的概念,所以新增一個父類的引用,方便程式編寫
		Node<E> parent;
		
		public Node(E element, int h, Node<E> left, Node<E> right, Node<E> parent){
			this.element = element;
			this.h = h;
			this.left = left;
			this.right = right;
			this.parent = parent;
		}
	}
	
	private Node<E> root;//指向偽根節點的引用
	private int size = 0;//節點個數
	Comparator<? super E> cmp;//節點大小的比較器
	
	//如果呼叫了不帶引數的建構函式,則使用該內部類作為比較器,
	//但此時泛型E需要繼承Comparable介面,否則執行時會丟擲異常
	private static class Cmp<T> implements Comparator<T>{
		@SuppressWarnings({ "unchecked", "rawtypes" })
		@Override
		public int compare(T e1, T e2) {
			return ((Comparable)e1).compareTo(e2);
		}
		
	}
	
	//帶比較器的建構函式
	public AVLtree(Comparator<? super E> cmp){
		if(cmp == null){
			throw new IllegalArgumentException();
		}
		this.cmp = cmp;
		//建立一個偽根節點,該節點的右子支才是真正的AVL樹的根
		//使用偽根節點節點的目的是,對插入和刪除操作遞迴的形式能夠統一
		root = new Node<E>(null, -1, null, null, null);
	}
	
	//不帶比較器的建構函式
	public AVLtree(){
		this.cmp = new Cmp<E>();
		root = new Node<E>(null, -1, null, null, null);
	}
	
	//如果樹中節點有變動,從底向上逐級呼叫該函式,可以更新節點的高度
	private int getHight(Node<E> x){
		if(x == null){
			return 0;
		}else{
			return x.h;
		}
	}
	
	//求某個節點作為根時,該子樹的最小值
	private E treeMin(Node<E> x){
		while(x.left != null){
			x = x.left;
		}
		return x.element;
	}
	
	public int size(){
		return size;
	}
	
	//先根遍歷,除錯時使用
	public void preorderTraverse(){
		if(root != null){
			preorderTraverse0(root.right);
		}
	}
	
	private void preorderTraverse0(Node<E> x){
		if(x != null){
			System.out.print(x.element+" ");
			if(x.left != null){
				System.out.print(x.left.element+" ");
			}else{
				System.out.print("null  ");
			}
			
			if(x.right != null){
				System.out.print(x.right.element+" ");
			}else{
				System.out.print("null  ");
			}
			System.out.println();
			preorderTraverse0(x.left);
			preorderTraverse0(x.right);
		}
	}
	
	//逆時針旋轉(左旋),引數表示軸節點
	private void antiClockwiseRotate(Node<E> X){
		Node<E> P = X.parent;
		Node<E> XR = X.right;
		if(P.left == X){
			P.left = XR;
		}else{
			P.right = XR;
		}
		XR.parent = P;
		
		X.right = XR.left;
		if(XR.left != null){
			XR.left.parent = X;
		}
		
		XR.left = X;
		X.parent = XR;
		
		//旋轉後要更新這兩個節點的高度
		X.h = Math.max(getHight(X.left), getHight(X.right)) + 1;
		XR.h = Math.max(getHight(XR.left), getHight(XR.right)) + 1;
	}
	
	//順時針旋轉(右旋),引數表示軸節點
	private void clockwistRotate(Node<E> X){
		Node<E> P = X.parent;
		Node<E> XL = X.left;
		if(P.left == X){
			P.left = XL;
		}else{
			P.right = XL;
		}
		XL.parent = P;
		
		X.left = XL.right;
		if(XL.right != null){
			XL.right.parent = X;
		}
		
		XL.right = X;
		X.parent = XL;
		
		//旋轉後要更新這兩個節點的高度
		X.h = Math.max(getHight(X.left), getHight(X.right)) + 1;
		XL.h = Math.max(getHight(XL.left), getHight(XL.right)) + 1;
	}
	
	//
	public void insert(E e){
		insert0(root.right, e);
	}
	
	private void insert0(Node<E> x, E e){
		if(x == null){
			root.right = new Node<E>(e, 1, null, null, root);//根節點
			size++;
			return;
		}
		
		if(cmp.compare(e, x.element) > 0){
			if(x.right != null){
				insert0(x.right, e);
				int lh = getHight(x.left);
				int rh = getHight(x.right);
				if(rh - lh == 2){
					if(cmp.compare(e, x.right.element) > 0){
						antiClockwiseRotate(x);
					}else{
						clockwistRotate(x.right);
						antiClockwiseRotate(x);
					}
				}
			}else{
				size++;
				x.right = new Node<E>(e, 1, null, null, x);
			}
		}else
		if(cmp.compare(e, x.element) < 0){
			if(x.left != null){
				insert0(x.left, e);
				int lh = getHight(x.left);
				int rh = getHight(x.right);
				if(lh - rh == 2){
					if(cmp.compare(e, x.left.element) < 0){
						clockwistRotate(x);
					}else{
						antiClockwiseRotate(x.left);
						clockwistRotate(x);
					}
				}
			}else{
				size++;
				x.left = new Node<E>(e, 1, null, null, x);
			}
		}else{
			//元素已存在,我們用新的元素更新舊,
			//compare返回值等於0,並不表示兩個物件完全相等
			x.element = e;
		}
		x.h = Math.max(getHight(x.left), getHight(x.right)) + 1;
	}
	
	public boolean delete(E e){
		return delete0(root.right, e);
	}
	
	//返回值表示是否刪除成功
	private boolean delete0(Node<E> x, E e){
		if(x == null){//沒有找到待刪除的元素
			return false;
		}
		
		if(cmp.compare(e, x.element) > 0){
			boolean reval = delete0(x.right, e);
			if(reval == false){
				return false;
			}
			
			int lh = getHight(x.left);
			int rh = getHight(x.right);
			if(lh - rh == 2){
				if(getHight(x.left.left) > getHight(x.left.right)){
					clockwistRotate(x);
				}else{
					antiClockwiseRotate(x.left);
					clockwistRotate(x);
				}
			}
		}else
		if(cmp.compare(e, x.element) < 0){
			boolean reval = delete0(x.left, e);
			if(reval == false){
				return false;
			}

			int lh = getHight(x.left);
			int rh = getHight(x.right);
			if(rh - lh == 2){
				if(getHight(x.right.right) > getHight(x.right.left)){
					antiClockwiseRotate(x);
				}else{
					clockwistRotate(x.right);
					antiClockwiseRotate(x);
				}
			}
		}else{//找到待刪除的元素
			Node<E> P = x.parent;
			
			if(x.left == null){//左子支為空,可直接刪除,在這一層一定不需要旋轉
				size--;
				if(P.left == x){
					P.left = x.right;
					if(P.left != null){
						P.left.parent = P;
					}
				}else{
					P.right = x.right;
					if(P.right != null){
						P.right.parent = P;
					}
				}
			}else
			if(x.right == null){//右子支為空,可直接刪除,在這一層一定不需要旋轉
				size--;
				if(P.left == x){
					P.left = x.left;
					if(P.left != null){					
						P.left.parent = P;
					}
				}else{
					P.right = x.left;
					if(P.right != null){
						P.right.parent = P;
					}
				}
			}else{//找到待刪除的節點,用後繼節點代替,然後刪除後繼節點
				E nextVal = treeMin(x.right);
				x.element = nextVal;
				delete0(x.right, nextVal);
				int lh = getHight(x.left);
				int rh = getHight(x.right);
				if(lh - rh == 2){
					if(getHight(x.left.left) > getHight(x.left.right)){
						clockwistRotate(x);
					}else{
						antiClockwiseRotate(x.left);
						clockwistRotate(x);
					}
				}
			}
		}
		x.h = Math.max(getHight(x.left), getHight(x.right)) + 1;
		return true;
	}
	
	public static void main(String[] args){
		AVLtree<Integer> avl = new AVLtree<Integer>();
		/*可自行新增插入,刪除操作進行測試*/
		avl.insert(3);
		avl.insert(5);
		avl.insert(6);
		avl.insert(7);
		avl.insert(8);
		avl.insert(9);
		avl.preorderTraverse();
		System.out.println();
		System.out.println(avl.size());
		
		avl.delete(7);
		avl.delete(8);
		avl.preorderTraverse();
		System.out.println();
		System.out.println(avl.size());
	}
}