您的当前位置:首页正文

二分法求数组最大最小_指下码上横戈行——二分法

2024-11-29 来源:个人技术集锦

今天学习一个很基础的算法——二分法,在接触计算机算法之前,十三就接触过二分法,高中时期数学课上面,求单调函数零点时,采取二分法可以快速地缩小根的取值范围,更快地求得零点,由此可见,二分法可以用来也是主要用来解决查找方面的问题。

使用二分法进行查找需要注意,查找的对象必须是有序的数组,二分查找算法的时间复杂度为logn,是一个性能上比较好的算法了,十三在这里多说一句,二分法的运用不仅仅在算法题上面,在日常生活中,二分法同样是一个可以使用的技巧,尤其是猜测别人的心思的时候,你挨个枚举,别人一直摇头,肯定会觉得这人怎么这么烦人,但如果你懂点二分法,你不直接给出结果,将你猜测的范围分解,逐步试探别人,逼格一下就上去了。

二分法算法流程各大网站也都有,这里十三简单描述,本质就是将一段有序数组分割成两块,同时用right,left标记当前数组的右左两侧,同时使用middle标记数组中间,每次比较需要查找数字var与middle的相对大小,并由两者的大小关系决定将继续在左半个区间还是右半个区间使用二分法进行查找,并依次进行下去,如下图所示

二分法示例图

二分法的逻辑理清楚了,代码也就不难写了,容易出错的地方就是二分法的查找算法的终止条件,其实就是while语句的循环条件该怎么写,这时候数组的左右标兵left right就派上了用场,即让left<=right,完整代码如下:

int binarySearch(int a[], int left, int right, int x){
    int mid;
    while(left <= right){
        mid = (left + right)/2;
        if(a[mid] == x) return mid;
        else if(a[mid] > x) right = mid-1;
        else left = mid + 1;
    }
    return -1;
}

基础的二分法代码如上,在此基础上,如果序列中有多个我们正在查找的数字,这时候我们应该返回哪一个值呢?于是我们引申思考两个问题:

直接上代码

//求序列中第一个大于等于x元素的位置
int lower_bound(int a[], int left, int right, int x){
    int mid;   
    while(left<right){
       mid = (right + left)/2;
       if(x<=a[mid]) right = mid;
       else left = mid +1;
    }
    return left;
}
//求序列中第一个大于x元素的位置
int upper_bound(int a[], int left, int right, int x){
    int mid;
    while(left<right){
       mid = (right + left)/2;
       if(x<a[mid]) right = mid;
       else left = mid +1;
    }
    return left;
}

两道问题的循环终止条件一致,都是在left==right时结束循环,由于都是在找第一某的元素,故返回值都为区间的左侧的left值,在整个函数运行的过程中,你会发现区间长度在缩小,而这两个问题不同的地方就在于两者缩小的方向不一致,第一问侧重于最小的x,第二问侧重于最小的大于x的数(好像有点废话hhh),第一问比第二问代码多的等号确保了x值一直在区间内,并且返回的是按照条件缩小的区间的左值(第一个数值),所以关于求解某序列中第一个什么元素,可以使用下方的模板:

//求序列中第一个满足某条件的元素位置
int solve(int a[], int left, int right, int x){
    int mid;
    while(left<right){
       mid = (right + left)/2;
       if(条件) right = mid;  //按照本条件对区间进行定向缩小
       else left = mid +1;
    }
    return left;
}

未完待续~

显示全文