第一章复习
二分搜索
问题来源:
假定元素满足,线序集合;A[1…n]中有x吗?从头到尾的扫描,依次比较:顺序搜索(适合无序的集合)如果存在等于x的,返回其下标;否则返回0;
算法解决:
j = 1; //设定初始值
while (j <= n) {
//while (循环条件){
if(A[j] == x) // 比较和更新;
{
//}
return j; // return 0;
}
j= j + 1;
}
return 0;
最小查找时间? 最好情况,A[1]=X;
平均查找时间?P(i)=1/n,T(n)=n/2;
最大查找时间?最坏情况,x不在A[1…n]或者x=A[n],复杂度为n;
二分搜索:
有序(非降序)的集合(Binary Search);
int low = 1, high = n; //设定初始值
while (low <= high) {
//while(循环条件){
mid = [ (low + high) / 2]; // 比较和更新;
if (A[mid] == x) //}
{
//return 0;
return mid;
}
if(A[mid] > x)
{
high = mid - 1;
}
else {
low = mid + 1;
}
}
return 0;
二分搜索的比较次数分析:
-
最少比较一次;
-
最多呢?
在while循环中,第一次循环之后A[mid+1,…,n]剩余的元素个数最多为:n/2 或者 (n-1)/2
若n为偶数,令n=2*k,则最多剩余k个;
若n为奇数,令n=2*k+1,则最多剩余k个;
最多剩余的个数总是:⌊n/2⌋;
接着在第二次循环之后剩余的元素个数为:⌊⌊n/2⌋/2⌋=⌊n/4⌋。(证明方法来源于教材的定理2.1)
快速证明:
1.在10进制的情况下:
123除以10等于12.3;3568除以10等于356.8;相当于小数点向左移一位;
2.同理在2进制的情况下:
小数点也是向左移一位,并且舍去小数位;
所以除以4就相当于小数点连续左移两位,并且舍去小数位;
那么,在第j-1次循环之后,剩余的元素个数为:⌊n/2(j-1)⌋(2的j-1次方);
所以,j=⌊㏒n⌋+1(以2为底取对数)。
最多循环次数,最多比较次数都是j=⌊㏒n⌋+1。
合并两个已排序的表
问题来源:
两个数组A[p…q],A[q+1…r]已经是有序排列了(不妨设为非降序)
例如:
输入:3,7,9,12;1,2,4,13,14作为两个数组
输出:1,2,3,4,7,9,12,13,14;
【方法一】:使用辅助数组
基本思想:
-
三个数组
-
A[p…q],A[q+1…r],B[p…r]
-
3个指针:s,t,k
s初始化时各自指向A[p] -》 t初始化时各自指向A[q+1] -》 k初始化时各自指向B[p],暂存器 -》
比较A[s],A[t],小的值存入B[k] -》 小的指针+1,形成新比较对,存入k+1 -》 某组已到尾部,将另一组尚未比较的复制到B -》 B回写到A
s=p,t=q+1,k=p; //设定初始值
while (s <= q && t <= r){
//while (循环条件){
if (A[s]<A[t]){
// 当A[s]小于A[t]时,将A[s]给B[k]
B[k]=A[s]; // s+1形成新的比较对
s++;
}
else{
// 同上,道理相同
B[k]=A[t];
t++;
}
k++; // k+1,将新比较对结果,放入B[k+1]
} //}
if(s>q){
//当一个数组全部处理完时,将另外一个数组尚未比较的数字复制到B
B[k....r]=A[t....r];
}
else {
B[k...r]=A[s...q];
}
A[p....r]=B[p....r]; //B回写到A;
return 0; //return 0;
观察结论:
-
辅助数组算法(Merge)将n1=q-p+1和n2=r-q大小的两个数组合并成一个n=n1+n2的数组,(n1≤n2)元素的比较次数为n1到n-1之间。
特例:如果两个数组的大小为**⌊n/2⌋和⌈n/2⌉**,需要比较的次数在⌊n/2⌋到n-1之间。
【方法二】:改进后的Merge算法
优化思路:
可以省去一部分由A写到B再由B回写A的重复步骤;
if(s>q){
B[k....r]=A[t....r]; //可以直接省去这一步,直接将A[t...r]不动即可
}
else{
B[k.....r]=A[s....q]; //可以将A[s....q]直接赋值给A[k....r]
}
A[p....r]=B[p....r