程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> C語言 >> C++ >> C++入門知識 >> B-樹 C++模板類封裝(有圖有真相),b-模板

B-樹 C++模板類封裝(有圖有真相),b-模板

編輯:C++入門知識

B-樹 C++模板類封裝(有圖有真相),b-模板


定義:

一棵m階B-樹是擁有以下性質的多路查找樹:

1、非葉子結點的根結點至少擁有兩棵子樹;

2、每一個非根且非葉子的結點含有k-1個關鍵字以及k個子樹,其中⌈m/2⌉≤k≤m;

3、每一個葉子結點都具有k-1個關鍵字,其中⌈m/2⌉≤k≤m;

4、key[i]和key[i+1]之間的孩子節點的值介於key[i]、key[i+1]之間

5、所有的葉子結點都在同一層。

ps: ⌈m/2⌉是向上取整

建立B-樹的節點:

template<class K,int M=3>
struct BTreeNode
{
	K _key[M];	//關鍵字 (有效關鍵字個數為M-1)
	BTreeNode<K, M>* _sub[M + 1];	//鏈接子樹的指針數組
	size_t _size;		//節點中關鍵字的個數
	BTreeNode<K, M>* _parent;	//指向父節點的指針

	BTreeNode()
		:_size(0)
		, _parent(NULL)
	{
		for (size_t i = 0; i < M + 1; i++)
		{
			_sub[i] = NULL;
		}
	}
};

插入數據key:

 M階B樹--M=3:

用例 {53, 75, 139, 49, 145, 36, 101};

template<class K,class V> struct Pair { K _first; V _second; Pair(const K &k = K(), const V& v = V()) :_first(k) , _second(v) {} };

返回值類型確定好的,其它的就好辦了:

查找函數思想:

遍歷關鍵字數組_key[],如果key比它小就 ++i 並繼續往後遍歷
1.如果key=_key[i]則停止遍歷,返回該結構體節點
2.如果key比它大則停止遍歷,此時的子樹_sub[i]指向的關鍵字數組的所有數據都是介於_key[i-1]和_key[i]之間的數據,我們要找的key或許就在其中
3.如果跳出循環則未找到該數據cur=NULL,返回cur的父節點;這時候若是插入key,就插入到parent指向的關鍵字數組中

        //遞歸查找key
	Pair<BTreeNode<K, M>*, int> Find(const K& key)
	{
		BTreeNode<K, M>* parent=NULL;
		BTreeNode<K, M>* cur=_root;
		
		while (cur!=NULL)
		{
			size_t i = 0;
			while (i < cur->_size&&cur->_key[i] < key)
				++i;
			if (cur->_key[i] == key)
				return Pair<BTreeNode<K, M>*, int>(cur, i);	
			// key<_key[i] 則走向與key[i]下標相同的子樹
			parent = cur;
			cur = cur->_sub[i];
		}
		return Pair<BTreeNode<K, M>*, int>(parent, -1);
	}

找到位置後,就可以插入該數據key了

分情況:

1.B-樹為NULL

2.B-樹中已經存在key

3.B-樹中不存在key,先把key以插入排序的方式插入到關鍵字數組中,判斷該關鍵字數組是否已滿,滿了就要進行分裂。注意,這裡的分裂有時可能不止一次!

//插入數據
	bool Insert(K& key)
	{
		// 1.B-樹為空
		if (NULL == _root)
		{
			_root = new BTreeNode<K, M>;
			_root->_key[0] = key;
			++_root->_size;
			return true;
		}

		Pair<BTreeNode<K, M>*, int> ret = Find(key);
		// 2.該數據已經存在
		if (ret._second != -1)	
			return false;

		// 3.插入數據到關鍵字數組
		BTreeNode<K, M>* cur = ret._first;
		BTreeNode<K, M>* sub = NULL;
		while (1)
		{
			int i = 0;
			for ( i = cur->_size - 1; i >= 0; )
			{ // 把大數往後挪,對應子樹也要進行挪動
				if (cur->_key[i] > key)
				{
					cur->_key[i + 1] = cur->_key[i];
					cur->_sub[i + 2] = cur->_sub[i + 1]; 
					i--;
				}
				else
				{
					break;
				}
			}
			cur->_key[i + 1] = key;
			cur->_sub[i + 2] = sub;
			if (sub!=NULL)
				cur->_sub[i+2]->_parent = cur;
			cur->_size++;

			//關鍵字數組未滿,插入成功
			if (cur->_size < M)
				return true;

			//關鍵字數組已滿,需要進行分裂
			int mid = M / 2;
			BTreeNode<K, M>* tmp = new BTreeNode<K, M>;
			int index = 0;
			size_t k;

			for ( k = mid + 1; k < cur->_size; k++)
			{
				tmp->_key[index] = cur->_key[k];
				if (cur->_sub[k] != NULL)
				{
					tmp->_sub[index] = cur->_sub[k];
					cur->_sub[k] = NULL;
					tmp->_sub[index]->_parent = tmp;
				}
				tmp->_size++;
				cur->_size--;
				index++;
			}
			if (cur->_sub[k] != NULL)
			{
				tmp->_sub[index] = cur->_sub[k];
				cur->_sub[k] = NULL;
				tmp->_sub[index]->_parent = tmp;
			}
			//父節點為空時的鏈接
			if (cur->_parent == NULL)
			{
				_root = new BTreeNode<K, M>;
				_root->_key[0] = cur->_key[mid];
				cur->_size--;
				_root->_sub[0] = cur;
				_root->_sub[1] = tmp;
				_root->_size++;
				
				//鏈接
				tmp->_parent = _root;
				cur->_parent = _root;
				return true;
			}
			//父節點不為空時的鏈接
			key = cur->_key[mid];
			cur->_size--;
			cur = cur->_parent;
			sub = tmp;
		}
	}

  要看完整代碼,可以去我的github查看代碼:https://github.com/Lynn-zhang/BTree

 

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved