平衡搜索树(三) B-Tree

B树的简介

B 树是为了磁盘或其它存储设备而设计的一种多叉平衡查找树。与红黑树很相似,但在降低磁盘I/0操作方面要更好一些(树的深度较低)。许多数据库系统都一般使用B树或者B树的各种变形结构。
B树与红黑树最大的不同在于,B树的结点可以有许多子女,从几个到几千个。那为什么又说B树与红黑树很相似呢?因为与红黑树一样,一棵含n个结点的B树的高度也为O(lgn),但可能比一棵红黑树的高度小许多,应为它的分支因子比较大。所以,B树可以在O(logn)时间内,实现各种如插入(insert),删除(delete)等动态集合操作。

如下图所示,即是一棵B树,现在要从树种查找字母R(包含x个关键字的内结点,x+1 个子女。所有的叶结点都处于相同的深度,带阴影的结点为查找字母R时要检查的结点):

非叶子结点最多子树个数也称为阶数,一棵M阶(M>2)的B树,是一棵平衡的M路平衡搜索树,满足一下性质:

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

    2. 每个非根节点有[M/2 ,M]个孩子;(M/2取上整)

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

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

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

现在你是不是对B—Tree树有了一个大体的认识了?如果还是一头雾水也没关系,相信接下来你会清楚介个“犹抱琵琶半遮面的姑凉”的真实面貌,让不看美女就闹心的你今晚能睡个好觉    O(∩_∩)O

OK现在我们一起来实现一下这个 B-Tree ......

该怎样实现这个B树呢,它的节点需要什么属性呢~~这是我们要思考的问题:

首先,节点中得有这个树的阶数(孩子节点的指针最多的个数)吧!好的那咱们定义一个M来做为你这个B树的阶数

然后呢,我们是不是得需要一个保存关键字的数组,和一个保存你需要保存关键字对应的信息的数组。比如说:我需要根据一个人的身证号来获取他的个人信息,这个身份证号就是关键字,个人信息就是我们经过检索想要得到的信息;  好的,我们现在就在节点中建立一个  _keys[M],_values[M]    哎哎....  有的小伙伴会说一个节点最多有 M-1(阶数-1)个关键字么,你怎么数组长度定义为M   你憋着急。。后面你就知道它的好处了;

还有什么呢?没错应该是你这个节点中真实保存的关键字的数目,来确定是不是超出规定的阶数减一个了;

接下来,节点中是不是有一个指向孩纸节点的指针数组啊!对  那咱们在结构体中定义一个  Node* _subs[M+1]

还有撒子嘞,哦哦咱们还需要一个指向父亲节点的指针,供回溯时使用。   Node* _parent;

一切就绪 Load。。。

 1 template<class K,class V,int M = 3>
 2 struct BTreeNode
 3 {
 4     typedef BTreeNode<K, V, M> Node;
 5     BTreeNode()
 6         :_size(0), _parent(NULL)
 7     {
 8         for (int i = 0; i < M + 1; ++i)
 9         {
10             _subs[i] = NULL;
11         }
12     }
13     K _keys[M];      //关键字数组
14     V _values[M];    //信息数组
15     Node* _subs[M + 1];   //指向孩子节点的指针数组
16     size_t _size;    //关键字的个数
17      Node* _parent;   //指向父亲点的指针
18 };

节点创建好之后呢,我们来创建这个B-Tree

 1 template<class K,class V,int M = 3>
 2 class BTree
 3 {
 4     typedef BTreeNode<K, V, M> Node;
 5 public:
 6     BTree()
 7         :_root(NULL)
 8     {}
 9 public:
10     Pair<Node*, bool> _Find(const K& key);   //查找
11     bool _Insert(const K& key, const V& value,Node* sub = NULL);  //插入
12     void _Order() { Order(_root);  };  //中序遍历
13     void Order(Node* root);
14
15 protected:
16     Node* _root;
17 };

你没有看错,基本的 B—Tree 结构就是这麽简单滴!现在就让我们来探究一下这背后的玄机....

 1 template<class K, class V, int M = 3>
 2 Pair<BTreeNode<K,V,M>*, bool> BTree<K, V, M>::_Find(const K& key)
 3 {
 4     Node* parent = NULL;
 5     Node* cur = _root;
 6     if (cur == NULL)
 7     {
 8         return Pair<Node*, bool>(cur, false);
 9     }
10     else
11     {
12         while (cur)
13         {
14             int i = 0;
15             for (; (key > cur->_keys[i]) && (i < cur->_size); ++i);
16             if (key == cur->_keys[i])  //找到相应的值了
17             {
18                 return Pair<Node*, bool>(cur, true);
19             }
20             else
21             {
22                 parent = cur;
23                 cur = cur->_subs[i];
24             }
25         }
26         return Pair<Node*, bool>(parent, false);
27     }
28 }
  1 template<class K, class V, int M = 3>
  2 bool BTree<K, V, M>::_Insert(const K& key, const V& value,BTreeNode<K, V, M>* sub = NULL)
  3 {
  4     if (_root == NULL)    //如果根节点为空
  5     {
  6         _root = new Node;
  7         _root->_keys[0] = key;
  8         _root->_values[0] = value;
  9         _root->_size++;
 10     }
 11     else     //根节点不为空
 12     {
 13         Pair<Node*, bool> exist = _Find(key);
 14         if (exist._second)   //如果该关键字已经存在
 15         {
 16             cout << "This Key already exists!" << endl;
 17             return false;
 18         }
 19         else       //B树中还没有此关键字,此时应该插入相应信息
 20         {
 21             Node* cur = exist._first;
 22             Node* before = NULL;
 23             Node* tmp = sub;
 24             K middlekey = key;
 25             V middlevalue = value;
 26             while (1)
 27             {
 28                 //第一次分裂完成之后_keys[middle] 要往父亲节点中插入,
 29                 //父亲节点可能为空
 30                 if (cur == NULL)
 31                 {
 32                     Node* parent = new Node();
 33                     parent->_keys[0] = middlekey;
 34                     parent->_values[0] = middlevalue;
 35                     ++parent->_size;
 36                     parent->_subs[0] = before;
 37                     before->_parent = parent;
 38                     parent->_subs[1] = tmp;
 39                     tmp->_parent = parent;
 40                     _root = parent;
 41                     return true;
 42                 }
 43                 int index = _BinarySearch<K>(cur->_keys, cur->_size, key);
 44                 for (int i = cur->_size; i > index; --i)
 45                 {
 46                     cur->_keys[i] = cur->_keys[i - 1];      //将关键字后移
 47                     cur->_values[i] = cur->_values[i - 1];  //将关键字对应的有效信息后移
 48                     cur->_subs[i + 1] = cur->_subs[i];      //指向孩子节点的指针后移
 49                 }
 50                 cur->_keys[index] = middlekey;       //移动好之后将相应的数据更新
 51                 cur->_values[index] = middlevalue;
 52                 cur->_subs[index+1] = tmp;
 53                 if (tmp)
 54                 {
 55                     tmp->_parent = cur;
 56                 }
 57                 ++cur->_size;
 58
 59                 if (cur->_size < M)  //关键字的个数合法
 60                 {
 61                     return true;
 62                 }
 63                 else     //关键字的个数非法(M个关键字)
 64                 {
 65                     int middle = M / 2;
 66                     int position = 0;
 67                     int size = cur->_size;
 68                     Node* _tmp = new Node();
 69
 70                     for (int i = middle + 1; i <= cur->_size; ++i)    //将右半边分裂出去(_tmp)
 71                     {
 72                         _tmp->_keys[position] = cur->_keys[i];
 73                         _tmp->_values[position] = cur->_values[i];
 74                         _tmp->_subs[position] = cur->_subs[i];
 75                         --cur->_size;
 76                         ++_tmp->_size;
 77                     }
 78                     _tmp->_subs[_tmp->_size] = cur->_subs[size];
 79
 80                     middlekey = cur->_keys[middle];   //往上插入的关键字
 81                     middlevalue = cur->_values[middle];
 82                     --cur->_size;
 83
 84                     before = cur;
 85                     cur = cur->_parent;
 86                     tmp = _tmp;
 87                 }
 88             }
 89         }
 90     }
 91 }
 92
 93
 94
 95 template<class K>
 96 int _BinarySearch(const K* keys, int size,const K& key)
 97 {
 98     assert(keys);
 99     int low = 0;
100     int high = size - 1;
101     while (low < high)
102     {
103         int middle = (high - low) / 2 + low;
104         key > keys[middle] ? (low = middle + 1) : (high = middle - 1);
105     }
106     return (key > keys[low] ? low + 1 : low);
107 }
 1 template<class K, class V, int M = 3>
 2 void BTree<K, V, M>::Order(Node* root)
 3 {
 4     if (root == NULL)
 5     {
 6         return;
 7     }
 8     else
 9     {
10         for (int i = 0; i < root->_size; ++i)
11         {
12             Order(root->_subs[i]);
13             cout << "[" << root->_keys[i] << "]" << " :" << root->_values[i] << endl;
14         }
15         Order(root->_subs[root->_size]);
16     }
17 }
void Test()
{
    BTree<int, string> btree;
    btree._Insert(53, "数据结构");
    btree._Insert(75,"Linux");
    btree._Insert(139,"算法导论");
    btree._Insert(49,"剑指offer");
    btree._Insert(145,"c++ Primer");
    btree._Insert(36,"操作系统");
    btree._Insert(101, "计算机原理");

    /*中序遍历*/
    btree._Order();
}
时间: 2024-11-16 23:04:13

平衡搜索树(三) B-Tree的相关文章

二叉搜索树(Binary Search Tree)

1.什么是二叉搜索树 二叉搜索树(Binary Search Tree)是一棵有序的二叉树,所以我们也可以称它为二叉排序树(不知道二叉树的童鞋,先看看二叉树:传送门).具有以下性质的二叉树我们称之为二叉搜索树:若它的左子树不为空,那么左子树上的所有值均小于它的根节点:若它的右子树不为空,那么右子树上所有值均大于它的根节点.它的左子树和右子树分别也为二叉搜索树. 2.二叉搜索树的结构 二叉搜索树能够高效的进行一下操作:①插入一个数值②查询是否包含某个数值③删除某个数值 根据实现的不同,还可以实现其

编程算法 - 二叉搜索树(binary search tree) 代码(C)

二叉搜索树(binary search tree) 代码(C) 本文地址: http://blog.csdn.net/caroline_wendy 二叉搜索树(binary search tree)能够高效的进行插入, 查询, 删除某个元素, 时间复杂度O(logn). 简单的实现方法例如以下. 代码: /* * main.cpp * * Created on: 2014.7.20 * Author: spike */ /*eclipse cdt, gcc 4.8.1*/ #include <s

编程算法 - 二叉搜索树(binary search tree) 集合(set)和映射(map) 代码(C)

二叉搜索树(binary search tree) 集合(set)和映射(map) 代码(C++) 本文地址: http://blog.csdn.net/caroline_wendy 二叉搜索树(binary search tree)作为常用而高效的数据结构, 标准库中包含实现, 在标准库的集合(set)和映射(map), 均使用. 具体操作代码如下. 代码: /* * main.cpp * * Created on: 2014.7.20 * Author: spike */ /*eclipse

平衡的三叉树

去年3月份,写了一个平衡的三叉树算法包,还写了一个基于逆向最大匹配算法的中文分词算法包.现在,将平衡的三叉树算法包上传.首先看一下包结构: 1.chinese.utility.cfg代码: package chinese.utility.cfg; /** * 获得主词典.量词词典以及扩展词典和扩展停词词典的路径 * @author TongXueQiang * @date 2015/11/25 * @since 1.8 */public interface Configuration {    

平衡搜索树(二) Rb 红黑树

Rb树简介 红黑树是一棵二叉搜索树,它在每个节点上增加了一个存储位来表示节点的颜色,可以是Red或Black.通过对任何一条从根到叶子简单 路径上的颜色来约束,红黑树保证最长路径不超过最短路径的两倍,因而近似于平衡.(性质3.性质4保证了红黑树最长的路径不超过最短路径的两倍) 如图所示: 红黑树是满足下面红黑性质的二叉搜索树 1. 每个节点,不是红色就是黑色的 2. 根节点是黑色的 3. 如果一个节点是红色的,则它的两个子节点是黑色的 4. 对每个节点,从该节点到其所有后代叶节点的简单路径上,均

【算法导论】 第十课 平衡搜索树

树的结构,如果不能保持平衡,那么其搜索性能会大大打折扣,而本节课介绍了几种经典的平衡树,如AVL,2-3-4tree,红黑树等等,然后着重讲了红黑树,接下来就红黑树的基本性质,作一些简短的总结. 首先,红黑树除了具有BST的基本性质外,还额外拥有以下的五大基本性质: 1)每个结点有一个色域,一个结点要么为黑结点,要么为红结点 2)根节点为黑结点 3)每个叶子结点都为黑结点(无键值) 4)每个红结点的父亲都为黑结点,即不可能出现两个红色结点相连的情况 5)从根节点到任意叶节点的路径中的黑色结点数目

平衡搜索树--红黑树 RBTree

红黑树是一棵二叉搜索树,它在每个节点上增加了一个存储位来表示节点的颜色,可以是Red或Black. 通过对任何一条从根到叶子节点简单路径上的颜色来约束树的高度,红黑树保证最长路径不超过最短路径的两倍,因而近似于平衡. 红黑树是满足下面红黑性质的二叉搜索树: 1. 每个节点,不是红色就是黑色的 2. 根节点是黑色的 3. 如果一个节点是红色的,则它的两个子节点是黑色的(不存在连续的红色节点) 4. 对每个节点,从该节点到其所有后代叶节点的简单路径上,均包含相同数目的黑色节点. 思考:为什么满足上面

平衡搜索树

------------------------------------------------------读<算法>后感 一直以来,对于红黑树都没有很好的理解,指导看了<算法>和上了coursera上的公开课,终于算是有了较好的理解,现写下来和大家分享 前言:2-3查找树 虽然二叉搜索树已经很好的解决大多数搜索问题,但是在最坏的情况下的性能还是很差 (~N),为了保证查找的树的平衡.我们引入了3-结点(相较于二叉查找树中的2-结点),并据此构造出2-3查找树.其性质如下: All

并行二分搜索树bianry search tree

二分搜索树是一种设计良好的有序集合,在平衡的情况下,查找search,插入insertion,删除deletion都具有O(logn)的计算时间.本文讨论实现二分搜索树的具体细节. 二分搜索树的每个结点包含key域,以及至多两个孩子结点,并且左孩子小于当前结点的值,右孩子大于当前结点值.为了方便操作,每个结点还需要维护父结点的信息.从上面的描述可以看出,二分搜索树的任何一个子树同样也是二分搜索树. 搜索操作是所有操作的基础.搜索操作可以很容易用递归过程描述:key>cur.key,则搜索右子树: