B_TREE B+TREE(C 实现)

只经过少量测试, malloc() 没有判断 是否为空

delete insert 假设 传进来的不是NULL



B_TREE


gcc 编译


 
 /*
 * precursor 返回值应该是 k 而不是指针,返回指针有可能会降到一个度为T-1的结点上
 * 指针重复释放
 * 使用宏错误 T ,使用根结点也是 T.
 * 文件的操作
 * 只经过少量的测试,可能还有问题没有测试出来
 * 把思路理清楚很重要
 * del 函数代码过长
 * 采用最小度方法,对M阶B树,删除要回溯还不明白
 */



#include <stdio.h>
#include <stdlib.h>


#define T	(2)
#define NOEXIST	(-1)	
#define ROOT(_x_)	(_x_->c[1])
#define MAX	(2*T)


/* error : 2T
 * confuse : T and ROOT(T)
 */


struct btree
{
	int key[MAX];                    //key[0] no used
	struct btree *c[MAX+1];
	int leaf;
	int n;
};


struct btree *allocate_node()
{
	struct btree *x = (struct btree *)malloc(sizeof(struct btree));
	x->n = 0;
	x->leaf = 1;
	int i = 0;
	for (i = 0; i < MAX; ++i)
	{
		x->c[i] = NULL;
		x->key[i] = 0;
	}
	x->c[MAX] = NULL;
	return x;
}





void split_child(struct btree *x, int i, struct btree *y)
{
	struct btree *z = allocate_node();
	z->leaf = y->leaf;
	z->n = T-1;
	int j = 0;
	for (j = 1; j <= T-1; ++j)
	{
		z->key[j] = y->key[j+T];
	}
	if (!y->leaf)
	{
		for (j = 1; j <= T; ++j)
		{
			z->c[j] = y->c[j+T];
		}
	}
	y->n = T-1;
	for (j = x->n+1; j >= i+1; --j)
	{
		x->c[j+1] = x->c[j];
	}
	x->c[i+1] = z;
	for (j = x->n; j >= i; --j)
	{
		x->key[j+1] = x->key[j];
	}
	x->key[i] = y->key[T];
	x->n += 1;
}


void insert_nofull(struct btree *x, int k)
{
	int i = x->n;
	if (x->leaf)
	{
		while (i >=1 && x->key[i] > k)
		{
			x->key[i+1] = x->key[i];
			--i;
		}
		i += 1;
		x->key[i] = k;
		x->n += 1;
	}
	else
	{
		while (i >= 1 && x->key[i] > k)
		{
			--i;
		}
		i += 1;
		if (MAX-1 == x->c[i]->n)
		{
			split_child(x, i, x->c[i]);
			if (k > x->key[i])
			{
				i += 1;
			}
		}
		insert_nofull(x->c[i], k);
	}
}


void insert(struct btree *H, int k)
{
	struct btree *r = ROOT(H);
	if (MAX-1 == r->n)
	{
		struct btree *s = allocate_node();
		s->leaf = 0;
		s->n = 0;
		s->c[1] = r;
		ROOT(H) = s;
		split_child(s, 1, r);
		insert_nofull(s, k);
	}
	else
	{
		insert_nofull(r, k);
	}
}


int binsearch(struct btree *x, int k)
{
	int left = 0;
	int right = x->n+1;
	while (left+1 != right)
	{
		int mid = left + (right-left)/2;
		if (x->key[mid] < k)
		{
			left = mid;
		}
		else
		{
			right = mid;
		}
	}
	if (right != x->n+1 && x->key[right] == k)
	{
		return right;
	}
	return -1;
}


struct btree *search(struct btree *x, int k)
{
	while (NULL != x)
	{
		int left = 0;
		int right = x->n+1;
		while (left+1 != right)
		{
			int mid = left + (right-left)/2;
			if (x->key[mid] < k)
			{
				left = mid;
			}
			else
			{
				right = mid;
			}
		}
		if (right != x->n+1 && k == x->key[right])
		{
			return x;
		}
		else
		{
			x= x->c[right];
		}
	}
	return NULL;
}


int precursor(struct btree *x)
{
	while (NULL != x->c[x->n+1])
	{
		x = x->c[x->n+1];
	}
	return x->key[x->n];
}


int successor(struct btree *x)
{
	while (NULL != x->c[1])
	{
		x = x->c[1];
	}
	return x->key[1];
}


//将 x->key[i] 与z 合并到 y中  
void merge_node(struct btree *x, int i, struct btree *y, struct btree *z)  //free(z)  ??
{
	y->key[T] = x->key[i];
	int j = 0;
	for (j = 1; j <= T-1; ++j)
	{
		y->key[j+T] = z->key[j];
	}
	if (!z->leaf)
	{
		for (j = 1; j <= T; ++j)
		{
			y->c[j+T] = z->c[j];
		}
	}
	y->n += T;
	for (j = i; j < x->n; ++j)
	{
		x->key[j] = x->key[j+1];
	}
	for (j = i+1; j < x->n+1; ++j)
	{
		x->c[j] = x->c[j+1];
	}
	x->n -= 1;
	free(z);
}


// 删除时 递归删除时 (del, k`)那么怎么删除的值赋给k呢?
// case 1  . x 叶子节点 k在x中,则把k删除,那么如果 x 的度为t-1 ??这里一定会因为从根下降到叶子 <√>
// 会和其他叶子合并
// case 2  . k 在 x 中,x 是内结点.则                                       
// case 2a . y = c[i] (y->n >= t),找到 k 的 precurson (检查是否存在) 递归的删除.             <√>
// case 2b . z = c[i+1] (z->n >= t),找到 k 的 successor (检查是否存在) 递归删除              <√>
// case 2c . y z 都只有 t-1, 那么将 y z merge 并将 k 下降到 y` 中, free(z)
// case 3  . k 不在 x 中,找到位置,如果 x->c[i] >= t,则递归的删除.                            <√>
// case 3a . x->c[i] == t-1 那么将它的 brother->n >= t 的一个key上升到 x,并将 x 中 key 下降到 x->c[i]中
// case 3b . brother->n == t-1 那么将 x->c[i] 与 brother merge 并将 x 中 key 下降到 x>c[i]` 中. free(brother).
// 每次下降都保证 结点至少有 T 个结点
//返回删除后结点的指针


struct btree *del(struct btree *H, struct btree *x, int k)   //x != NULL;
{
	if (NULL == x)
	{
		return NULL;
	}
	int pos = binsearch(x, k); 
	while (!x->leaf || -1 != pos)
	{
		if (-1 != pos && x->leaf)
		{
			int i = pos;
			while (i < x->n)
			{
				x->key[i] = x->key[i+1];
				++i;
			}
			x->n -= 1;
			return x;
		}
		else if (-1 != pos && !x->leaf)  //这里会不会出现 z 的key == t-1,不会
		{
			struct btree *y = x->c[pos];
			struct btree *z = NULL;
			int new_k = 0;
			if (y->n >= T)                     //success
			{
				new_k = precursor(y);              //这里出现错误,当返回来的是叶子节点并且节点在叶子中,在进入循环时x->n==0
				k = new_k;                         //那么ROOT(H) 就会变成NULL;而实际上并不是这样
				x->key[pos] = k;
				x = y;
			}
			else if (x->c[pos+1]->n >= T)    //success
			{
				y = x->c[pos+1];
				new_k = successor(y);
				k = new_k;
				x->key[pos] = k;
				x = y;
			}
			else                          //success
			{
				y = x->c[pos];
				z = x->c[pos+1];
				merge_node(x, pos, y, z);   //将x->key[pos] z合并到y中, 合并后 x 为空,要处理一下
				if (0 == x->n)
				{
					ROOT(H) = y;            //这里一定是root 因为不会将到 度为T-1的节点上.
					free(x);
					x = NULL;
				}
				//free(z);                 //重复释放两次
				x = y;
			}
		}
		else
		{
			int i = x->n;
			while (i >= 1 && x->key[i] > k)      
			{
				--i;
			}
			i += 1;
			if (x->c[i]->n >= T)                //success
			{
				x = x->c[i];
			}
			else
			{
				/*这里还要check是否存在 
				 * pre
				 * next
				 * */
				struct btree *pre = NULL;
				struct btree *cur = x->c[i];
				struct btree *next = NULL;
				if (i-1 >= 1 && x->c[i-1]->n >= T)          //success
				{
					pre = x->c[i-1];
					int j = 0;
					for (j = cur->n; j >= 1; --j)
					{
						cur->key[j+1] = cur->key[j];
					}
					cur->key[1] = x->key[i-1];
					if (!pre->leaf)
					{
						for (j = cur->n+1; j >= 1; --j)
						{
							cur->c[j+1] = cur->c[j]; 
						}
						cur->c[1] = pre->c[pre->n+1];
					}
					cur->n += 1;
					x->key[i-1] = pre->key[pre->n];
					pre->n -= 1;
				}
				else if (i <= x->n && x->c[i+1]->n >= T)
				{
					next = x->c[i+1];
					cur->key[cur->n+1] = x->key[i];
					x->key[i] = next->key[1];
					cur->n += 1;
					int j = 0;
					for (j = 1; j <= next->n; ++j)
					{
						next->key[j] = next->key[j+1];
					}
					if (!next->leaf)
					{
						cur->c[cur->n+1] = next->c[1];
						for (j = 1; j <= next->n+1; ++j)
						{
							next->c[j] = next->c[j+1];
						}
					}
					next->n -= 1;
				}
				else if (i-1 >= 1)                 // success
				{
					pre = x->c[i-1];
					merge_node(x, i-1, pre, cur);
					if (0 == x->n)
					{
						ROOT(H) = pre;
						free(x);
						x = pre;
					}
				}
				else                                   //success
				{
					next = x->c[i+1];
					merge_node(x, i, cur, next);
					if (0 == x->n)
					{
						ROOT(H) = cur;
						free(x);
						x = cur;
					}
				}
			}
		}
		pos = binsearch(x, k);
	}
	return NULL;
}


void creat(struct btree *H)
{
	struct btree *x = allocate_node();
	x->leaf = 1;
	x->n = 0;
	ROOT(H) = x;
}


void print_node(struct btree *x)
{
	int i = 0;
	for (i = 1; i < x->n; ++i)
	{
		printf("%d-->", x->key[i]);
	}
	printf("%d\n", x->key[x->n]);        // x->key[i]  如果i没有增加,这里有危险
}


void print_btree(struct btree *x)
{
	if (NULL != x)
	{
		print_node(x);
		int i = 0;
		for (i = 1; i <= x->n+1; ++i)
		{
			print_btree(x->c[i]);
		}
	}
}



int main()
{
	struct btree *H = allocate_node();
	ROOT(H) = NULL;
	while (1)
	{
		printf("\t\t 1 . creat\n");
		printf("\t\t 2 . insert\n");
		printf("\t\t 3 . search\n");
		printf("\t\t 4 . delete\n");
		printf("\t\t 5 . print\n");
		printf("\t\t 0 . exit\n");
		int sel = 0;
		scanf("%d", &sel);
		switch (sel)
		{
		case 1:
			creat(H);
			break;
		case 2:
			{
				int k = 0;
				printf("insert : \n");
				while (EOF != scanf("%d", &k))
				{
					insert(H, k);             // ROOT(H) != NULL;
				}
			}
			break;
		case 3:
			{
				int k = 0;
				printf("search : ");
				scanf("%d", &k);
				struct btree *x = search(ROOT(H), k);
				if (x)
				{
					print_node(x);
				}
			}
			break;
		case 4:
			{
				int k = 0;
				printf("delete : ");
				scanf("%d", &k);
				struct btree *x = del(H, ROOT(H), k);
				if (x)
				{	
					if (0 == x->n) //是不是肯定是根节点, 这里要考虑会不会降到别的节点上
					{
						free(x);
						x = NULL;
						ROOT(H) = NULL;
					}
					else
					{
						print_node(x);
					}
				}
			}
			break;
		case 5:
			{
				print_btree(ROOT(H));
			}
			break;
		case 0:
			{
				return 0;
			}
		default:
			{
				printf("error : input num is error !\n");
			}
			break;
		}
	}
	return 0;
}



B TREE 插入 3 阶不行

5 阶B-TREE

key : ceil(5/2)-1 ~ 5-1 (2 ~ 4)

delete 5 阶时,

当left brother 只有 2 个,right brother也只有2个

那么 merge 就是 变成了 5 个了,超过了范围。





B+TREE

/*1 . 开始的时候因为B+TREE 定义,所以在插入时就卡住了,
 * 看到july的B+TREE 图有疑问,要是一个更小的值插入进来
 * 那么不是要更新所有的index
 * 2 . B+TREE 实现有两种
 * 2a. 度为m的b+tree,内部索引点和叶子节点的  key 最多m-1,
 * pointer最多m
 * 2b.度为m的b+tree,内部索引点和叶子节点的  key 最多m,
 * pointer最多m,根节点至少2个key,下层节点的key是上层"最大值"(或者"最小值")的复写
 * 如果树高是多层的,那么这时内部索引节点的key,可能被复写多次
 * 程序中出现的错误
 * 1 . 39 i <= 2*M 
 * 2 . 338 shift_to_left 为什么i-1
 * 3 . merge_node(x, i-1, pre, x-c[i])
 * 4 . merge shift leaf node 区别
 */
#include <stdio.h>
#include <stdlib.h>




#define M	(2)
#define ROOT(_x_)	(_x_->c[1])  //root of bplustree
#define DATA(_x_)	(_x_->c[1])  //root of leaf




typedef struct BPlusNode
{
	int k[2*M];
	struct BPlusNode *c[2*M+1];
	int leaf;
	int n;
	struct BPlusNode *next;
}BPlusNode;




BPlusNode *allocate_node()
{
	BPlusNode *x = (BPlusNode *)malloc(sizeof(BPlusNode));      // x != NULL;
	x->leaf = 1;
	x->n = 0;
	x->next = NULL;
	int i = 0;
	for (i = 1; i < 2*M; ++i)
	{
		x->k[i] = 0;             // error : x->k[2*M] overflow
		x->c[i] = NULL;
	}
	x->c[2*M] = NULL;
	return x;
}




void creat(BPlusNode *T, BPlusNode *D)
{
	BPlusNode *x = allocate_node();
	//DISK-WRITE(x);
	ROOT(T) = x;
	DATA(D) = x;                        //这里不会出现问题
}                                       //1 . merge 只会向前merge
                                        //2 . shift 最少还会剩下一了


void split_node(BPlusNode *x, int pos, BPlusNode *y)
{
	BPlusNode *z = allocate_node();
	z->leaf = y->leaf;
	z->n = M-1;
	int i = 0;
	for (i = 1; i < M; ++i)
	{
		z->k[i] = y->k[i+M];
	}
	if (!y->leaf)
	{
		for (i = 1; i <= M; ++i)
		{
			z->c[i] = y->c[i+M];
		}
	}
	y->n = M-1;
	if (y->leaf)
	{
		y->n += 1;         // error : leaf 最后一个留在leaf中
	}
	for (i = x->n+1; i > pos; --i)
	{
		x->c[i+1] = x->c[i];
	}
	x->c[pos+1] = z;
	for (i = x->n; i >= pos; --i)   //??这样key 比 c 就多一个?? 
	{                               // 两种实现方法
		x->k[i+1] = x->k[i];
	}
	x->k[pos] = y->k[M]; 
	x->n += 1;
	if (y->leaf)
	{
		z->next = y->next;
		y->next = z;
	}
}




void insert_nonfull(BPlusNode *x, int key)
{
	int i = x->n;
	if (x->leaf)
	{
		while (i > 0 && key < x->k[i])
		{
			x->k[i+1] = x->k[i];
			--i;
		}
		i += 1;
		x->k[i] = key;
		x->n += 1;
	}
	else
	{
		while (i > 0 && key < x->k[i])
		{
			--i;
		}
		i += 1;
		if (2*M-1 == x->c[i]->n)
		{
			split_node(x, i, x->c[i]);
			if (key > x->k[i])
			{
				i += 1;
			}	
		}
		insert_nonfull(x->c[i], key);
	}
}




void insert_node(BPlusNode *T, int key)
{
	BPlusNode *r = ROOT(T);
	if (2*M-1 == r->n)
	{
		BPlusNode *x = allocate_node();
		ROOT(T) = x;
		x->c[1] = r;
		x->leaf = 0;
		x->n = 0;
		split_node(x, 1, r);
		insert_nonfull(x, key);
	}
	else
	{
		insert_nonfull(r, key);
	}
}


/*为什么会合并,因为 y z num == M-1*/


void merge_node(BPlusNode *x, int pos/*position of key*/, BPlusNode *y, BPlusNode *z)  //从x中拿出一个key && z merge y
{
	int i = 0;
	if (y->leaf)
	{
		for (i = 1; i <= z->n; ++i)   // leaf 不用 y->k[M] = x->k[pos];
		{                         // case 1 . y->k[M-1] == x->k[pos]
			y->k[i+M-1] = z->k[i];// case 2 . x->k[pos] 已经删除
		}
		y->n = 2*M-2;
	}
	else
	{
		y->k[M] = x->k[pos];     //test success
		for (i = 1; i <= z->n; ++i)
		{
			y->k[i+M] = z->k[i]; // 到底 i+M 还是 i+M-1
		}	
		for (i = 1; i <= z->n+1; ++i)
		{
			y->c[i+M] = z->c[i];
		}	
		y->n = 2*M-1;
	}
	for (i = pos; i < x->n; ++i)
	{
		x->k[i] = x->k[i+1];
	}
	for (i = pos+1; i <= x->n; ++i)
	{
		x->c[i] = x->c[i+1];
	}
	x->n -= 1;
	if (y->leaf)
	{
		y->next = z->next;
	}
	free(z);
}




int binsearch(BPlusNode *x, int key)
{
	int left = 0;
	int right = x->n+1;
	while (left+1 != right)
	{
		int mid = left + (right-left)/2;
		if (x->k[mid] < key)
		{
			left = mid;        //error : leaf = mid+1;
		}
		else
		{
			right = mid;
		}
	}
	if (right <= x->n && x->k[right] == key)
	{
		return right;
	}
	return 0;
}




void shift_to_left(BPlusNode *x, int pos, BPlusNode *y, BPlusNode *z)   // leaf test
{
	if (!z->leaf) 
	{
		y->k[y->n+1] = x->k[pos];   // y 记得 +1
	}
	else
	{
		y->k[y->n+1] = z->k[1];	
	}
	x->k[pos] = z->k[1];
	int i = 0;
	for (i = 1; i < z->n; ++i)
	{
		z->k[i] = z->k[i+1];
	}
	if (!z->leaf)    //test
	{
		y->c[y->n+2] = z->c[1];
		for (i = 1; i <= z->n; ++i)
		{
			z->c[i] = z->c[i+1];   //cover 
		}
	}
	y->n += 1;
	z->n -= 1;
}




void shift_to_right(BPlusNode *x, int pos, BPlusNode *y, BPlusNode *z)
{
	int i = 0;
	for (i = x->n; i > 0; --i)
	{
		y->k[i+1] = y->k[i];
	}
	if (!y->leaf)
	{
		y->k[1] = x->k[pos];
		x->k[pos] = z->k[z->n];
	}
	else
	{
		y->k[1] = z->k[z->n];
		x->k[pos] = z->k[z->n-1];  //leaf test
	}
	if (!y->leaf)                  //test
	{
		for (i = y->n+1; i >= 1; --i)
		{
			y->c[i+1] = y->c[i];
		}
		y->c[1] = z->c[z->n+1];
		//z->c[z->n+1] = NULL;
	}
	y->n += 1;
	z->n -= 1;
}


/* b tree case 
 * 1 . key in the node and node is leaf
 * 2 . key in the node and node isn't leaf
 * 2a. prececursor
 * 2b. successor
 * 2c. merge precessor and successor
 * 3 . key isn't in the node
 * 3a. brother (pre or next)
 * 3b. merge brother
 * b+tree 就相当于在内结点中找不到key case 3
 * b+tree
 * 1a.brother
 * 1b.merge brother
 * leaf 相对于 b tree 要另外处理
 **/




BPlusNode *delete_nonone(BPlusNode *x, int key)
{
	while (1)
	{
		if (x->leaf)
		{
			int pos = binsearch(x, key);
			if (pos)
			{	
				int i = pos;
				while (i < x->n)
				{
					x->k[i] = x->k[i+1];
					++i;
				}
				x->n -= 1;
				return x;


			}
			else
			{
				printf("error : no the key int the leaf !\n");
				return NULL;
			}	
		}
		else
		{
			int i = x->n;
			while (i > 0 && x->k[i] >= key)
			{
				--i;
			}
			i += 1;
			BPlusNode *pre = NULL;
			BPlusNode *next = NULL;
			if (x->c[i]->n >= M) 
			{
				x = x->c[i];    //test
			}
			else if (i > 1 && x->c[i-1]->n >= M)
			{
				pre = x->c[i-1];
				shift_to_right(x, i-1, x->c[i], pre); // i 的值这里奇怪
				x = x->c[i];                          // i-1为什么?
			}                                         // pre and x->c[i] 中间的是i-1
			else if (i <= x->n && x->c[i+1]->n >= M)
			{
				next = x->c[i+1];
				shift_to_left(x, i, x->c[i], next);  //要替换key的位置为什么不减1,c[i] and next is i
				x = x->c[i];
			}
			else if (i > 1)
			{
				pre = x->c[i-1];
				merge_node(x, i-1, pre, x->c[i]);  // leaf success
				x = pre;
			}
			else
			{
				next = x->c[i+1];
				merge_node(x, i, x->c[i], next);  //leaf success
				x = x->c[i];
			}
		}
	}
}






BPlusNode *delete_node(BPlusNode *T, int key)
{
	BPlusNode *x = ROOT(T);


	if (1 == x->n)  //特殊情况,有可能删除根节点
	{
		BPlusNode *y = x->c[1];
		BPlusNode *z = x->c[2];
		if (y && z && M-1 == y->n && M-1 == z->n)
		{
			merge_node(x, 1, y, z);
			free(x);
			x = y;
			ROOT(T) = x;
		}
	}
	return delete_nonone(x, key);
}




void print_node(BPlusNode *x);




int search_node(BPlusNode *x, int key)
{
	BPlusNode *y = NULL;
	while (x)
	{
		y = x;
		int i = x->n;
		while (i > 0 && key <= x->k[i])
		{
			--i;
		}
		i += 1;
		x = x->c[i];
	}
	print_node(y);
	return binsearch(y, key);
}




void print_node(BPlusNode *x)
{
	int i = 0;
	for (i = 1; i < x->n; ++i)
	{
		printf("%d--", x->k[i]);
	}
	printf("%d\n", x->k[x->n]);
}




void print_tree(BPlusNode *x)
{
	if (NULL != x)
	{
		print_node(x);
		int i = 0;
		for (i = 1; i <= x->n+1; ++i)
		{
			print_tree(x->c[i]);
		}
	}
}




void print_leaf(BPlusNode *D)                
{
	BPlusNode *x = DATA(D);
	printf("leaf :\n");
	while (NULL != x)
	{
		int i = 0;
		for (i = 1; i < x->n; ++i)
		{
			printf("%d-->", x->k[i]);
		}
		printf("%d\n", x->k[x->n]);
		x = x->next;
	}
}






int main()
{
	BPlusNode *T = allocate_node();
	ROOT(T) = NULL;
	BPlusNode *D = allocate_node();
	DATA(D) = NULL;
	int flag = 1;
	while (flag)
	{
		printf("\n\t\t1 . creat");
		printf("\n\t\t2 . insert");
		printf("\n\t\t3 . delete");
		printf("\n\t\t4 . search");
		printf("\n\t\t5 . print");
		printf("\n\t\t6 . print leaf");
		printf("\n\t\t0 . exit\n");
		int sel = 0;
		scanf("%d", &sel);
		switch (sel)
		{
		case 1:
			{
				creat(T, D);
			}break;
		case 2:
			{
				printf("insert node : \n");
				int key = 0;
				while (EOF != scanf("%d", &key))
				{
					insert_node(T, key);
				}
			}break;
		case 3:
			{
				printf("delete node : \n");
				int key = 0;
				scanf("%d", &key);
				BPlusNode *x = delete_node(T, key);
				if (x)
				{
					if (0 == x->n)
					{
						free(x);
						ROOT(T) = NULL;
						DATA(D) = NULL;
					}
					else
					{
						print_node(x);
					}
				}


			}break;
		case 4:
			{
				printf("search node :\n");
				int key = 0;
				scanf("%d", &key);
				int pos = search_node(ROOT(T), key);
				if (pos)
				{
					printf("found!\n");
				}
			}break;
		case 5:
			{
				print_tree(ROOT(T));
			}break;
		case 6:
			{
				print_leaf(D);
			}break;
		case 0:
			flag = 0;
				break;
		default:;
		}
	}
	return 0;
}




参考:

http://163lixianfeng.blog.163.com/blog/static/137268212011475333290/

http://blog.oldsharp.info/btree_definition/              B TREE 定义详细分析

http://blog.chinaunix.net/uid-20196318-id-3030529.html


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值