适用于外查找的平衡树——B树

什么是B树:

        1970年,R.Bayer和E.mccreight提出了一种适用于外查找的树,它是一种平衡的多叉树,称为B树(或B-树、B_树)。在B-树中查找给定关键字的方法是,首先把根结点取来,在根结点所包含的关键字K1,…,kj查找给定的关键字(可用顺序查找二分查找法),若找到等于给定值的关键字,则查找成功;否则,一定可以确定要查的关键字在某个Ki或Ki+1之间,于是取Pi所指的结点继续查找,直到找到,或指针Pi为空时查找失败。



图解B树的插入和分裂:



下面是关于B树的代码,以及涉及到的遍历B树的面试题

“test.cpp”

#define _CRT_SECURE_NO_WARNINGS 1
#include <iostream>
using namespace std;
#include "BTree.h"

void Test1()
{
	BTree<int,3> bt;
	bt.Insert(20);
	bt.Insert(30);
	bt.Insert(10);
}
void Teat2()
{
	BTree<int,3> bt;
	int arr[] = {53,75,139,49,145,36,101};
	int size = sizeof(arr)/sizeof(arr[0]);
	for (size_t i = 0;i < size;i++)
	{
		bt.Insert(arr[i]);
	}
	bt.InOrder();
}
int main()
{
	//Test1();
	Teat2();
	system("pause");
	return 0;
}


"BTree.h"

<strong><span style="font-size:18px;">#pragma once

template<class K,int M>
struct BTreeNode
{
	K _key[M];//多给了一个位置是为了方便处理分裂
	BTreeNode<K,M>* _subs[M+1];//多给了一个位置是为了方便处理分裂
	BTreeNode<K,M>* _parent;
	size_t _size;//存了几个关键字

	//构造函数
	BTreeNode()
		:_parent(NULL)
		,_size(0)
	{
		for (size_t i = 0;i < M;i++)
		{
			_key[i] = K();
			_subs[i] = NULL;
		}
		_subs[M] = NULL;
	}
};

template<class K,int M>
class BTree
{
	typedef BTreeNode<K,M> Node;
public:
	BTree()//构造函数
		:_root(NULL)
	{}
	pair<Node*,int> Find(const K& key)
	{
		Node* parent = NULL;
		Node* cur = _root;

		while (cur)
		{
			size_t i = 0;
			for (i = 0;i < cur->_size;)
			{
				if (cur->_key[i] == key)
				{
					return pair<Node*,int>(cur,i);
				} 
				else if (cur->_key[i] > key)
				{
					break;
				}
				else
				{
					i++;
				}
			}
			parent = cur;
			cur = cur->_subs[i];
		}
		return pair<Node*,int>(parent,-1);//_root == NULL 的情况下返回
		                                  //找不到的情况下返回
	}
	bool Insert(const K& key)
	{
		//第一个数据插入的情况
		if (_root == NULL)
		{
			_root = new Node;
			_root->_key[0] = key;
			_root->_size = 1;
			return true;
		}

		pair<Node*,int> ret = Find(key);
		if (ret.second != -1)
		{
			//表明key值已经存在,则无需插入,错误返回
			return false;
		}

		Node* cur = ret.first;
		K newkey = key;
		Node* sub = NULL;
		while (1)
		{
			InsertKey(cur,newkey,sub);
			if (cur->_size < M)
			{
				return true;
			}
			//分裂
			int div = M / 2;
			Node* tmp = new Node;//先创建一个新的结点
			int index = 0;
			size_t i = 0;
			for (i = div + 1;i <cur->_size;i++)
			{
				//拷贝右半区间的数据到新结点
				tmp->_key[index++] = cur->_key[i];
				tmp->_size++;
				cur->_key[i] = K();
			}
			index = 0;
			for (i = div+1;i<cur->_size;i++)
			{
				//拷贝右半区间的孩子结点到新结点
				tmp->_subs[index++] = cur->_subs[i];
				if (cur->_subs[i])
				{
					cur->_subs[i]->_parent = tmp;
				}
			}

			cur->_size = cur->_size - tmp->_size - 1;//中位数往上提
			if (cur->_parent == NULL)
			{
				_root = new Node;

				_root->_key[0] = cur->_key[div];
				cur->_key[div] = K();
				
				_root->_subs[0] = cur;
				cur->_parent = _root;
				_root->_subs[1] = tmp;
				tmp->_parent = _root;
				
				_root->_size = 1;
				
				return true;
			}
			else
			{
				newkey = cur->_key[div];
				cur->_key[div] = K();
				cur = cur->_parent;
				sub = tmp;
			}
		}
		return true;//只要能跳出while(1)的死循环,就一定有返回值的了,
		            //这里的返回值是没有意义的
	}
	void InOrder()
	{
		_InOrder(_root);
		cout<<endl;
	}
private:
	void _InOrder(Node* root)
	{
		if (root == NULL)
		{
			return;
		}
		size_t i = 0;
		for (i = 0;i < root->_size;i++)
		{
			_InOrder(root->_subs[i]);
			cout<<root->_key[i]<<" ";
		}
		_InOrder(root->_subs[i]);
	}
	void InsertKey(Node* cur,const K& key,Node* sub)
	{
		int end = cur->_size-1;//让end指向数据的最后一个位置
		while (end >= 0)
		{
			if (cur->_key[end] > key)
			{
				cur->_key[end+1] = cur->_key[end];
				cur->_subs[end+2] = cur->_subs[end+1];
				end--;
			}
			else
			{
				//end->_key < key
				break;
			}
		}
		cur->_key[end+1] = key;
		cur->_subs[end+2] = sub;
		if (sub)
		{
			//如果孩子存在
			sub->_parent = cur;
		}
		cur->_size++;
	}
protected:
	Node* _root;
};</span></strong>


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值