算法三:二分查找(与快排相结合)

程序或算法的时间复杂度

我们为何要考虑二分查找呢,那是因为在面对很大的数据时它能有效的减少程序运行耗费的时间
在这里插入图片描述
最多需要猜测的次数与列表长度相同,这被称为线性时间(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;
}

后续我会根据题型的价值把更多典例添加到该算法栏目中。如果有什么好的题目也可以推荐我哦,评论区欢迎各位。

如果喜欢我的文章,请记得三连哦,点赞关注收藏,你的每一个赞每一份关注每一次收藏都将是我前进路上的无限动力 !!!↖(▔▽▔)↗感谢支持,下期更精彩!!!

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值