B树——磁盘链式存储数据结构

简介: B树——磁盘链式存储数据结构

B树详解

我们知道,内存的读写速度是远远高于磁盘的读写速度。磁盘的IO成本是极高的,所以,应尽量减少磁盘的访问次数。同样量级的数据,用二叉树和B树(多叉树)存储,树的高度是不同的。树的高度决定了访问磁盘的次数。

B树的性质

一颗M阶B树T,满足以下条件

  1. 每个结点至多拥有M颗子树
  2. 根结点至少拥有两颗子树
  3. 除了根结点以外,其余每个分支结点至少拥有M/2课子树
  4. 所有的叶结点都在同一层上
  5. 有k课子树的分支结点则存在k-1个关键字,关键字按照递增顺序进行排序
  6. 关键字数量满足ceil(M/2)-1 <= n <= M-1

B树添加节点(分裂)

void btree_split_child(btree *T, btree_node *x, int i) {
  int t = T->t;
  btree_node *y = x->childrens[i];
  btree_node *z = btree_create_node(t, y->leaf);
  z->num = t - 1;
  int j = 0;
  for (j = 0;j < t-1;j ++) {
    z->keys[j] = y->keys[j+t];
  }
  if (y->leaf == 0) {
    for (j = 0;j < t;j ++) {
      z->childrens[j] = y->childrens[j+t];
    }
  }
  y->num = t - 1;
  for (j = x->num;j >= i+1;j --) {
    x->childrens[j+1] = x->childrens[j];
  }
  x->childrens[i+1] = z;
  for (j = x->num-1;j >= i;j --) {
    x->keys[j+1] = x->keys[j];
  }
  x->keys[i] = y->keys[t-1];
  x->num += 1;
}
void btree_insert_nonfull(btree *T, btree_node *x, KEY_VALUE k) {
  int i = x->num - 1;
  if (x->leaf == 1) {
    while (i >= 0 && x->keys[i] > k) {
      x->keys[i+1] = x->keys[i];
      i --;
    }
    x->keys[i+1] = k;
    x->num += 1;
  } else {
    while (i >= 0 && x->keys[i] > k) i --;
    if (x->childrens[i+1]->num == (2*(T->t))-1) {
      btree_split_child(T, x, i+1);
      if (k > x->keys[i+1]) i++;
    }
    btree_insert_nonfull(T, x->childrens[i+1], k);
  }
}
void btree_insert(btree *T, KEY_VALUE key) {
  //int t = T->t;
  btree_node *r = T->root;
  if (r->num == 2 * T->t - 1) {
    btree_node *node = btree_create_node(T->t, 0);
    T->root = node;
    node->childrens[0] = r;
    btree_split_child(T, node, 0);
    int i = 0;
    if (node->keys[0] < key) i++;
    btree_insert_nonfull(T, node->childrens[i], key);
  } else {
    btree_insert_nonfull(T, r, key);
  }
}

B树删除节点(借位或合并)

void btree_merge(btree *T, btree_node *node, int idx) {
  btree_node *left = node->childrens[idx];
  btree_node *right = node->childrens[idx+1];
  int i = 0;
  /data merge
  left->keys[T->t-1] = node->keys[idx];
  for (i = 0;i < T->t-1;i ++) {
    left->keys[T->t+i] = right->keys[i];
  }
  if (!left->leaf) {
    for (i = 0;i < T->t;i ++) {
      left->childrens[T->t+i] = right->childrens[i];
    }
  }
  left->num += T->t;
  //destroy right
  btree_destroy_node(right);
  //node 
  for (i = idx+1;i < node->num;i ++) {
    node->keys[i-1] = node->keys[i];
    node->childrens[i] = node->childrens[i+1];
  }
  node->childrens[i+1] = NULL;
  node->num -= 1;
  if (node->num == 0) {
    T->root = left;
    btree_destroy_node(node);
  }
}
void btree_delete_key(btree *T, btree_node *node, KEY_VALUE key) {
  if (node == NULL) return ;
  int idx = 0, i;
  while (idx < node->num && key > node->keys[idx]) {
    idx ++;
  }
  if (idx < node->num && key == node->keys[idx]) {
    if (node->leaf) {
      for (i = idx;i < node->num-1;i ++) {
        node->keys[i] = node->keys[i+1];
      }
      node->keys[node->num - 1] = 0;
      node->num--;
      if (node->num == 0) { //root
        free(node);
        T->root = NULL;
      }
      return ;
    } else if (node->childrens[idx]->num >= T->t) {
      btree_node *left = node->childrens[idx];
      node->keys[idx] = left->keys[left->num - 1];
      btree_delete_key(T, left, left->keys[left->num - 1]);
    } else if (node->childrens[idx+1]->num >= T->t) {
      btree_node *right = node->childrens[idx+1];
      node->keys[idx] = right->keys[0];
      btree_delete_key(T, right, right->keys[0]);
    } else {
      btree_merge(T, node, idx);
      btree_delete_key(T, node->childrens[idx], key);
    }
  } else {
    btree_node *child = node->childrens[idx];
    if (child == NULL) {
      printf("Cannot del key = %d\n", key);
      return ;
    }
    if (child->num == T->t - 1) {
      btree_node *left = NULL;
      btree_node *right = NULL;
      if (idx - 1 >= 0)
        left = node->childrens[idx-1];
      if (idx + 1 <= node->num) 
        right = node->childrens[idx+1];
      if ((left && left->num >= T->t) ||
        (right && right->num >= T->t)) {
        int richR = 0;
        if (right) richR = 1;
        if (left && right) richR = (right->num > left->num) ? 1 : 0;
        if (right && right->num >= T->t && richR) { //borrow from next
          child->keys[child->num] = node->keys[idx];
          child->childrens[child->num+1] = right->childrens[0];
          child->num ++;
          node->keys[idx] = right->keys[0];
          for (i = 0;i < right->num - 1;i ++) {
            right->keys[i] = right->keys[i+1];
            right->childrens[i] = right->childrens[i+1];
          }
          right->keys[right->num-1] = 0;
          right->childrens[right->num-1] = right->childrens[right->num];
          right->childrens[right->num] = NULL;
          right->num --;
        } else { //borrow from prev
          for (i = child->num;i > 0;i --) {
            child->keys[i] = child->keys[i-1];
            child->childrens[i+1] = child->childrens[i];
          }
          child->childrens[1] = child->childrens[0];
          child->childrens[0] = left->childrens[left->num];
          child->keys[0] = node->keys[idx-1];
          child->num ++;
          node->key[idx-1] = left->keys[left->num-1];
          left->keys[left->num-1] = 0;
          left->childrens[left->num] = NULL;
          left->num --;
        }
      } else if ((!left || (left->num == T->t - 1))
        && (!right || (right->num == T->t - 1))) {
        if (left && left->num == T->t - 1) {
          btree_merge(T, node, idx-1);          
          child = left;
        } else if (right && right->num == T->t - 1) {
          btree_merge(T, node, idx);
        }
      }
    }
    btree_delete_key(T, child, key);
  }
}
int btree_delete(btree *T, KEY_VALUE key) {
  if (!T->root) return -1;
  btree_delete_key(T, T->root, key);
  return 0;
}

文章参考与<零声教育>的C/C++linux服务期高级架构线上课学习。有兴趣的同学可以了解下哦。

相关文章
|
2月前
|
存储 安全 数据库
除了 HashMap,还有哪些数据结构可以实现键值对存储?
【10月更文挑战第11天】 除了`HashMap`,其他常见支持键值对存储的数据结构包括:`TreeMap`(基于红黑树,键有序)、`LinkedHashMap`(保留插入顺序)、`HashTable`(线程安全)、`B-Tree`和`B+Tree`(高效存储大量数据)、`SkipList`(通过跳跃指针提高查找效率)及`UnorderedMap`(类似`HashMap`)。选择合适的数据结构需根据排序、并发、存储和查找性能等需求。
|
3月前
|
存储 Java
java数据结构,线性表链式存储(单链表)的实现
文章讲解了单链表的基本概念和Java实现,包括头指针、尾节点和节点结构。提供了实现代码,包括数据结构、接口定义和具体实现类。通过测试代码演示了单链表的基本操作,如添加、删除、更新和查找元素,并总结了操作的时间复杂度。
java数据结构,线性表链式存储(单链表)的实现
|
2月前
|
存储 算法 关系型数据库
数据结构与算法学习二一:多路查找树、二叉树与B树、2-3树、B+树、B*树。(本章为了解基本知识即可,不做代码学习)
这篇文章主要介绍了多路查找树的基本概念,包括二叉树的局限性、多叉树的优化、B树及其变体(如2-3树、B+树、B*树)的特点和应用,旨在帮助读者理解这些数据结构在文件系统和数据库系统中的重要性和效率。
32 0
数据结构与算法学习二一:多路查找树、二叉树与B树、2-3树、B+树、B*树。(本章为了解基本知识即可,不做代码学习)
|
3月前
|
存储 人工智能 C语言
数据结构基础详解(C语言): 栈的括号匹配(实战)与栈的表达式求值&&特殊矩阵的压缩存储
本文首先介绍了栈的应用之一——括号匹配,利用栈的特性实现左右括号的匹配检测。接着详细描述了南京理工大学的一道编程题,要求判断输入字符串中的括号是否正确匹配,并给出了完整的代码示例。此外,还探讨了栈在表达式求值中的应用,包括中缀、后缀和前缀表达式的转换与计算方法。最后,文章介绍了矩阵的压缩存储技术,涵盖对称矩阵、三角矩阵及稀疏矩阵的不同压缩存储策略,提高存储效率。
484 8
|
3月前
|
存储 算法 C语言
数据结构基础详解(C语言): 二叉树的遍历_线索二叉树_树的存储结构_树与森林详解
本文从二叉树遍历入手,详细介绍了先序、中序和后序遍历方法,并探讨了如何构建二叉树及线索二叉树的概念。接着,文章讲解了树和森林的存储结构,特别是如何将树与森林转换为二叉树形式,以便利用二叉树的遍历方法。最后,讨论了树和森林的遍历算法,包括先根、后根和层次遍历。通过这些内容,读者可以全面了解二叉树及其相关概念。
|
3月前
|
存储 机器学习/深度学习 C语言
数据结构基础详解(C语言): 树与二叉树的基本类型与存储结构详解
本文介绍了树和二叉树的基本概念及性质。树是由节点组成的层次结构,其中节点的度为其分支数量,树的度为树中最大节点度数。二叉树是一种特殊的树,其节点最多有两个子节点,具有多种性质,如叶子节点数与度为2的节点数之间的关系。此外,还介绍了二叉树的不同形态,包括满二叉树、完全二叉树、二叉排序树和平衡二叉树,并探讨了二叉树的顺序存储和链式存储结构。
|
3月前
|
存储 算法 C语言
C语言手撕数据结构代码_顺序表_静态存储_动态存储
本文介绍了基于静态和动态存储的顺序表操作实现,涵盖创建、删除、插入、合并、求交集与差集、逆置及循环移动等常见操作。通过详细的C语言代码示例,展示了如何高效地处理顺序表数据结构的各种问题。
|
3月前
|
存储 Java
java数据结构,线性表顺序存储(数组)的实现
文章介绍了Java中线性表顺序存储(数组)的实现。线性表是数据结构的一种,它使用数组来实现。文章详细描述了线性表的基本操作,如增加、查找、删除、修改元素,以及其他操作如遍历、清空、求长度等。同时,提供了完整的Java代码实现,包括MyList接口和MyLinearList实现类。通过main函数的测试代码,展示了如何使用这些方法操作线性表。
|
4月前
|
存储 Java
数据结构中的哈希表(java实现)利用哈希表实现学生信息的存储
这篇文章通过Java代码示例展示了如何实现哈希表,包括定义结点类、链表类、数组存储多条链表,并使用简单的散列函数处理冲突,以及如何利用哈希表存储和查询学生信息。
数据结构中的哈希表(java实现)利用哈希表实现学生信息的存储
|
4月前
|
存储 缓存 算法
深入解析B树:数据结构、存储结构与算法优势
深入解析B树:数据结构、存储结构与算法优势

热门文章

最新文章