Binary_Search(二分算法)

二分查找:在有序数组中查找一个值
通过不断 缩小查找区间 达到查找的目的 调节指针位置
如果arr[mid]<x,min = mid+1;
如果arr[mid]>x,max = mid-1;
如果arr[mid] == x, 找到结果;
时间效率为O(logn)

	#include<iostream>
#include<cstdio>
#include<queue>
#include<stack>
#include<algorithm>
#include<string>
#include<map>
#include<set>
#include<vector>
using namespace std;
int binary_search(int *arr,int n,int x){//数组,总大小,待查值
    int head=0,tail=n-1,mid;
    while(head <=tail){
        mid = head +((tail-head)>>1);//mid = (head+tail)>>1; head和tail同时很大时会越界
        
        if(arr[mid] == x) return mid;
        if(arr[mid]<x)head =mid+1;
        else tail = mid-1;
    }
    return -1;

}

int *getRandData(int n){
    int *arr = (int *)malloc(sizeof(int)*n);
    arr[0] = rand()%10;
    for(int i=1;i<n;i++){
        arr[i] = arr[i-1]+rand()%5+1;
    }
    return arr;
}

void output(int *arr,int n){
    int len = 0;
    for(int i =0;i<n;i++){
        len +=printf("%5d",i);
    }
    printf("\n");
    for(int i=0;i<len;i++) printf("-");
    printf("\n");
    for(int i=0;i<n;i++){
        printf("%5d\t",arr[i]);
    }
    return ;
}   

int main(){
    srand(time(0));
    int x,n;
    scanf("%d",&n);
    int *arr = getRandData(n);
    output(arr,n);
    while(~scanf("%f",&x)){
        printf("arr[%d] = %d\n",binary_search(arr,10,x),x);
    }
    return 0;
}

二分查找的两种泛型情况
第一种情况:00001111111(查找第一个1)
binary_search_01模型:
i

nt binary_search_01(int *arr,int n,int x){
	int head=0,tail=n-1,mid;
	while(head<tail){
		mid = head + ((tail-head)>>1);
		output_binary_process(arr,n,head,tail,mid);
		if(arr[mid]<x)head = mid+1;
		else tail = mid;
	}
	return head;
}

第二种情况:11110000000
代码同理

大的区间使用二分查找,小区间使用顺序查找:(可防止死循环;不需要考虑边界条件)

while(tail-head>3){
	mid = head+((tail-head)>>1);
	if(arr[mid]==x) return mid;
	if(arr[mid]<x) head = mid+1;
	else head = mid -1;
}
for(int i =head;i<=tail;i++){
	if(arr[i] == x) return i;
}
  • 函数是压缩的数组,数组是展开的函数 *
  1. x 的平方根
class Solution {
public:
//10模型(找最后一个1)
/*  如果x是10
 y    1 2 3  4  5 
y^2   1 4 9 16 25
*/
    int mySqrt(int x) {
        double head = 0,tail = x,mid;
        tail +=1;
        for(int i=0;i<100;i++){
            mid = head+((tail-head)/2.0);
            if(mid*mid <= x) head = mid;
            else tail = mid ;
        }
        return floor(head) ;
        
    }
};
 
  1. 搜索插入位置
 class Solution {
public:
//01模型 (找到第一个大于等于1的位置)
    int searchInsert(vector<int>& nums, int target) {
        int head=0,tail = nums.size()-1,mid;
        while(tail -head>3){
            mid = head +((tail -head)>>1);
            if(nums[mid]>=target) tail = mid;
            else head = mid+1;
        }
        for(int i=head;i<=tail;i++){
            if(nums[i] >= target)
            return i;
        }
        return nums.size();//重点!! 因为当 tail为nums.size()-1时 如果某个值不在搜索区间范围内时,应该插入到数组末尾
    }
};
  1. 两数之和

class Solution {
public:
//先排序,再扫描数组中的每一位,寻找在扫描当前数组后面的元素,找到等于target-x的值。(target-x的任务可使用二分)
    int binary_search(vector<int>&nums,vector<int>&ind,int head,int x){
        int tail = ind.size()-1,mid;
        while(head<=tail){
            mid = head + ((tail-head)>>1);
            if(nums[ind[mid]] == x) return mid;
            if(nums[ind[mid]]<x) head = mid+1;
            else tail = mid-1;
        }
        return -1;
    }
    vector<int> twoSum(vector<int>& nums, int target) {
        //首先对下标进行排序
        vector<int> ind(nums.size());//下标数组
        for(int i=0;i<ind.size();i++) ind[i]=i;//初始化下标数组
        sort(ind.begin(),ind.end(),[&](int i,int j){return nums[i]<nums[j];});//对下标数组进行排序
        vector<int> ret(2);
        //对下标数组进行二分
        for(int i=0;i<ind.size();i++){
            int val = nums[ind[i]];
            //j在ind[i+1] -- ind[-1] 进行查找
            int j=binary_search(nums,ind,i+1,target-val);//第三位传入的是二分查找的起始位置
            if(j == -1) continue;
            int a = ind[i];
            int b = ind[j];
            if(a>b) swap(a,b);
            ret[0] = a;
            ret[1] =b;

            
        }
        return ret;
    }
};
  1. 在排序数组中查找元素的第一个和最后一个位置
class Solution {
public:
    //在nums数组中查找第一个>=x的位置 01模型
    int binary_search01(vector<int>&nums,int x){
        int head=0,tail=nums.size()-1,mid;
        while(tail-head>3){
            mid = head+((tail-head)>>1);
            if(nums[mid]>=x) tail = mid;//条件成立,尾指针向前移动
            else head = mid+1;
        }
        for(int i = head;i<=tail;i++){
            if(nums[i]>=x) return i;
        }
        return nums.size();
    }
    vector<int> searchRange(vector<int>& nums, int target) {
        vector<int>ret(2);
        ret[0] = binary_search01(nums,target);
        if(ret[0]==nums.size()||nums[ret[0]]!=target){
            ret[0]=ret[1] = -1;
            return ret;
        }
        ret[1] = binary_search01(nums,target+1)-1;//ret[1]的求法: 寻找第一个大于目标值加1的数 ,然后在该位置减1就是目标值的为后一个数。
        return ret;
    }
};
  1. 将 x 减到 0 的最小操作数
class Solution {
public:
    int binary_search(vector<int>&nums,int x){
        int head=0,tail=nums.size()-1,mid;
        while(head <=tail){
            mid = head+((tail-head)>>1);
            if(nums[mid] == x) return mid;
            if(nums[mid]<x) head = mid+1;
            else tail=mid-1;
        }
        return nums.size();
    }
    int minOperations(vector<int>& nums, int x) {
        //前缀和数组(前缀和数组第一个位置固定为前0个元素的累加之和所以值一定为0,数组大小为nums.size()+1);
        vector<int>suml(nums.size()+1);
        vector<int>sumr(nums.size()+1);
        suml[0] = sumr[0] = 0;
        for(int i=0;i<nums.size();i++) suml[i+1] = suml[i]+nums[i];//数组前半部分的前缀和
        for(int i=nums.size()-1;i>=0;i--) sumr[nums.size()-i] = sumr[nums.size()-i-1] +nums[i];//数组后半部分的前缀和
        int ans =-1;
        for(int i=0;i<suml.size();i++){
            int j= binary_search(sumr,x-suml[i]);
            if(j==-1) continue;
            //选择元素合法性判断:
            if(i+j>nums.size()) continue;//使用的元素数量大于总的元素数量
            
            if(ans ==-1|| ans >i+j) ans = i+j;
        }
        return ans;
    }
};
  1. 供暖器
class Solution {
public:
    int binary_search_01(vector<int>&nums,int x){
        int head = 0,tail =nums.size()-1,mid;
        while(head<tail){
            mid = head+((tail-head)>>1);
            if(nums[mid]>=x)tail = mid;
            else head = mid+1;
        }
        return head;
    }
    int findRadius(vector<int>& houses, vector<int>& heaters) {
        sort(heaters.begin(),heaters.end());
        int ans=0;
        for(auto &x:houses){
            int j=binary_search_01(heaters,x);
            int a = abs(heaters[j]-x);
            int b = (j? x-heaters[j-1]:a+1);
            ans = max(ans,min(a,b));

        }
        return ans;

    }
};
  1. 搜索旋转排序数组 II
class Solution {
public:
    bool search(vector<int>& nums, int target) {
        if(nums[0] == target || nums[nums.size()-1]==target)
            return true;
        int l=0,r=nums.size()-1,mid,head,tail;
        while(l<nums.size() && nums[l] == nums[0]) ++l;
        while(r>=0&& nums[r] == nums[0]) --r;
        head = l,tail = r;
        while(l<=r){
            mid = (l+r)>>1;
            if(nums[mid] == target) return true;
            if(nums[mid]<=nums[tail]){
                if(target<= nums[tail] && target >nums[mid]) l=mid+1;
                else r= mid-1;
            }else{
                if(target<nums[mid] && target>=nums[head])r =mid -1;
                else l =mid +1;
            }
        }
        return false;
    }
};
  1. 寻找两个正序数组的中位数
class Solution {
public:
    double findk(vector<int> &nums1,vector<int>&nums2,int i,int j,int k){
        if(i==nums1.size())return nums2[j+k-1];
        if(j == nums2.size()) return nums1[i+k-1];
        if(k == 1) return nums1[i]<nums2[j] ?nums1[i]:nums2[j];
        int a= min(k/2,(int)nums1.size()-i);
        int b=min(k-a,(int)nums2.size()-j);
        a=k-b;
        if(nums1[i+a-1]<=nums2[j+b-1]){
            return findk(nums1,nums2,i+a,j,k-a);
        }
        return findk(nums1,nums2,i,j+b,k-b);

    } 
    double findMedianSortedArrays(vector<int>& nums1, vector<int>& nums2) {
        int n=nums1.size(),m=nums2.size(),mid = (n+m+1)/2;
        double a = findk(nums1,nums2,0,0,(n+m+1)/2);
        if((n+m)%2==1) return a;
        double b=findk(nums1,nums2,0,0,mid+1);
        return (a+b)/2.0;
    }
};
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值