程序或算法的时间复杂度
我们为何要考虑二分查找呢,那是因为在面对很大的数据时它能有效的减少程序运行耗费的时间
最多需要猜测的次数与列表长度相同,这被称为线性时间(linear time)。
1.1、标准二分法查找代码
二分查找的前提是给定目标数据应该是单调的,要么按从小到大顺序排,要么按从大到小排,也就是说给定的数组事先要排好序
函数BinarySearch用于查找某个设定的元素,前提是在从小到大排序的数组里查找,size表示数组的里元素的数目,找到则返回元素下标,否则返回0,复杂度为O(log(n))
C版:
#include<stdio.h>
int BinarySearch(int a[], int size, int p)
{
int L = 0;
int R = size - 1;
while(L <= R) //数组区间不为空
{
int mid = L + (R - L) / 2;
if(p == a[mid])
return mid;
else if(p < a[mid])
R = mid - 1;
else
L = mid + 1;
}
return 0;
}
int main()
{
int a[10] = {1, 3, 6, 9, 12, 24, 33, 36, 52, 80};
int n;
scanf("%d", &n);
if(BinarySearch(a,10,n) != 0)
printf("Find it!%d is number : %d\n", n, BinarySearch(a,10,n) + 1);
else
printf("No such digit!\n");
return 0;
}
C++版:
#include<iostream>
using namespace std;
void binarySearch(int data_array[], int element, int len)
{
int low = 0;
int high = len;
while(low <= high)
{
int mid = low + (high - low) / 2;
int guess = data_array[mid];
if(guess == element)
{
cout << "Element found at " << mid + 1<< "th index" << endl;
return;
}
else if(guess > element)
{
high = mid - 1;
}
else
{
low = mid + 1;
}
}
cout << "Element Not Found" << endl;
return;
}
int main()
{
int data_array[] = {2, 10, 23, 44, 100, 121};
int length = sizeof(data_array) / sizeof(int);
binarySearch(data_array, 3, length);
binarySearch(data_array, 2, length);
binarySearch(data_array, 44, length);
return 0;
}
Python版:
def binary_search(list, item):
# low and high keep track of which part of the list you'll search in.
low = 0
high = len(list) - 1
# While you haven't narrowed it down to one element ...
while low <= high:
# ... check the middle element
mid = (low + high) // 2
guess = list[mid]
# Found the item.
if guess == item:
return mid
# The guess was too high.
if guess > item:
high = mid - 1
# The guess was too low.
else:
low = mid + 1
# Item doesn't exist
return None
my_list = [1, 3, 5, 7, 9]
print(binary_search(my_list, 3)) # => 1
# 'None' means nil in Python. We use to indicate that the item wasn't found.
print(binary_search(my_list, -1)) # => None
注:为了防止(L + R)过大溢出,这里mid都设为L + (R - L) / 2.
这里还有一个蓝桥杯题目就是需要二分法,可以看此博文蓝桥杯2017第八届C语言B组省赛习题题解——习题I.分巧克力**加深印象
1.2、变式二分法查找代码
写一个函数LowerBound,在包含size个元素的、从小到大排序的int数组a里查找比给定整数p小,下标最大的元素。找到则返回其下标,否则返回-1
详细C代码如下:
#include<stdio.h>
int BinarySearch(int a[], int size, int p)
{
int L = 0;
int R = size - 1;
while(L <= R) //数组区间不为空
{
int mid = L + (R - L) / 2;
if(p == a[mid])
return mid;
else if(p < a[mid])
R = mid - 1;
else
L = mid + 1;
}
return 0;
}
int LowerBound(int a[], int size, int p)
{
int L = 0;
int R = size - 1;
int Pos = -1;
while(L <= R)
{
int mid = L + (R - L) / 2;
if(p < a[mid])
R = mid - 1;
else
{
Pos = mid;
L = mid + 1;
}
}
return Pos;
}
int main()
{
int a[10] = {1, 3, 6, 9, 12, 24, 33, 36, 52, 80};
int n;
scanf("%d", &n);
// if(BinarySearch(a,10,n) != 0)
// printf("Find it!%d is number : %d\n", n, BinarySearch(a,10,n) + 1);
// else
// printf("No such digit!\n");
if(LowerBound(a, 10, n) != -1)
printf("%d's position is %d\n", n, LowerBound(a, 10, n) + 1);
else
printf("%d's position is %d\n", n, LowerBound(a, 10, n));
return 0;
}
2、二分法求方程的根
求下面方程的一个根 : x3 - 5x2 + 10x - 80 = 0
若求出的根是a,则要求|f(a)| <= 10-6
由高数知识可知,函数的倒数大于0,所以函数是单调递增的,因此我们可以选择一个区域[x1, x2],使得f(x1) < 0,f(x2) > 0,这里要注意的是循环终止条件不再是L > R了,而是f(root) < EPS
具体代码演示如下
#include<stdio.h>
#include<math.h>
#define EPS 1e-6
double f(double n)
{
return n * n * n - 5 * n * n + 10 * n - 80;
}
int main()
{
int i;
double L = 0;
double R = 100;
double root = L + (R - L) / 2;
while(fabs(f(root)) > EPS)
{
if(f(root) > 0)
R = root;
else
L = root;
root = L + (R - L) / 2;
}
printf("%lf\n",root);
return 0;
}
3、找一对数
输入n(n <= 100000)个整数,找出其中的两个数,它们之和等于整数m(假定肯定有解)。题中所有整数都能用int表示
方案一:先用快速排序给所给整数排序,然后给l,r初始化分别为0和n - 1,如果sum > m,则让r–,如果sum < m,则进行l++(关于快速排序不懂的可以看这篇博文算法四:分治(归并排序和快速排序经典代码))
#include<stdio.h>
void swap(int *a, int *b)
{
int tmp = *a;
*a = *b;
*b = tmp;
}
void QuickSort(int a[], int l, int r)
{
if(l >= r)
return;
int i, j, k;
i = l;
j = r;
k = a[i];
while(i != j)
{
while(i < j && a[j] >= k)
{
j--;
}
swap(&a[i], &a[j]);
while(i < j && a[i] <= k)
{
i++;
}
swap(&a[i], &a[j]);
}
QuickSort(a, l, i - 1);
QuickSort(a, i + 1, r);
}
int main()
{
int a[100000];
int i;
int n;
scanf("%d", &n);
for(i = 0; i < n; i++)
scanf("%d", &a[i]);
int m;
scanf("%d", &m);
QuickSort(a, 0, n - 1);
int l = 0;
int r = n - 1;
int sum = a[l] + a[r];
while(sum != m)
{
if(l > r)
{
printf("No such couple!\n");
return 0;
}
if(sum > m)
r--;
else if(sum < m)
l++;
sum = a[l] + a[r];
}
printf("%d = %d + %d\n", m, a[l], a[r]);
printf("\n");
return 0;
}
部分示例运行结果如下:
方案二:快排和二分的完美结合
#include<stdio.h>
void swap(int *a, int *b)
{
int tmp = *a;
*a = *b;
*b = tmp;
}
int BinarySearch(int a[], int size, int p)
{
int l = 0;
int r = size - 1;
int mid = l + (r - l) / 2;
while(l <= r)
{
if(a[mid] < p)
l = mid + 1;
else if(a[mid] > p)
r = mid - 1;
else
return mid;
mid = l + (r - l) / 2;
}
return 0;
}
void QuickSort(int a[], int l, int r)
{
if(l >= r)
return;
int i, j, k;
i = l;
j = r;
k = a[i];
while(i != j)
{
while(i < j && a[j] >= k)
{
j--;
}
swap(&a[i], &a[j]);
while(i < j && a[i] <= k)
{
i++;
}
swap(&a[i], &a[j]);
}
QuickSort(a, l, i - 1);
QuickSort(a, i + 1, r);
}
int main()
{
int a[100000];
int i;
int n;
scanf("%d", &n);
for(i = 0; i < n; i++)
scanf("%d", &a[i]);
int m;
scanf("%d", &m);
QuickSort(a, 0, n - 1);
int k;
int t = 0;
for(k = 0; k < n; k++)
{
int cnt = m - a[k];
if(BinarySearch(a, n, cnt))
{
t = BinarySearch(a, n, cnt);
break;
}
}
if(k < n)
printf("%d = %d + %d\n", m, a[k], a[t]);
else
printf("No such couple!\n");
return 0;
}