折半查找(二分法)

折半查找是很有意思的,它的算法复杂度非常低,但它要求数据必须是已经排好序的。比如数组 a 中:

13  45  78  90  127  189  243  355

现在看看怎么用折半算法在其中查找 243。
1) 先定义一个变量 key 用于存放要查找的 243:

key = 243

2) 定义变量 low、mid和high 分别存储数组的最小下标、中间下标和最大下标。并有:

mid = (low+high)/2 = (0+7)/2 = 3

3) 此时 a[3]=90,而 key>90,说明 243 在 90 的右边,则往后查找:

low = mid + 1 = 4

4) 然后重新更新 mid:

mid = (4+7)/2 = 5

5) 此时 a[5]=189,而 key>189,说明 243 在 189 的右边,继续往后查找:

low = mid+1 = 6

6) 然后重新更新 mid:

mid = (6+7)/2 = 6

7) 此时 a[6]=key=243,找到了。

下面再来怎么查找 78:
1) key=78,mid=(low+high)/2=(0+7)/2=3。

2) 此时 a[3]=90,而 key<90,说明 78 在 90 的左边,则往前查找:

high = mid-1 = 2

3) 然后重新更新 mid:

mid = (0+2)/2 = 1

4) 此时 a[1]=45,而 key>45,说明 78 在 45 的右边,则往后查找:

low = 1+1 = 2

5) 然后重新更新 mid:

mid = (2+2)/2 = 2

6) 此时 a[2]=key=78,就找到了。

若所查找的在数据序列中没有呢?比如查找 123:
1) key=123,mid=(low+high)/2=(0+7)/2=3。

2) 此时 a[3]=90,而 key>90,说明 123 在 90 的左边,则往后查找:

low = mid+1 = 4

3) 然后重新更新 mid:

mid = (4+7)/2 = 5

4) 此时 a[5]=189,而 key<189,说明 123 在 189 的左边,则往前查找:

high=mid-1=4。

5) 此时 low==high,如果该数仍不是要找的数的话,说明该序列中就没有该数了。

则代码为

# include <stdio.h>
int main(void)
{
    int a[] = {13, 45, 78, 90, 127, 189, 243, 355};
    int key;  //存放要查找的数
    int low = 0;
    int high = sizeof(a)/sizeof(a[0]) - 1;
    int mid;
    int flag = 0;  //标志位, 用于判断是否存在要找的数
    printf("请输入您想查找的数:");
    scanf("%d", &key);
    while ((low <= high))
    {
        mid = (low + high) / 2;
        if (key < a[mid])
        {
            high = mid - 1;
        }
        else if (a[mid] < key)
        {
            low = mid +1;
        }
        else
        {
            printf("下标 = %d\n", mid);
            flag = 1;
            break;
        }
    }
    if (0 == flag)
    {
        printf("sorry, data is not found\n");
    }
    return 0;
}

 


例子:

题目描述:

小民来到了朋友家,但无论他怎么敲门,朋友都没有把门打开。

原来,朋友家里柴火不够了,他正在院子里劈柴。小明的朋友有强迫症,每次劈柴之前都要计算好柴火的长度,规则如下:有x根木头,现在想要把这这些木头劈成k段长度为y的柴火,木头允许存在剩余的情况,劈开木头得到的柴火长度必须为整数,求每段柴火y的最大长度是多少。

举个例子,现在有两根木头的长度分别为13和20,需要把它们劈成6段,则柴火的最大长度为5。

 

输入格式

第一行是两个正整数x,k。分别表示木头的数量,需要得到的柴火的数量。

接下来 x 行,每行一个正整数 Li,表示每一根木头的长度。

数据范围:1≤x≤10e5,1≤k≤10 e8,1≤Li≤10e8(i∈[1,n])。

 

输出格式

仅一行,即 y的最大值。

如果连1为长度的柴火都劈不出来,输出 0。

 

样例

输入 #1

3 7

229

120

465

 

输出 #1

114

 

代码:

#include<bits/stdc++.h>
using namespace std;

int main()
{
    int n,k;
    int sum=0;
    long long int m[100001];
    cin>>n>>k;
    int l=0,r=1e8+1,x;//l能取的最小值 
    for(int i=0;i<n;i++)
    cin>>m[i];
    while(l+1<r)//二分 
    {
        sum=0;
        x=(l+r)/2;
        for(int i=0;i<n;i++)
        sum+=m[i]/x;
        //判断
        if(sum>=k) //段数大于等于需求段,切得过短 
        l=x;
        else//段数不够,过长 
        r=x;
    }
    cout<<l;
    return 0;
}

 

posted @ 2021-12-01 10:11  [吃瓜][吃瓜]  阅读(175)  评论(0编辑  收藏  举报