- 查找表:由同一类型的数据元素构成的集合;
- 关键字:数据元素中某一数据项的值,用以表示一个数据元素;
- 静态查找:查找+提取数据元素属性信息;
- 动态查找:查找 +(插入或删除元素)。
//线性表
for (i=0; i<n; i++){
if (元素i == 查找目标)
查到,退出;
}
//链表
p = No.1元素
while (p != NULL){ //为空则搜索完成
if (p == 查找目标)
查到,退出;
else
p = p->next;//下一个元素
}
- 思想来源:
- 一定范围内,前者想一个数,后者猜
- 并让前者告诉后者“后者猜的数,比想的数大,还是小?”
// 非递归
Int Binary-Search(keytype k, LIST F )
{
int low , up , mid ;
low = 1 ; up = last ; // 设置查找范围
while ( low <= up ){
mid = ( low + up ) / 2 ; // 查找中间值 ≈ 中位数
if ( F[mid].key = = k ) // 中间值即为查找的目标值
return mid ; // 返回中间值“下标”
else if ( F[mid].key > k ) // 中间值大于查找的目标值
up = mid – 1 ; // 左半边查找,且不包含中间值
else // 否则,中间值小于查找的目标值
low = mid + 1 ; // 右半边查找,且不包含中间值
}
return –1; // 返回未找到
}
// 递归
int Bsearch( F , i , j , k )
{
int m;
if (i > j) return -1 ; // 左边界大于右边界,查找范围为空,则查找完毕
else {
m=( i + j ) / 2 ; // 取中间值
if( F[m].key = = k ) // 中间值即为查找的目标值
return m; // 返回中间值“下标”
if( F[m].key < k ) // 中间值小于查找的目标值
return( Bsearch( F , i , m-1 , k) ); // 右半边查找,且不包含中间值
else // 中间值大于查找的目标值
return( Bsearch( F , m+1 , j , k) ); // 左半边查找,且不包含中间值
}
}
索引表 | 22 | 44 | 74 |
数组 | 22 12 13 9 8 | 33 42 44 38 24 | 48 60 58 74 47 |
Keytype k; // 关键字
int blocks; // 整个索引表的长度
int last; // 整个数组的长度 last = blocks * L
index ix LIST F ;
int L ;
Int index_search( k, last, blocks, ix, F,L )
{
int i, j ; // i是索引表坐标,j是数组每块的坐标
i = 0; // 第一位索引表
while (( ix[i] < k) && (i < blocks) ) i++ ;
// 当前查找值小于目标值 且 查找的坐标在索引表范围内
if ( i < blocks ) { // i在范围内则说明上一行代码有查找到,不在范围内则没查找到
j = i*L; // 每块长度为L,要在第i块内查找,数组内的坐标j为i×L
while (( k != F[j].key ) && (j <= (i+1)*L-1 ) && (j < last))
// 当前查找值目标值不同 且 坐标j在范围内 且 j在整个数组范围内(防止查找最后一“块”时越界)
j = j + 1; // 查找块内的下一个查找元素
if ( k == F[ j ].key ) return j ; // 若查找到则返回下标
/* 执行这条语句,有可能是:
1. 上一个while的第一个条件不满足(即查找成功!)
2. 第二/三个条件不满足(即越界≈无目标值,查找失败
*/
}
return –1 ; // 查找失败
}
BST search( keytype k, BST F ) // k:目标值;F:当前查找子树的根节点
{
p = F ;
if ( p == NULL ) return Null ;
else if (k == p->data.key) return p; // 相等,查找成功
else if (k < p->data.key) // 目标值小于根值
return search (k, p->lchild); // 进入左子树
else if (k > p->data.key) // 目标值大于根值
return search (k, p->rchild); // 进入右子树
}
- 核心点:值一定在树的最底端插入
- 因此,从根节点往下搜索合适的插入位置时,一定要到一个***“空位”(NULL)才能新建节点***
Void Insert (Records R, BST &F)
{
if ( F == NULL ) { // 已经查找到底层,可以插入新节点
F = new CellType ; // 新建节点
F->data = R ; // 存值
F->lchild = NULL ; // 预设左右子树都为NULL
F->rchild = NULL ;
}
else if ( R.key < F->data.key ) // 插入的值小于根值,往左子树插入
Insert ( R , F->lchild )
else if ( R.key >= F->data.key ) // 插入的值大于根值,往右子树插入
Insert ( R , F->rchild )
}
删除三类结点:
Void Delete ( keytype k ,BST &F )
{
if ( F != NULL )
if ( k < F->data.key ) Delete( k, f->lchild ) ; // 目标值比根值小,进入左儿子
else if ( k > F->data.key ) Delete( k, f->rchild ); // 目标值比根值大,进入右儿子
else // 找到该元素,开始删除
if ( F->rchild == NULL ) F = F->lchild ; // 一边儿子为空,则用另一边儿子代替
else if ( F->lchild == NULL ) F = F->rchild ;
else F->data = DeleteMin(F->rchild) // 两边都有儿子,进行DeleteMin操作
}
// 取右子树中最小的(即最左儿子),DeleteMin以此为例
// 同理,也可以取左子树最大
Records DeleteMin( F ) // F:右子树的根
{
records tmp ;
BST p ;
if ( F->lchild == NULL ) { // 已经找到最左儿子
p = F ;
tmp = F->data ; // 暂存节点的信息
F = F->rchild ; // 该节点删除,右儿子补上(无左儿子 and 可能是空)
Delete p ; // 删除p(当前F)节点
return tmp ; // 返回data给上个函数要删除的点,即将右子树的最左儿子的移到删除点的位置
}
else
return ( DeleteMin( F->lchild ) ; // 未找到最左,继续往左找
}
- 平衡树是具有如下性质的二叉查找树:其左子树和右子树都是高度平衡的二叉树,且左子树和右子树高度之差的绝对值不超过1。
- 结点的平衡因子 BF (Balanced Factor) 定义为:结点左子树与右子树的高度之差。
- 平衡树中的任意结点的 BF 只可能是 -1,0,+1
每次插入时,发现有任意一点平衡因子不是-1,0,+1,则进行旋转操作
主要涉及2点的旋转
主要涉及3点的旋转
- 方法与二叉查找树相同
- 平均查找长度
- 相同节点时,期望相同
- 平衡树方差小
B-树(Balanced-Tree):B-树是一种非二叉的查找树
除了要满足查找树的特性,还要满足以下结构特性。一棵 m 阶的 B- 树:
(1)树的根或者是一片叶子(一个节点的树),或者其儿子数在 2 和 m 之间;
(2)除根外,所有的非叶子结点的孩子数在 m/2 和 m 之间;
(3)所有的叶子结点都在相同的深度。
B-树上的查找有两个基本步骤:
查找操作的时间为:
关键问题:
- 装载因子α = 表中装入的记录数 / 哈希表的长度
装填因子α标志着哈希表的装满程度,α越小,发生冲突的可能性越小,反之,发生冲突的可能性越大。- 成功查找平均查找长度ASLs:查找到散列表中已存在结点的平均比较次数。
失败查找平均查找长度ASLu:查找失败,但找到插入位置的平均比较次数。
散列函数分类:
构造方法:
构造Hash函数应注意以下几个问题:
开放定址法
再散列法
链地址法
建立一个公共溢出区
具体内容请返回上述总结和书本中查看