一.B树定义:
结点属性:
1.n 表示存储在结点中的关键字个数
2.n 个关键字以非降序存放
3.isleaf 表示是否是叶节点
4.有n+1个指向孩子的指针ci(i:1 n+1),叶节点不定义属性c
5.关键字key对子树中的关键字范围进行分割
6.每个叶节点有相同的深度即h
2.n 个关键字以非降序存放
3.isleaf 表示是否是叶节点
4.有n+1个指向孩子的指针ci(i:1 n+1),叶节点不定义属性c
5.关键字key对子树中的关键字范围进行分割
6.每个叶节点有相同的深度即h
定义最小度数:t>=2
1.除根结点外每个结点至少有t-1个关键字,t个孩子。根结点在树非空的情况下,至少有1个关键字。
2.每个结点至多有2*t-1个关键字,至多有2*t个孩子。当有2*t-1个关键字时,称该结点时满的。
2.每个结点至多有2*t-1个关键字,至多有2*t个孩子。当有2*t-1个关键字时,称该结点时满的。
高度:
结点:
const int Maxn = 10; struct Node { int n; int key[Maxn]; Node* C[Maxn + 1]; bool isleaf; Node(int n, bool is) :n(n), isleaf(is) {} };
类:
class Btree { private: int t; int h; Node * root; pair<Node*, int> Nsearch(Node* t, int k); void split(Node* x, int i, Node* y); void Insert_Nonfull(Node* x, int k); int Predecessor(Node* y); //前驱 int Successor(Node* z); //后继 //x的第i个关键字转移到右子节点 void Shift_Right(Node* x, int i, Node* y, Node* z); //x的第i个关键字转移到左子节点 void Shift_Left(Node* x, int i, Node* y, Node* z); void DeleteNon(Node* x, int k); //合并y,z与x的第i个关键字->合为一个y void Merge(Node* x, int i, Node* y, Node* z); public: pair<Node*, int> search(int k); Btree(int tt) { t = tt; h = 1; root = new Node(0, true); } Btree() {} void Insert(int k); void Delete(int k); void showNode(Node* t) { for (int i = 0; i<t->n; i++) cout << t->key[i] << '\t'; cout << endl; } Node* GetRoot() { return root; } int GetHeight() { return h; } };
二.搜索:
递归实现
pair<Node*, int> Btree::Nsearch(Node* t, int k)//在子树t中查找,返回结点指针和关键字序号(从1开始) { Node* r = root; int i = 0; while (i<t->n&&t->key[i]<k)//k对应哪一个关键字或哪一个子树(排列为非降序) i++; if (i < t->n&&t->key[i] == k)//找到 { return pair<Node*, int>(t, i + 1); } else if (t->isleaf)//查找失败 { cout << k << "SF!!!\n"; return pair<Node*, int>(nullptr, -1); } else return Nsearch(t->C[i], k);//递归查找 } pair<Node*, int> Btree::search(int k) { return Nsearch(root, k); }
二.插入:
分裂:
//传入一个非满的结点x,同时x.C[i]指向的结点为满的。将x.C[i]第t个结点上移到x成为分割y和z的关键字,x.C[i]分裂为两部分,各自有t-1个关键字 //此时两个结点孩子总数为2*(t)=2*t,与分裂前没有变化。新的结点z作为x新的孩子,排在y之后。 void Btree::split(Node* x, int i, Node* y) { Node* z = new Node(t - 1, y->isleaf);//一个新的结点 y->n = t - 1;//更新y的大小 for (int j = 0; j < t - 1; j++)//后t-1个归z z->key[j] = y->key[j + t]; if (!y->isleaf) { for (int j = 0; j < t; j++) z->C[j] = y->C[j + t]; } for (int j = x->n; j > i; j--) //x下标第i到第n-1的关键字后移1位 { x->key[j] = x->key[j - 1]; } x->key[i] = y->key[t - 1]; for (int j = x->n + 1; j > i+1; j--)//x下标第i+1到第n孩子后移一位 { x->C[j] = x->C[j - 1]; } x->C[i+1] = z; x->n++; }
插入:
//辅助过程,要求x非满 void Btree::Insert_Nonfull(Node* x, int k) { int i = x->n - 1; if (x->isleaf)//x为叶节点(非满) { while (i >= 0 && k < x->key[i])//从后找到插入位置,向后移动调整关键字 { x->key[i + 1] = x->key[i]; i--; } x->key[i + 1] = k; x->n++; } else { while (i >= 0 && k < x->key[i])//决定向哪个子结点递归 i--; i++;//目标子结点在关键字之后,序号比关键字大1 if (x->C[i]->n == 2 * t - 1)//分裂 { split(x, i, x->C[i]); if (k > x->key[i]) i++; } Insert_Nonfull(x->C[i], k); } } //沿着树单程向下插入关键字 //当根结点为满。分裂根结点,第t个关键字对应结点成为子女的根,有两个孩子。 void Btree::Insert(int k) { Node* r = root; if (r->n == 2 * t - 1) { root = new Node(0, false); root->C[0] = r;//初始时新根结点为空,分裂时结点上移到根结点 split(root, 0, r); h++; Insert_Nonfull(root, k); } else Insert_Nonfull(r, k); }
三.删除:
从x中删除k,必须保证无论何时x递归调研自身时,x中关键字个数至少为t,比通常最小关键字树多一个。
分如下情况:
Case1:k在结点x中,且x为叶节点,删除k;
Case2:k在结点x中,且x为内部结点
a:若结点x中前于k的子结点y至少有t个关键字,找出k在y中的前驱。递归的删除前驱,并以前驱在x中代替k。
b:对称的,若y只有t-1个结点,则检查x中后于k的结点z,寻找后继。
c:若y和z均只有t-1个关键字,将k和z全部合并进y,释放z并递归的在y中删除k。
Case3:k不在x中,应确定包含k的子树x.C[i]。若x.C[i]只有t-1个关键字,则:
a:其相邻的兄弟结点至少有t个关键字,将x的某一个关键字降至x.C[i],将相邻兄弟结点中的一个关键字升到x。
b:若所有相邻兄弟结点均只有t-1个关键字,将x.C[i]与一个兄弟合并,x的一个关键字移到新合并的结点。
注意i是代表第几个还是0开始的索引。
int Btree::Predecessor(Node* y) //前驱 { Node* x = y; int i = x->n; while (!x->isleaf) { x = x->C[i]; i = x->n; } return x->key[i - 1]; } int Btree::Successor(Node* z) //后继 { Node* x = z; while (!x->isleaf) { x = x->C[0]; } return x->key[0]; } //x的第i个关键字转移到右子节点 void Btree::Shift_Right(Node* x, int i, Node* y, Node* z)//i-1才为索引 { int j = z->n; while (j > 0) { z->key[j] = z->key[j - 1]; j--; } z->key[0] = x->key[i - 1]; x->key[i - 1] = y->key[y->n - 1]; z->n++; if (!z->isleaf) { j = z->n; while (j > 0) { z->C[j] = z->C[j - 1]; } z->C[0] = y->C[y->n]; } y->n--; } //x的第i个关键字转移到左子结点 void Btree::Shift_Left(Node* x, int i, Node* y, Node* z)//i-1才为索引 { y->n++; y->key[y->n - 1] = x->key[i - 1]; x->key[i - 1] = z->key[0]; z->n--; int j = 0; while (j < z->n) { z->key[j] = z->key[j + 1]; j++; } if (!z->isleaf) { y->C[y->n] = z->C[0]; j = 0; while (j <= z->n) { z->C[j] = z->C[j + 1]; j++; } } } void Btree::Merge(Node* x, int i, Node* y, Node* z)//i-1才为C++索引 { y->n = 2 * t - 1; for (int j = t; j < 2 * t - 1; j++) y->key[j] = z->key[j - t]; y->key[t - 1] = x->key[i - 1]; if (!y->isleaf) { for (int j = t; j <= 2 * t - 1; j++) y->C[j] = z->C[j - t]; } for (int j = i; j < x->n; j++) x->key[j - 1] = x->key[j]; for (int j = i; j < x->n; j++) x->C[j] = x->C[j + 1]; x->n--; delete z; } void Btree::DeleteNon(Node* x, int k) { Node* y = nullptr; Node* z = nullptr; int i = 0; if (x->isleaf) //Case1 { while (i < x->n&&k>x->key[i]) { i++; } if (k == x->key[i]) { x->n--; for (int j = i; j < x->n; j++) { x->key[j] = x->key[j + 1]; } } else cout << "DF!!!\n"; } else { while (i<x->n&&k>x->key[i]) { i++; } y = x->C[i]; if (i < x->n) z = x->C[i + 1]; if (k == x->key[i]) //Case2 if (y->n > t - 1) //Case2 a { int k0 = Predecessor(y); DeleteNon(y, k0); x->key[i] = k0; } else if (z->n > t - 1) //Case2 b { int k0 = Successor(z); DeleteNon(z, k0); x->key[i] = k0; } else //Case2 c { Merge(x, i + 1, y, z); DeleteNon(y, k); } else //Case3 { Node* p = nullptr; if (i > 0)//最左端的情况,无左兄弟结点 p = x->C[i - 1];//左兄弟结点,y为目标结点,z为右兄弟结点 if (y->n == t - 1) { if (i > 0 && p->n > t - 1) Shift_Right(x, i, p, y);//i为第几个,不为索引 Case3 a else if (i<x->n&&z->n>t - 1) Shift_Left(x, i + 1, y, z); else if (i > 0) { Merge(x, i, p, y); //Case3 b y = p; } else Merge(x, i + 1, y, z); } DeleteNon(y, k); } } } void Btree::Delete(int k) { Node* r = root; if (r->n == 1) { Node* y = r->C[0]; Node* z = r->C[1]; if (y->n == t - 1 && z->n == t - 1) { Merge(r, 1, y, z); root = y; delete r; DeleteNon(y, k); } else DeleteNon(r, k); } else DeleteNon(r, k); }