折半查找(二分法)
折半查找是很有意思的,它的算法复杂度非常低,但它要求数据必须是已经排好序的。比如数组 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; }