二分查找(折半查找)——数组中的重复数字

二分查找(折半查找)——数组中的重复数字

  • 二分查找(折半查找)
  • 数组中的重复数字

二分查找(折半查找)

二分查找是一种算法,其输入是一个有序的元素列表(必须是有序的),如果查找的元素包含在列表中,二分查找返回其位置,否则返回NULL

比如说有一个1-100的数字,我随机的选择其中一个数字(假设为60),你需要以最少的次数猜到我所选择的数字,每次猜测后,我会告诉你大了,小了,对了。

假设你第一次从1开始猜,小了

第二次:2 小了

第三次:3 小了

……

第五十九次:59 小了

第六十次:60 对了

这是简单的查找,每次猜测只能排除一个数字,如果我想的数字是100,那么你可能需要从1猜到100了!

那么有没有更好的查找方式呢?

答案当然是有的。

如果我选的数字是60

第一次:你从50开始猜,那么我告诉你小了,就排除了接近一半的数字,因为你至少知道1-50都小了

第二次:你猜75,那么我告诉你大了,这样剩下的数字又少了一半!或许你已经想到了,我们每次猜测都是选择了中间的那个数字,从而使得每次都将余下的数字排除了一半。

第三次:接下来,很明显应该猜测63,大了

第四次:然后你猜56,小了

第五次:然后你猜59 小了

第六次:猜测61,大了

第七次,你就能很明确的告诉我,答案是60!

这样的查找方式,很明显比第一种要高效很多。第一种需要猜测60次才能猜出正确答案,而使用第二种方式,只需要七次就能猜出正确答案

或许看到这里你已经明白了,这就是二分查找的方法。为什么二分查找要求有序,从这里也可以看出来。一般而言,对于包含n个元素的列表,用二分查找最多需要logn步,而简单查找最多需要n步。

#include
using namespace std;
int main() {
	int guess = 499;		//猜测字符
	int a[1000];			//注意这里的数组下标,即a[0]=1,a[1]=2……a[99]=100
	int low = 0, mid, high = 999;
	//初始化
	cout << "1、初始化" << endl;
	for (int i = 0; i < 1000; i++) {
		a[i] = i;
	}
	// 二分查找
	while (low <= high) {		// 有等号
		mid = (low + high) / 2;
		if (a[mid] == guess) {
			cout << "mid:" << mid << endl;
			break;
		}
		else if (a[mid] < guess) low = mid + 1;		// 有+1
		else if (a[mid] > guess) high = mid - 1;	// 有+1
	}
	system("pause");
	return 0;
}

数组中的重复数字

题目:在一个长度为n+1的数组里的所有数字都在1~n的范围内,要求不修改数组找出任一重复数字。

虽然可以将数组复制出来,再对其进行操作。但需要O(n)的辅助空间。

我们可以从1~n的数字从中间的数字m分成两份,前一部分为1~m 后一部分为(m-1)~n。如果在1~m区间中的数超过m则重复的数在这个区间,反之在(m-1)~n这个区间。之后进一步将区间细分。这里就用到了二分查找的思想。

int getDuplication(const int* numbers, int length) {
	if (numbers == nullptr || length <= 0) return -1;

	int start = 1;
	int end = length - 1;
	while (start <= end) {
		int mid = ((end - start) >> 1) + start;
		int count = countRange(numbers, length, start, mid); 
		if (end == start) {
			if (count > 1) return start;
			else break;
		}
		if (count > (mid - start + 1)) end = mid;
		else start = mid + 1;
	}
	return -1;
}

int countRange(const int* numbers, int length, int start, int end) {
	if (numbers == nullptr) return 0;
	int count = 0;
	for (int i = 0; i < length; i++)
		if (numbers[i] >= start && numbers[i] <= end)
			count++;
	return count;
}

你可能感兴趣的:(二分查找(折半查找)——数组中的重复数字)