1. 程式人生 > >二叉查詢樹(二叉排序樹)學習筆記

二叉查詢樹(二叉排序樹)學習筆記

本文轉載自:http://blog.csdn.net/qq_37887537/article/details/75647670

在學習資料結構的時候,除了基本的之外的,還有許多樹像是二叉搜尋樹,2-3樹,紅黑樹等等。

也曾經學習過二叉樹,以及前序排列中序排列後序排列等等,但是一直無緣使用它!

排序有快速排序,歸併排序,查詢有二分法,甚至直接遍歷查詢,二叉樹的使用很少。那二叉樹究竟是幹什麼的呢?

進行了一番粗淺的研究,我們學習的經典二叉樹,僅僅當他是一種資料結構是不行的,他還是一種程式設計思想,例如解決揹包問題(後面進行學習),在考試和麵試中使用較多。

而實際場景使用上,用的最多的是二叉平衡樹,有種特殊的二叉平衡樹就是紅黑樹

,Java集合中的TreeSet和TreeMap,C++STL中的set、map以及Linux虛擬記憶體的管理,都是通過紅黑樹去實現的,還有哈弗曼樹編碼方面的應用,以及B-Tree,B+-Tree在檔案系統中的應用。當然二叉查詢樹可以用來查詢和排序啦(知乎網友)。

那麼二叉樹有什麼優點?

大家看到最多的是這麼說的,二叉排序樹是一種比較有用的折中方案:

陣列的搜尋比較方便,可以直接使用下標,但刪除或者插入就比較麻煩了,而連結串列與之相反,刪除和插入都比較簡單,但是查詢很慢,這自然也與這兩種資料結構的儲存方式有關,陣列是取一段相連的空間,而連結串列是每建立一個節點便取一個節點所需的空間,只是使用指標進行連線,空間上並不是連續的。而二叉樹就既有連結串列的好處,又有陣列的優點。

二叉樹的分類,瞭解一下

滿二叉樹:從高到低,除了葉節點外,所以節點左右節點都存在。

完全二叉樹:比滿二叉樹少幾個葉節點,從左向右放子節點。

平衡二叉樹:空樹或者它的左右兩個子樹的高度差的絕對值不超過1,並且左右兩個子樹也都是平衡樹。

二叉搜尋樹:空樹或者二叉樹的所有節點比他的左子節點大,比他的右子節點小。

紅黑樹:不僅是具有二叉搜尋樹的屬性,還具有平衡樹的屬性,有序且子樹差不超過1,顏色規則:根節點和特殊節點(即葉節點下面兩個虛無的節點和未填寫的節點)是黑的,紅節點的左右子節點是黑的,最重要的是對於每個節點,從該節點到子孫葉節點的所有路徑包含相同數目的黑節點。

今天就學習一下最簡單的二叉排序樹

二叉排序樹又叫二叉查詢樹或者二叉搜尋樹

,它首先是一個二叉樹,而且必須滿足下面的條件:

1)若左子樹不空,則左子樹上所有結點的值均小於它的根節點的值;

2)若右子樹不空,則右子樹上所有結點的值均大於它的根結點的值

3)左、右子樹也分別為二叉排序樹

這種特殊的結構二叉樹,採用中序遍歷左根右)即可獲得一個有序陣列。

在網上找到的一個基礎圖形:

前序遍歷(根-左-右):ABDGHECKFIJ
中序遍歷(左-根-右):GDHBEAKCIJF
後序便利(左-右-根):GHDEBKJIFCA

二叉搜尋樹的資料關係:G<D<H<B<E<A<K<C<I<J<F

實現方法也比較乾淨利落

void PreOrderTraverse(BiTree root)	// 先序遍歷
{
	if(root){
		printf("%d",root->value);
		PreOrderTraverse(root->lchild);
		PreorderTraverse(root->rchild);
	}
}

void InOrderTraverse(BiTree root)	// 中序遍歷
{
	if(root){
		InOrderTraverse(root->lchild);
		printf("%d",root->value);
		InOrderTraverse(root->rchild);
	}
}

void PostOrderTraverse(BiTree root)	// 後序遍歷
{
	if(root){
		PostOrderTraverse(root->lchild);
		PostOrderTraverse(root->rchild);
		printf("%d",root->value);
	}
}

接著就是關於建立一個二叉搜尋樹的過程,這裡沒有將相同的數字遮蔽掉:

#include <iostream>
using namespace std;

// BST的節點
typedef struct node{
	node( int element, node *lt, node *rt)
	 : key{ element }, lChild{ lt }, rChild{ rt } { }
	int key;
	struct node *lChild;
	struct node *rChild;
}Node, *BST;

// 在給定的BST中插入節點,其資料域為element,使之稱為新的BST
bool BSTInsert(BST &p, int element){
	if(p==nullptr){
		p = new Node(element,nullptr,nullptr);
		return true;
	}
	
//	if(element == p->key)	// BST中不能有相等的值,不註釋掉會遮蔽掉相同的樹
//		return false;
	
	// 遞迴
	if(element < p->key)
		return BSTInsert(p->lChild, element);
	else
		return BSTInsert(p->rChild, element);
}

// 先序遍歷(根-左-右)
void preOrderTraverse(BST T){
	if(T){
		cout << T->key << ends;
		preOrderTraverse(T->lChild);
		preOrderTraverse(T->rChild);
	}
}
// 中序遍歷(左-根-右)
void inOrderTraverse(BST T){
	if(T){
		inOrderTraverse(T->lChild);
		cout << T->key << ends;
		inOrderTraverse(T->rChild);
	}
}
// 後序遍歷(左-右-根)
void postOrderTraverse(BST T){
	if(T){
		postOrderTraverse(T->lChild);
		postOrderTraverse(T->rChild);
		cout << T->key << ends;
	}
}

int main(){
	int a[13] = {4,5,2,1,0,12,3,7,6,8,5,4,7};
	int n = 13;
	BST T = nullptr;
	int i;
	for(i=0;i<n;++i){
		BSTInsert(T, a[i]);
	}
	inOrderTraverse(T);
	
	cout << endl;
	
	BSTInsert(T,11);
	inOrderTraverse(T);
	cout << endl;
	
	return 0;
}