内查找:搜索二叉树

外查找:红黑树

B树配合二分查找

    1.数据库索引

    2.文件系统

决定数据库性能:B+树,缓存系统(热数据:常访问)

建索引:另建B树索引

两个字段也可建索引,封装成结构体。

B树特点:

  1. 根节点至少有两个孩子

  2. 每个非根节点有[M/2,M]个孩子

  3. 每个非根节点有[M/2-1,M-1]个关键字,并且以升序排列

  4. key[i]和key[i+1]之间的孩子节点的值介于key[i]、key[i+1]之间

  5. 所有的叶子节点都在同一层

B+树特点:

B+树的定义

B+树是应文件系统所需而出的一种B-树的变型树。一棵m阶的B+树和m阶的B-树的差异在于:

1.有n棵子树的结点中含有n个关键字,每个关键字不保存数据,只用来索引,所有数据都保存在叶子节点。

2.所有的叶子结点中包含了全部关键字的信息,及指向含这些关键字记录的指针,且叶子结点本身依关键字的大小自小而大顺序链接。

3.所有的非终端结点可以看成是索引部分,结点中仅含其子树(根结点)中的最大(或最小)关键字。

通常在B+树上有两个头指针,一个指向根结点,一个指向关键字最小的叶子结点。

一棵m阶的B+树和m阶的B树的异同点在于:

  • 所有的叶子结点中包含了全部关键字的信息,及指向含有这些关键字记录的指针,且叶子结点本身依关键字的大小自小而大的顺序链接。(而B 树的叶子节点并没有包括全部需要查找的信息)

  • 所有的非终端结点可以看成是索引部分,结点中仅含有其子树根结点中最大(或最小)关键字。(而B 树的非终节点也包含需要查找的有效信息)

BTree,B+Tree,B*Tree_Tree

#pragma once
#include<iostream>
using namespace std;
template<class K,int M>
struct BPTreeNoneLeafNode
{
	K _keys[M];
	//BPTreeNoneLeafNode<K, M>* _subs[M];
	void* _subs[M];
	size_t _size;
	BPTreeNoneLeafNode<K, M>* _parent;
};
template<class K,class V,int M>
struct BPTreeLeafNode
{
	K _keys[M];
	BPTreeLeafNode<K, V, M>* _subs[M];
	size_t _size;
	BPTreeNoneLeafNode<K, M>* _parent;
};
//另一种定义方式
template<class K,class V,int M>
struct BPTreeNode
{
	K _keys[M];
	void* _subs[M];
	BPTreeNode<K, V, M>* _parent;
	size_t size;
	bool _IsLeaf;
};
template<class K,class V,int M>
class BPTree
{
public:
	bool Insert(K& key, V& val);
	bool Remove(K& key);
	BPTreeLeafNode<K, V, M>* Find(K& key);
private:
	BPTreeNoneLeafNode<K, M>* _root;
	BPTreeLeafNode<K, V, M>* _data;//链表
};

注:

为什么说B+树比B 树更适合实际应用中操作系统的文件索引和数据库索引?

  • B+树的磁盘读写代价更低

    B+树的内部结点并没有指向关键字具体信息的指针。因此其内部结点相对B 树更小。如果把所有同一内部结点的关键字存放在同一盘块中,那么盘块所能容纳的关键字数量也越多。一次性读入内存中的需要查找的关键字也就越多。相对来说IO读写次数也就降低了。

  • B+树的查询效率更加稳定

    由于非终结点并不是最终指向文件内容的结点,而只是叶子结点中关键字的索引。所以任何关键字的查找必须走一条从根结点到叶子结点的路。所有关键字查询的路径长度相同,导致每一个数据的查询效率相当。

BTree,B+Tree,B*Tree_B_02

B*树


是B+树的变体,在B+树的非根和非叶子结点再增加指向兄弟的指针;B*树定义了非叶子结点关键字个数至少为(2/3)*M,即块的最低使用率为2/3(代替B+树的1/2);

B+树的分裂:当一个结点满时,分配一个新的结点,并将原结点中1/2的数据复制到新结点,最后在父结点中增加新结点的指针;B+树的分裂只影响原结点和父结点,而不会影响兄弟结点,所以它不需要指向兄弟的指针;

B*树的分裂:当一个结点满时,如果它的下一个兄弟结点未满,那么将一部分数据移到兄弟结点中,再在原结点插入关键字,最后修改父结点中兄弟结点的关键字(因为兄弟结点的关键字范围改变了);如果兄弟也满了,则在原结点与兄弟结点之间增加新结点,并各复制1/3的数据到新结点,最后在父结点增加新结点的指针;

所以,B*树分配新结点的概率比B+树要低,空间使用率更高;


BTree,B+Tree,B*Tree_BTree_03