1. 程式人生 > >【資料結構】平衡搜尋樹之---B樹的演算法實現

【資料結構】平衡搜尋樹之---B樹的演算法實現

#include<iostream>
using namespace std;

#ifndef __BTREE_H__
#define __BTREE_H__

template<class K,int M=3>//設為三階B樹(每個陣列三個關鍵字)
struct BNode
{
	BNode<K,M>* _parent;
	size_t _size;//元素個數
	K keys[M];//由於subs多了一個長度
	BNode<K,M> *subs[M+1];//為了使插入元素分裂時先講該數放進陣列中再調整而又不越界
	BNode()
		:_parent(NULL)
		,_size(0)
	{
		for (int i = 0; i <= M + 1; i++)
		{
			subs[i] = NULL;//指標陣列每個元素都為空指標,key不初始化,sub初始化為空指標
		}
	}
};

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


template<class K,int M=3>
class BTree
{
	typedef BNode<K,M> Node;
public:
	BTree()
		:_root(NULL)
	{}


	Pair<Node*,int> Find(const K& key)
	{
		if (_root == NULL)
			return Pair<Node*,int>(NULL,-1);
		//思路:根據元素的大小以及遍歷每個陣列的關鍵字判斷要走哪一條支路,再逐層逐層查詢
		Node* cur = _root;
		Node* parent = NULL;
		while (cur)
		{
			int index = 0;
			while (index < cur->_size)
			{
				if (cur->keys[index] > key)
				{
					break;
				}
				else if (cur->keys[index] < key)
				{
					index++;
				}
				else
				{
					return Pair<Node*, int>(parent, index);
				}
			}
			parent = cur;
			cur = cur->subs[index];
		}
		return Pair<Node*, int>(parent, -1);
		//找完也沒找到,為了使得該情況下方便插入節點,因此返回parent,插入節點插在parent上
	}


	bool Insert(const K& key)
	{
		//思路:若無結點,插入根節點;若有節點,找到合適位置插入在根節點上,如果此時插上該節點後
		//元素個數_size=M,則分裂,向上調整中位數,若調整後仍然是這樣,則繼續調整,直到不滿足。
		if (_root == NULL)
		{
			_root = new Node;
			_root->keys[0] = key;
			_root->subs[0] = NULL;
			_root->_parent = NULL;
			_root->_size++;
			return true;
		}
		//若有該節點了,則不插入重複節點!!!
		if (Find(key)._second != -1)//不重複
		{
			return false;
		}
		Node* cur = Find(key)._first;//!!!根據查詢到的位置直接插,避免程式碼冗餘
		Node* sub = NULL;
		int insertKey = key;
		while (1)
		{
			_Insert(cur, sub, insertKey);//先將該元素key插入,放在該陣列的合適位置
			if (cur->_size < M)//插入後,無需分裂
			{
				return true;
			}
			//需要分裂		
			int index = 0;
			Node* tmp = new Node;
			int mid = (cur->_size-1) / 2;
			//拷貝key值
			for (int i = mid + 1; i < cur->_size; i++)
			{
				tmp->keys[index++] = cur->keys[i];
				tmp->_size++;
			}
			//拷貝關鍵字的下標
			for (int i = mid + 1; i <= cur->_size; i++)
			{
				tmp->subs[index++] = cur->subs[i];
				if (cur->subs[i])//有子鏈,鏈上
				{
					cur->subs[i]->_parent = tmp;
				}
			}
			cur->_size = (cur->_size - 1) / 2;//更新cur陣列元素個數
			if (cur->_parent == NULL)
			{
				//分兩條支路,左支路為cur,右支路為tmp鏈上
				_root = new Node;
				_root->keys[0] = cur->keys[mid];
				_root->subs[0] = cur;
				_root->subs[1] = tmp;
				_root->_size++;
				cur->_parent = _root;
				tmp->_parent = _root;
				return true;
			}
			else
			{			
				insertKey = cur->keys[mid];//該插入mid位置元素
				sub = tmp;//子串便成為tmp
				cur = cur->_parent;//繼續調整
			}
		}
		
	}


protected:
	void _Insert(Node* cur, Node* sub,const K& key)
	{
		size_t index = cur->_size;
		while (index >= 0 && cur->keys[index]>key)//為便於key查到合適位置,先將元素往後移動1個長度到合適位置
		{
			cur->keys[index+1] = cur->keys[index];
			cur->subs[index + 1] = cur->subs[index];//下標也要更新
			index--;
		}	
		cur->keys[index+1] = key;
		cur->subs[index + 2] = sub;
		if (sub)
		{
			sub->_parent = cur;
		}
		cur->_size++;
	}
private:
	Node* _root;
};
#endif //__BTREE_H__

測試程式碼:

#define _CRT_SECURE_NO_WARNINGS 1
#include "BTree.h"

void BTreeTest()
{
	BTree<int,3> btree;
	int a[] = { 53, 75, 139, 49, 145, 36, 101 };
	for (int i = 0; i < sizeof(a) / sizeof(a[0]); i++)
	{
		btree.Insert(a[i]);
	}
	btree.Find(5);
}
int main()
{
	BTreeTest();
	system("pause");
	return 0;
}