mid = (low+high)/2(向下取整,也是 C 语言除法运算的特性)
,则:【例】若 low 和 high 之间有 10 个元素,则
mid = (low+high)/2 = (1+10)/2 = 11/2 = 5
,所以 mid 的左半部分有 4 个元素,右半部分有 5 个元素。
mid = (low+high)/2(向上取整,或按照 C 语言规则写成 (low+high)/2+1)
,则:【例】若 low 和 high 之间有 10 个元素,则
mid = (low+high)/2 + 1 = (1+10)/2 + 1 = 11/2 + 1 = 6
,所以 mid 的左半部分有 5 个元素,右半部分有 4 个元素。
【推论】在分块查找中,若对索引表进行向下取整的折半查找时,若索引表不包含目标关键字,则折半查找最终停在 low > high,要在 low 所指的分块查找;若对索引表进行向上取整的折半查找时,若索引表不包含目标关键字,则要在 high 所指的分块查找。
【注】在 1.2 节判定树的画法中将体会到这一点。
【证明】判定树是一棵有 n 个结点的非空二叉树,设不同度的结点个数分别为 n0、n1、n2,有
n0 = n2 + 1
,n = n0 + n1 + n2
,将n2
代入消去,得n = 2n0 + n1 - 1
。注意到此时2n0 + n1
即为空指针域的总数,因而总结点数比空指针域总数少 1。
log2(n+1)(向上取整)
或log2n(向下取整)+1
,即查找成功最多需要log2(n+1)(向上取整)
次,时间复杂度为 O(log2n)。向下取整的折半判定树的画法:
向上取整的折半判定树的画法:
【例】画出一棵共 12 个结点的查找判定树(向下取整):
ASL = ∑(查找次数*概率)
ASL(成功) = ∑(查找成功次数*概率)
,其中概率 = 1/成功结点总数
ASL(成功) = ∑(查找失败次数*概率)
,其中概率 = 1/失败结点总数
以上图为例:
关键字:6,8,10,13,19,21,28,32,36,40,45
若二叉排序树有 n 个结点,则树高(不包含失败结点)为log2(n+1)(向上取整)
或log2n(向下取整)+1
,即查找成功最多需要log2(n+1)(向上取整)
次,时间复杂度为 O(log2n)。插入和删除操作的时间复杂度需要 O(n)。
在二叉排序树中删除并插入某结点,得到的排序二叉树一般与原来不同。
折半查找判定树唯一,但二叉排序树不唯一,其结构与关键字的插入顺序有关。
假设以 nh 表示深度为 h 的二叉平衡树中含有的最少结点数(可得到最大深度的二叉平衡树),则有 n0 = 0,n1 = 1,n2 = 2,并且nh = nh-1 + nh-2 + 1
。
#include
using namespace std;
// 二叉树结点声明
typedef struct TreeNode{
TreeNode *left;
TreeNode *right;
int data;
TreeNode (int x): left(NULL), right(NULL), data(x) {}
} TreeNode;
// 二叉排序树的建立
TreeNode *insert (int x, TreeNode *root){
if (root == NULL){
root = new TreeNode(x);
}
else if (x < root->data){
root->left = insert(x, root->left);
}
else{
root->right = insert(x, root->right);
}
return root;
}
// 前序遍历
void PreOrder (TreeNode *root){
if (root != NULL){
printf("%d ", root->data);
PreOrder(root->left);
PreOrder(root->right);
}
}
// 中序遍历
void InOrder (TreeNode *root){
if (root != NULL){
InOrder(root->left);
printf("%d ", root->data);
InOrder(root->right);
}
}
// 后序遍历
void PostOrder (TreeNode *root){
if (root != NULL){
PostOrder(root->left);
PostOrder(root->right);
printf("%d ", root->data);
}
}
int main(){
int n;
TreeNode *T;
while (scanf("%d", &n) != EOF){
T = NULL;
for (int i = 0; i < n; i++){
int x;
scanf("%d", &x);
T = insert(x, T);
}
PreOrder(T); printf("\n");
InOrder(T); printf("\n");
PostOrder(T); printf("\n");
}
return 0;
}
删除操作:可先将元素按序排列,然后找到待删除元素的左右两个元素,挑选任意一个元素替代这个待删除元素即可。
如上图:元素排好序后为 1,2,3,5,6,7,8,10,11,12,则删除元素 2,可选择元素 1 去替代它(法二),也可以选择元素 3 去替代它(法一)。
因为实际的二叉树形态很复杂,因此需要一个简便的方法用来快速解题(例子见下节):
【例 1】插入序列 {15,3,7,10,9,8}
【例 2】插入序列 {1,2,3,4,5,6,7}
【例 3】插入序列 {2,1,5,3,8,9,10,11,4,7,6}
假设一棵 m 阶 B 树,则满足以下特性:
每个结点最多有 m 棵子树,最多含有 m-1 个关键字。
根结点(如果它不是终端结点)至少有两棵子树。
所有非叶结点(根结点除外)最少有┍m/2┑
棵子树(即最少有 m 的一半过数这么多),最少含┍m/2┑-1
个关键字。
【推论】结点的关键字个数范围为
┍m/2┑-1 ≤ n ≤ m-1
。
logm(n+1) ≤ h ≤ log┍m/2┑((n+1)/2) + 1
。【证明 1】结点最多(且每个结点含关键字数最多,高度最小)的情况:B 树每个结点最多有 m 棵子树、m-1 个关键字,所以关键字总数的范围为
n ≤ (m-1)(1+m+m2+…+mh-1) = mh-1
,反解出 h 的范围:logm(n+1) ≤ h
。
【证明 2】结点最少(且每个结点含关键字数最少,高度最大)的情况:设
k = ┍m/2┑
,B 树每个结点最少有 k 棵子树、k-1 个关键字。第 1 层最少有 1 个结点,第 2 层至少有 2 个结点,第 3 层至少有 2k 个结点,第 4 层至少有 2k2 个结点,以此类推,第 h 层至少有 2kh-2 个结点;因而,第 1 层最少有 1 个关键字,第 2 层至少有 2(k-1) 个关键字,第 3 层至少有 2k(k-1) 个关键字,第 4 层至少有 2k2(k-1) 个关键字,以此类推,第 h 层至少有 2kh-2(k-1) 个关键字。所以,关键字总数的范围为n ≥ 1 + 2(k-1) + 2k(k-1) + 2k2(k-1) + … + 2kh-2(k-1) = 1 + (k-1)(1 + k + k2 + … + kh-2) = 1 + 2(kh-1 - 1)
,反解出 h 的范围:h ≤ logk((n+1)/2) + 1 = log┍m/2┑((n+1)/2) + 1
。
【例 1】高度为 5 的 5 阶 B 树至少有几个结点和关键字?至多有几个结点和关键字?
【解】a. 结点最少的情况:根结点有 2 棵子树,其他非叶结点有 3 棵子树,设高度为 h,则结点总数为
1 + 2 + 2*31 + 2*32 +…+ 2*3h-2 = 1 + 2 * (30 + 31 +…+ 3h-2) = 1 + 2 * 1/2 * (3h-1 - 1) = 3h-1
,代入 h=5 得结点总数为 81。b. 关键字最少的情况:在结点总数最少的情况下,根结点有 1 个关键字,其他非叶结点有 2 个关键字,设高度为 h,则关键字总数为
1 + 2 * (2 + 2*31 + 2*32 +…+ 2*3h-2) = 1 + 4 * (30 + 31 +…+ 3h-2) = 1 + 4 * 1/2 * (3h-1 - 1) = 1 + 2 * (3h-1 - 1)
,代入 h=5 得关键字总数为 161。c. 结点最多的情况:所有结点都有 5 棵子树,设高度为 h,则结点总数为
1 + 5 + 52 + 53 +…+ 5h-1 = 1/4 * (5h - 1)
,代入 h=5 得结点总数为 781。d. 关键字最多的情况:在结点总数最多的情况下,根结点和其他非叶结点有 4 个关键字,设高度为 h,则关键字总数为
4 * (1 + 5 + 52 + 53 +…+ 5h-1) = 5h - 1
,代入 h=5 得关键字总数为 3124。
【例 2】一棵 3 阶 B 树中有 2047 个关键字,则此 B 树的最大高度是多少?最小高度是多少?
【解】a. 最大高度:即结点总数和关键字总数都是最少的情况,根结点有 2 棵子树(1 个关键字),其他非叶结点有 2 棵子树(1 个关键字)。则关键字总数为
1 * (1 + 2 + 22 + 23 +…+ 2h-1) = 2h - 1 = 2047
,解得 h=11。b. 最小高度:即结点总数和关键字总数都是最多的情况,所有结点都有 3 棵子树(2 个关键字)。则关键字总数为
2 * (1 + 3 + 32 + 33 +…+ 3h-1) = 2 * (3h - 1) = 2047
,解得 h=7。
【例 3(变形)】在一棵有 15 个关键字的 4 阶 B 树中,含关键字的结点个数最多几个?
【解】因为题目所求的是最多的结点数量,所以在关键字总数一定(15 个)的情况下,让每个结点都存储最少的关键字数。即根结点有 1 个关键字,其他非叶结点有 1 个关键字,推出每个非叶结点有 2 棵子树。因此关键字总数为
1 * (1 + 2 + 22 + 23 +…+ 2h-1) = 1 * (2h - 1) = 2h - 1 = 15
,解得 h=4。易知,含关键字的结点有 15 个。
【例 4(变形)】在一棵高度为 2 的 5 阶 B 树中,所含关键字的个数至少几个?
【解】因为题目所求的是最少的关键字总数,所以在高度一定的情况下,要求结点数最少,且每个结点所含关键字尽可能的少。因此,根结点有 1 个关键字,2 棵子树;其他非叶结点有 2 个关键字。因为高度为 2,所以第一层是根结点,第二层是两个非叶结点,关键字总数为 1+2*2=5。
一棵 3 阶 B 树,插入 90、插入 25、插入 45、删除 60、删除 80:
(略)