查找是指在数据集合中寻找满足某种条件的数据元素的过程,用于查找的数据集合则成为查找表,查找表中的数据元素类型是一致的,并且有能够唯一标识出元素的关键字。如果从查找表找出了关键字等于某一个给定值的数据元素,则称为查找成功,否则称查找不成功。
静态查找表:对查找表只进行(查找和检索)
静态查找表建立之后,不能再执行插入或者是删除的操作,查找表页不再发生变化。对应的,如果对查找表还需要执行两种操作,那么这类查找表就是动态查找表。
针对静态查找表,比如顺序查找、折半查找、分块查找等,
动态查找表:(查找、检索、插入、删除)
而对于动态查找表,往往使用二叉平衡树、B-树或者哈希表来处理
对于各种各样的查找算法, 我们用平均查找长度来衡量查找算法的性能,对于含有n个元素的查找表,定义查找成功的平均查找长度为
ASL = (加和)PiCi
Pi是搜索查找表中第i个记录的概率,并且概率的加和是1
Ci是搜索查找表中的第i个元素时直到查找成功为止,表中元素的比较次数,考虑到查找不成功的情况。
查找算法的平均查找长度应该是查找成功的平均查找长度和查找不成功的平均查找长度之和。
在后面的课程中,我们会继续巩固平均查找长度的概念,并且会给同学们介绍顺序查找、折半查找和分块查找。
二分查找
- min = 0, max = 尾巴指针, mid中间指针
- 终止条件(min >= max)
- 如果arr[mid] < x ----min = mid + 1;
- arr[mid] > x ---- max = mid - 1;
- arr[mid] == x ---- 找到结果
二分查找会出现, 跳跃个不停,不能得出结果的情况吗?
111110000 要查找最后那个1
我们把1看作是满足问题条件,0看作是不满足条件;
因此我们把问题模型抽象化为
-
min = 0, max是尾指针;mid = (min + max) / 2
-
arr[mid] == 1, min = mid (不能往后+1,因为不知道这个1是不是最后一个1啊)
-
arr[mid] != 1, max = mid - 1(可以往左看一个,因为这个max == mid 肯定不是满足性质的)
-
min == max,就找到结果
但是不是如此就结束了,加入,我现在区间是0000000
上面的操作还能顺利找到1吗?
显然此时我的min == max,也不能指向我希望的数字,我现在可以默认min = -1,这样不断靠近,最后靠近到-1,才能说明我没有找到啊,
//11111111111000000000000
int binary_search(int *arr, int n) {
int head = 0, tail = n - 1, mid;
while(head <= tail) {
cout << "head = " << head << ", tail = " << tail << endl;
mid = (head + tail + 1) >> 1;
if (arr[mid] == 0) tail = mid - 1;
else head = mid;
if(head == tail) return (arr[head] == 1 ? head : -1);
}
// return (arr[head] == 1 ? head : -1);
}
0000111111,找最前面的一个1
相当于找到一个最先满足条件的值的下标
当前我需要调整和思考的问题是,min是头指针,max是尾指针,mid = (min + max) / 2;
- 如果arr[mid] == 1, min
顺序查找
-
顺序查找(线性查找)
顺序查找算法是最直观的一种查找算法,从线性表的一段出发,逐个比较。
顺序查找分为:对一般的无序线性表的顺序查找和对按关键字有序的线性表的顺序查找。 -
相对于无序查找表,有序查找表可以降低(查找不成功情况下的检索次数)
例子:开辟一个动态的数组,存放数字
折半查找
- 折半查找的时间复杂度稳定为O(logn)
- 折半查找只能应用在有序的顺序表中,因为链表不能够随机访问元素,所以折半查找不能直接应用在链表中
- 折半查找中,中点选择不同的🌲会导致二叉判定树的形态发生变化,但是时间复杂度不会改变
int search(int *data, int length, int value) {
int ans = 0;
int l = 0, r = length - 1;
while (l <= r) {
int mid = (l + r) / 2;
ans++;
if (data[mid] == value) {
printf("%d success\n", ans);
return 0;
} else if (data[mid] > value) {
r = mid - 1;
} else if (data[mid] < value) {
l = mid + 1;
}
}
printf("%d failed\n", ans);
return 0;
}
三分查找
凹凸函数求极值点的问题:例子
-
首先把区间[L, R]平均分成三部分:
m1三分点,m2三分点 -
计算三等分点m1和m2对应的函数值
-
比较两个函数值的大小,
首先,循环终止条件是right - left < 2,而且这时候两者之间只有left left + 1两个元素了
我们让m1 是left + (right -left) / 3
m2是right-(right - left + 2)/ 3,这也就是说尽量让m2往极点靠拢
分块查找
分块查找也被叫做索引顺序查找,在分块查找方法中,我们需要建立一个索引表,索引表包含两类信息:关键字和指针
其中,关键字指的是厄秘一个子表中最大的关键字,指针则表示这个子表中第一个元素在整个表中的下标