1. 程式人生 > >二叉搜尋樹及AVL平衡二叉搜尋樹

二叉搜尋樹及AVL平衡二叉搜尋樹

二叉搜尋樹

二叉搜尋樹是二叉樹的的一種,只不過多了個限制條件,即左子樹節點的值都小於該點,右子樹節點的值都大於該點。

定義:

// 樹節點
template <typename T>
class Node
{
public:
	T key;
	Node* parent;
	Node* left;
	Node* right;
	
	Node(T k, Node* p = nullptr, Node* l = nullptr, Node* r = nullptr) :
		key(k), parent(p), left(l), right(r) {}
};

// 二叉搜尋樹
template
<typename T> class BSTree { private: Node<T>* root_; public: BSTree(); ~BSTree(); Node<T>* Search(T key); Node<T>* Insert(T key); void Remove(T key); private: void Destroy(Node<T>* &head); };

這裡只實現了最關鍵的三個操作:查詢、插入和刪除。

查詢自然不必多說,類似於二分查詢,從根節點開始判斷往左往右還是命中。
插入也簡單,類似查詢,找到要插入的位置即可,注意新插入的節點必為葉子節點。

刪除節點就麻煩一些了,需要分情況進行處理。
(1)刪除的節點為葉子節點,直接刪除即可。
在這裡插入圖片描述
(2)刪除的節點只有一個孩子,把這個孩子提上來代替自己即可。
在這裡插入圖片描述
(3)刪除的節點左右孩子皆有,這種情況需要先找到右子樹最左節點,然後把這個節點跟要刪除的節點交換,這樣就變成了刪除右子樹最左節點了,而該最左節點必定無左孩子,這樣刪除就變成上面兩種情況之一了。
在這裡插入圖片描述

這裡還要注意的一點是當刪除的節點為根節點時,要維護好根節點的更新。

實現:

template <typename T>
BSTree<T>::BSTree() : root_(nullptr) {}

template <
typename T> BSTree<T>::~BSTree() { Destroy(root_); } template <typename T> Node<T>* BSTree<T>::Search(T key) { Node<T>* cur = root_; while (cur && cur->key != key) { if (key < cur->key) cur = cur->left; else cur = cur->right; } return cur; } template <typename T> Node<T>* BSTree<T>::Insert(T key) { Node<T>* cur = root_; Node<T>* pre = nullptr; // 找到插入位置 while (cur) { pre = cur; if (key == cur->key) return cur; else if (key < cur->key) cur = cur->left; else cur = cur->right; } Node<T>* new_node = new Node<T>(key); new_node->parent = pre; // 插入為根節點 if (pre == nullptr) root_ = new_node; // 插入為左孩子 else if (key < pre->key) pre->left = new_node; // 插入為右孩子 else pre->right = new_node; } template <typename T> void BSTree<T>::Remove(T key) { Node<T>* remove; // 不存在該節點 if ((remove = Search(key)) == nullptr) return; // 為葉子節點,直接刪除 if (remove->left == nullptr && remove->left == nullptr) { if (remove->parent == nullptr) // 刪除節點為根節點的情況 root_ = nullptr; else if (remove->parent->left == remove) remove->parent->left = nullptr; else remove->parent->right = nullptr; delete remove; return; } // 左孩子為空,直接把右孩子提上來後刪除自己 if (remove->left == nullptr) { if (remove->parent == nullptr) { remove->right->parent = nullptr; root_ = remove->right; } else if (remove->parent->left == remove) { remove->right->parent = remove->parent; remove->parent->left = remove->right; } else { remove->right->parent = remove->parent; remove->parent->right = remove->right; } delete remove; return; } // 右孩子為空,直接把左孩子提上來後刪除自己 if (remove->right == nullptr) { if (remove->parent == nullptr) { remove->left->parent = nullptr; root_ = remove->left; } else if (remove->parent->left == remove) { remove->left->parent = remove->parent; remove->parent->left = remove->left; } else { remove->left->parent = remove->parent; remove->parent->right = remove->left; } delete remove; return; } // 左右孩子均不為空的情況,把該節點跟右子樹最小節點交換後,轉換為刪除右子樹最小節點 Node<T>* right_min = remove->right; while (right_min->left) // 找到右子樹最小節點 { right_min = right_min->left; } remove->key = right_min->key; // 與要刪除的節點交換 if (right_min->right) // 有右孩子,轉換為第三種情況 { right_min->right->parent = right_min->parent; right_min->parent->left = right_min->right; } else // 無右孩子,轉換為第二種情況 { if (right_min->parent->left == right_min) right_min->parent->left = nullptr; else right_min->parent->right = nullptr; } delete right_min; return; } template <typename T> void BSTree<T>::Destroy(Node<T>* &head) { if (head == nullptr) return; if (head->left) Destroy(head->left); if (head->right) Destroy(head->right); delete head; head = nullptr; }

AVL 自平衡二叉搜尋樹

AVL樹在二叉搜尋樹的基礎上加了平衡的限制條件,即對於每個節點,左右子樹的高度差不能超過1,這樣對於某些情況,該搜尋樹的效能就不至於下降。

AVL樹的定義於普通的二叉搜尋樹大同小異,不過受限於平衡條件,插入和刪除節點的操作就變得複雜起來,在每次插入或刪除節點後,都要判斷是否失衡並恢復平衡。

定義:

template <typename T>
class Node
{
public:
	T key;
	int height;
	Node* left;
	Node* right;
	
	Node(T k, Node* l = nullptr, Node* r = nullptr) :
		key(k), height(0), left(l), right(r) {}
};

template <typename T>
class AVLTree
{
private:
	Node<T>* root_;
public:
	AVLTree();
	~AVLTree();

	int Height(Node<T>* head);

	Node<T>* Search(T key);
	void Insert(T key);
	void Remove(T key);
private:
	Node<T>* LLRotation(Node<T>* head);
	Node<T>* RRRotation(Node<T>* head);
	Node<T>* LRRotation(Node<T>* head);
	Node<T>* RLRotation(Node<T>* head);

	Node<T>* Insert(Node<T>* &head, T key);
	Node<T>* Remove(Node<T>* &head, Node<T>* r);

	void Destroy(Node<T>* &head);
};

四種失衡狀態及其恢復

(1)LL

在這裡插入圖片描述

(2)RR

在這裡插入圖片描述

(3)LR

在這裡插入圖片描述

(4)RL

在這裡插入圖片描述

有了四種失衡情況調整後就可以實現插入、刪除了,這裡插入刪除都是用遞迴來寫,比如插入,判斷要往左子樹還是右子樹插入,遞迴下去,返回時就要判斷左右子樹是否出現了失衡,如果出現就判斷是哪種情況並進行處理。

實現:

template <typename T>
AVLTree<T>::AVLTree() : root_(nullptr) {}

template <typename T>
AVLTree<T>::~AVLTree()
{
	Destroy(root_);
}

template <typename T>
int AVLTree<T>::Height(Node<T>* head)
{
	if (head)
		return head->height;
	return -1;
}

template <typename T>
Node<T>* AVLTree<T>::Search(T key)
{
	Node<T>* cur = root_;
	while (cur && cur->key != key)
	{
		if (key < cur->key)
			cur = cur->left;
		else
			cur = cur->right;
	}
	return cur;
}

template <typename T>
Node<T>* AVLTree<T>::LLRotation(Node<T>* head)
{
	Node<T>* tmp = head->left;
	head->left = tmp->right;
	tmp->right = head;

	head->height = max(Height(head->left), Height(head->right)) + 1;
	tmp->height = max(Height(tmp->left), Height(tmp->right)) + 1;

	return tmp;
}

template <typename T>
Node<T>* AVLTree<T>::RRRotation(Node<T>* head)
{
	Node<T>* tmp = head->right;
	head->right = tmp->left;
	tmp->left = head;

	head->height = max(Height(head->left), Height(head->right)) + 1;
	tmp->height = max(Height(tmp->left), Height(tmp->right)) + 1;

	return tmp;
}

template <typename T>
Node<T>* AVLTree<T>::LRRotation(Node<T>* head)
{
	head->left = RRRotation(head->left);
	return LLRotation(head);
}

template <typename T>
Node<T>* AVLTree<T>::RLRotation(Node<T>* head)
{
	head->right = LLRotation(head->right);
	return RRRotation(head);
}

template <typename T>
Node<T>* AVLTree<T>::Insert(Node<T>* &head, T key)
{
	// 插入為根節點
	if (head == nullptr)
	{
		head = new Node<T>(key);
	}
	// 往左子樹插入
	else if (key < head->key)
	{
		head->left = Insert(head->left, key);
		// 判斷是否失衡
		if (Height(head->left) - Height(head->right) == 2)
		{
			if (key < head->left->key)
				head = LLRotation(head);
			else
				head = LRRotation(head);
		}
	}
	// 往右子樹插入
	else if (key > head->key)
	{
		head->right = Insert(head->right, key);
		if (Height(head->right) - Height(head->left) == 2)
		{
			if (key > head->right->key)
				head = RRRotation(head);
			else
				head = RLRotation(head);
		}
	}

	// 更新高度
	head->height = max(Height(head->left), Height(head->right)) + 1;
	// 返回插入後的根節點
	return head;
}

template <typename T>
void AVLTree<T>::Insert(T key)
{
	Insert(root_, key);
}

template <typename T>
Node<T>* AVLTree<T>::Remove(Node<T>* &head, Node<T>* r)
{
	// 找不到要刪除的節點
	if (head == nullptr || r == nullptr)
		return nullptr;

	// 待刪除的節點在左子樹中
	if (r->key < head->key)
	{
		head->left = Remove(head->left, r);
		// 判斷是否失衡
		if (Height(head->right) - Height(head->left) == 2)
		{
			if (Height(head->right->left) > Height(head