第四章---查找

问题4.1 寻找脚码

已知一个整数数组x[],其中的元素彼此都不同,而且也已经从小到大排列好。请用比较大小、相等的方式写一个程序,找出给定的数组中是否有一个元素满足x[i] = i的关系。举例而言,如果x[]有2, 1, 3, 7, 8, x[3] = 3, 因此3就是答案。

#include <iostream>

using namespace std;

int findTheIndex(int *array, int size)
{
	if (array == NULL || size <= 0)
		return -1;

	int leftIndex = 1;
	int rightIndex = size;
	while (leftIndex <= rightIndex)
	{
		int midIndex = (rightIndex + leftIndex) / 2;
		if (array[midIndex - 1] == midIndex)
			return midIndex;
		else if (array[midIndex - 1] > midIndex)
			rightIndex = midIndex - 1;
		else
			leftIndex = midIndex + 1;
	}

	return -1;
}

void main()
{
	int array[] = {0, 2, 4, 7, 8};
	const int size = sizeof array / sizeof *array;

	int index = findTheIndex(array, size);
	if (index == -1)
		cout << "can't find it" << endl;
	else
		cout << "index = " << index << endl;
}

问题4.2 寻找固定的和

有两个数组x[]与y[],各有m与n个元素,而且各个元素没有依顺序排列;d是一个已知的值,请写一个程序,看看在x[]和y[]中有没有满足x[i] + y[i] = d的元素。例如,若x[]为3, 7, 2, 4,y[]为1, 5, 2, 3,d为9;那么x[1] + y[2]与x[3] + y[1]都合乎条件,也就是9.

这道题的解题思路不难,如果遍历两个数组,复杂度为O(m * n),这个复杂度显然高了。如何降低复杂度呢?分析如下:

1. 先排序,然后二分查找。问题是对哪个数组排序,如果对m排序,则复杂度为O((m + n) * logm), 如果对n排序,则复杂度为O((m + n) * logn), 对比之下,也就是说对较长的数组排序,遍历较小的数组。

代码如下:

#include <iostream>
#include <algorithm>
#include <iterator>
#include <vector>

using namespace std;

struct Pair
{
	Pair(int i = 0, int j = 0) : left(i), right(j) {}
	int left;
	int right;
};

int binarySearch(int *array, int size, int value)
{
	if (array == NULL || size <= 0)
		return -1;

	int leftIndex = 0;
	int rightIndex = size - 1;
	while (leftIndex <= rightIndex)
	{
		int midIndex = (leftIndex + rightIndex) / 2;
		if (array[midIndex] == value)
			return midIndex;
		if (array[midIndex] > value)
			rightIndex = midIndex - 1;
		else
			leftIndex = midIndex + 1;
	}

	return -1;
}

int findSum(int *lhs, int sizeLhs, int *rhs, int sizeRhs, int sum, vector<Pair> &pvec)
{
	if (lhs == NULL || sizeLhs <= 0 || rhs == NULL || sizeRhs == 0)
		return -1;

	sort(lhs, lhs + sizeLhs);

	Pair pair;
	for (int i = 0; i < sizeRhs; i++)
	{
		int diff = sum - rhs[i];

		int index = binarySearch(lhs, sizeLhs, diff);
		if (index == -1)
			continue;
		else
		{
			pvec.push_back(Pair(lhs[index], rhs[i]));	
		}
	}

	if (pvec.size() == 0)
		return -1;
	else
		return 0;
}

void main()
{
	vector<Pair> pvec;
	int lhs[] = {3, 7, 2, 4};
	int rhs[] = {1, 5, 2, 3};
	const int sizeLhs = sizeof lhs / sizeof *lhs;
	const int sizeRhs = sizeof rhs / sizeof *rhs;
	int sum = 9;
	int result = findSum(lhs, sizeLhs, rhs, sizeRhs, sum, pvec);
	if (result != -1)
	{
		for (int i = 0; i < pvec.size(); i++)
		{
			cout << sum << " = " << pvec[i].left << " + " << pvec[i].right << endl;
		}
	}
	else
	{
		cout << "not exist" << endl;
	}
}

2. 将排序换成hash,复杂度立马降低为O(n)或者O(m)

考虑到标准库里没有hash map,而且hash函数实现起来虽然不难,但不是本题的重点~代码用map来替代

#include <iostream>
#include <algorithm>
#include <iterator>
#include <vector>
#include <map>

using namespace std;

struct Pair
{
	Pair(int i = 0, int j = 0) : left(i), right(j) {}
	int left;
	int right;
};

void constructHash(int *lhs, int size, map<int, int> &imap)
{
	if (lhs == NULL || size <= 0)
		return;

	for (int i = 0; i < size; i++)
		imap[lhs[i]] = 1;
}

int findSum(int *lhs, int sizeLhs, int *rhs, int sizeRhs, int sum, vector<Pair> &pvec)
{
	if (lhs == NULL || sizeLhs <= 0 || rhs == NULL || sizeRhs == 0)
		return -1;

	map<int, int> imap;
	constructHash(lhs, sizeLhs, imap);

	Pair pair;
	for (int i = 0; i < sizeRhs; i++)
	{
		int diff = sum - rhs[i];
		map<int, int>::const_iterator iter = imap.find(diff);
		if (iter == imap.end())
			continue;
		else
		{
			pvec.push_back(Pair(iter->first, rhs[i]));	
		}
	}

	if (pvec.size() == 0)
		return -1;
	else
		return 0;
}

void main()
{
	vector<Pair> pvec;
	int lhs[] = {3, 7, 2, 4};
	int rhs[] = {1, 5, 2, 3};
	const int sizeLhs = sizeof lhs / sizeof *lhs;
	const int sizeRhs = sizeof rhs / sizeof *rhs;
	int sum = 9;
	int result = findSum(lhs, sizeLhs, rhs, sizeRhs, sum, pvec);
	if (result != -1)
	{
		for (int i = 0; i < pvec.size(); i++)
		{
			cout << sum << " = " << pvec[i].left << " + " << pvec[i].right << endl;
		}
	}
	else
	{
		cout << "not exist" << endl;
	}
}

问题4.3 无限式查找

已知一个数组,元素个数有多少并不是很清楚,但是数组元素已经依顺序从小到大排好,而且在数组最后添加了足够多的无穷记号,无穷表示最大的值,比数组中每一个元素都大,而且个数足够多。试编写一个程序,在这个数组中找出某个给定的值。

思路:每次查找1、2、4、8...个数组元素

x[0]

x[1], x[2]

x[3], x[4], x[5], x[6]

x[7], x[8], x[9], x[10], x[11], x[12], x[13], x[14]

如果rightIndex大于size - 1, 则rightindex 取 size - 1即可

#include <iostream>
#include <algorithm>
#include <iterator>
#include <vector>
#include <map>

using namespace std;

#define INF 65535

int array[] = {1, 3, 4, 9, 10, 17, 20, 24, 28, 31, 36, 42, 43, 45, 50, 53, 55, 57, 59, INF, INF, INF, INF, INF, INF, INF, INF, INF};
const int size = sizeof array / sizeof *array;

int binarySearch(int *array, int left, int right, int dest)
{
	if (array == NULL || left < 0 || right < 0 || left > right)
		return -1;

	while (left <= right)
	{
		int mid = (left + right) / 2;
		if (array[mid] == dest)
			return mid;
		else if (array[mid] > dest)
			right = mid - 1;
		else
			left = mid + 1;
	}

	return -1;
}

int findDestValue(int *array, int size, int dest)
{
	if (array == NULL || size <= 0)
		return -1;

	int leftIndex = 0;
	int rightIndex = 0;
	int dist = 1;

	while (leftIndex < size)
	{
		int result = binarySearch(array, leftIndex, rightIndex, dest);
		if (result == -1)
		{
			leftIndex = rightIndex + 1;
			dist *= 2;
			rightIndex = rightIndex + dist;
			if (rightIndex >= size - 1)
				rightIndex = size - 1;
		}
		else
			return result;
	}

	return -1;
}

void main()
{
	for (int i = 0; i < size; i++)
	{
		if (array[i] != INF)
		{
			int result = findDestValue(array, size, array[i]);
			if (result == -1)
				cout << "can't find the value" << endl;
			else
				cout << "result = " << result + 1 << endl;
		}
	}
}


问题4.4 寻找极小值

说一个组数是以循环顺序排列的,这就是说在数组中有个元素i, 自xi起有这样的关系x0 < x1 < x2 < ... < xi-1, xi < xi - 1 < .. < xn < x0;比如说,8, 10, 14, 15, 2, 6这6个元素就是循环排列的,因为自2起递增,到了最后一个元素就转折为第一个元素,再依顺序递增。换句话说,如果把xi, xi+ 1, ..., xn取出,并且接到数组开头,就是一个从小到大的排列顺序(这就是个旋转的动作)。请写一个程序,接受一个以循环顺序排列的数组,把它的极小值找出来,以上面数据为例,程序应该输出2.

思路:每次取midIndex的value时,和最右边的值相比,如果比右边的值大,则leftIndex = midIndex + 1,否则rightIndex = midIndex;

#include <iostream>

using namespace std;

int cyclicMin(int *array, int size)
{
	if (array == NULL || size <= 0)
		return -1;

	int leftIndex = 0;
	int rightIndex = size - 1;
	int midIndex;

	while (leftIndex < rightIndex)
	{
		midIndex = (leftIndex + rightIndex) / 2;
		if (array[midIndex] < array[rightIndex])
			rightIndex = midIndex;
		else
			leftIndex = midIndex + 1;
	}

	return leftIndex;
}

void main()
{
	int array[] = {8, 10, 14, 15, 2, 6};
	const int size = sizeof array / sizeof *array;

	int result = cyclicMin(array, size);
	cout << "result = " << array[result] << endl;
}

问题4.5 两个数组的中位数

已知两个数组x[] 和 y[],各有n个元素,并且已经以从小到大的顺序排好,请写个程序,用少于n次比较找出x[]和y[]合并后的中位数

思路:x[]和y[]的元素个数相同~每次取x[mid]和y[mid],比较二者大小,如果x[mid]比y[mid]小,则去掉x[0]~x[mid - 1]和y[mid + 1]和y[sizeY - 1]之间的元素。

对于n > 2的情形,midIndex肯定不会是0,所以其左右都有元素,那么比较两数组中位数,肯定会有元素可以用来删除

对于n <= 2,则需要特殊考虑:

1. 如果n = 1, 则直接判断即可,取较小的那个值

2. 如果n = 2,则两个数组较小的元素对比、较大的元素对比,去掉最小的元素和最大的元素,然后比较剩下的两个元素,取较小的值即可

代码不难写,如下:

#include <iostream>

using namespace std;

int array1[] = {1, 3, 5, 7, 9};
int array2[] = {2, 4, 6, 8, 10};
const int size = sizeof array1 / sizeof *array1;

int findMidValue(int *array1, int *array2, int size)
{
	if (array1 == NULL || array2 == NULL || size <= 0)
		return -1;

	if (size == 1)
		return min(array1[0], array2[0]);

	if (size == 2)
	{
		int candidate1 = max(array1[0], array2[0]);
		int candidate2 = min(array1[1], array2[1]);
		return min(candidate1, candidate2);
	}

	int leftIndex1 = 0, rightIndex1 = size - 1;
	int leftIndex2 = 0, rightIndex2 = size - 1;

	while (leftIndex1 != rightIndex1 - 1 && leftIndex2 != rightIndex2 - 1)
	{
		int midIndex1 = (leftIndex1 + rightIndex1) / 2;
		int midIndex2 = (leftIndex2 + rightIndex2) / 2;
		if (array1[midIndex1] < array2[midIndex2])
		{
			leftIndex1 = midIndex1;
			rightIndex2 = midIndex2;
		}
		else if (array1[midIndex1] > array2[midIndex2])
		{
			rightIndex1 = midIndex1;
			leftIndex2 = midIndex2;
		} 
		else
		{
			return array1[midIndex1];
		}
	}

	int candidate1 = max(array1[leftIndex1], array2[leftIndex2]);
	int candidate2 = min(array1[rightIndex1], array2[rightIndex2]);
	return min(candidate1, candidate2);
}

void main()
{
	int midValue = findMidValue(array1, array2, size);
	cout << "midValue = " << midValue << endl;
}

问题4.6 寻找中间值

已知一个整数数组x[]有n个元素。对于任意两个相邻元素x[i]与x[i + 1]而言,他们的差的绝对值不会超过1,用数学式来写就是|x[i] - x[i+1]| <= 1,而且x[0] < x[n-1]。请写一个程序,在x[]数组中找出它是否有某一个输入的值a,x[0]<=a<=x[n-1],程序一共做了多少次比较?能够做到少于n次吗?

没能理解这道题的意思。。。

问题4.7 3个数组的共同元素

有3个数组x[], y[]与z[],各有x、y与z个元素,而且三者都已经从小到大依序排列。请写一个程序,找出值最小的共同元素(也就是同时在3个数组中出现,并且值最小的元素),若没有共同元素,请显示合适信息。

思路:其实这道题很简单,而且题目也未曾规定不允许判断相等。。。

#include <iostream>

using namespace std;

int array1[] = {1, 3, 6, 7, 8};
const int size1 = sizeof array1 / sizeof *array1;
int array2[] = {2, 3, 6, 10, 12};
const int size2 = sizeof array2 / sizeof *array2;
int array3[] = {3, 5, 6, 7, 9};
const int size3 = sizeof array3 / sizeof *array3;

int findCommonValue(int *array1, int *array2, int *array3, int size1, int size2, int size3, 
					int &position1, int &position2, int &position3)
{
	if (array1 == NULL || array2 == NULL || array3 == NULL ||
		size1 <= 0 || size2 <= 0 || size3 <= 0)
		return -1;

	position1 = 0;
	position2 = 0;
	position3 = 0;

	while (position1 < size1 && position2 < size2 && position3 < size3)
	{
		if (array1[position1] < array2[position2])
			position1++;
		else if (array2[position2] < array3[position3])
			position2++;
		else if (array3[position3] < array1[position1])
			position3++;
		else
			break;
	}

	if (position1 == size1 || position2 == size2 || position3 == size3)
		return -1;
	else
		return 0;
}

void main()
{
	int position1;
	int position2;
	int position3;
	int result = findCommonValue(array1, array2, array3, size1, size2, size3, position1, position2, position3);
	if (result == -1)
		cout << "can't find it" << endl;
	else
	{
		cout << "position1 = " << position1 << endl;
		cout << "position2 = " << position2 << endl;
		cout << "position3 = " << position3 << endl;
	}

}

很容易想到的一个解法如下:

#include <iostream>

using namespace std;

int array1[] = {1, 3, 6, 7, 8};
const int size1 = sizeof array1 / sizeof *array1;
int array2[] = {2, 3, 6, 10, 12};
const int size2 = sizeof array2 / sizeof *array2;
int array3[] = {3, 5, 6, 7, 9};
const int size3 = sizeof array3 / sizeof *array3;

int findCommonValue(int *array1, int *array2, int *array3, int size1, int size2, int size3, 
					int &position1, int &position2, int &position3)
{
	if (array1 == NULL || array2 == NULL || array3 == NULL ||
		size1 <= 0 || size2 <= 0 || size3 <= 0)
		return -1;

	position1 = 0;
	position2 = 0;
	position3 = 0;

	while (position1 < size1 && position2 < size2 && position3 < size3)
	{
		while (array1[position1] != array2[position2])
		{
			position1++;
			position2++;
		}
		while (array3[position3] <= array2[position2])
		{
			if (array3[position3] != array2[position2])
				position3++;
			else
				return 0;
		}
		position1++;
		position2++;
	}

	if (position1 == size1 || position2 == size2 || position3 == size3)
		return -1;
	else
		return 0;
}

void main()
{
	int position1;
	int position2;
	int position3;
	int result = findCommonValue(array1, array2, array3, size1, size2, size3, position1, position2, position3);
	if (result == -1)
		cout << "can't find it" << endl;
	else
	{
		cout << "position1 = " << position1 << endl;
		cout << "position2 = " << position2 << endl;
		cout << "position3 = " << position3 << endl;
	}

}

仔细对比两个解法,oh, god, 下面的复杂度居然是O(min(n1, n2) * n3),而第一个解法的复杂度为O(min(n1, n2, n3))。。。

把while换成if,结果复杂度立马降下来了,坑你妹。。。

int findCommonValue(int *array1, int *array2, int *array3, int size1, int size2, int size3, 
					int &position1, int &position2, int &position3)
{
	if (array1 == NULL || array2 == NULL || array3 == NULL ||
		size1 <= 0 || size2 <= 0 || size3 <= 0)
		return -1;

	position1 = 0;
	position2 = 0;
	position3 = 0;

	while (position1 < size1 && position2 < size2 && position3 < size3)
	{
		if (array1[position1] != array2[position2])
		{
			position1++;
			position2++;
		}
		if (array3[position3] <= array2[position2])
		{
			if (array3[position3] != array2[position2])
				position3++;
			else
				return 0;
		}
		position1++;
		position2++;
	}

	if (position1 == size1 || position2 == size2 || position3 == size3)
		return -1;
	else
		return 0;
}

问题4.8 寻找最小与次小元素

请写一个函数,接收一个数组,找出该数组中最小与次小的元素的值

思路:

1. 两个变量small1 和 small2,分别用来保存最小值和次小值,取第三个及之后的元素和这两个值来判断,更新这两个变量

2. 分治法,每次分成两半,分别return当前的最小值和次小值

3. 一次拿两个元素,比较出最大值和最小值,然后再和small1和small2比较

思路1代码:

#include <iostream>

using namespace std;

int array[] = {8, 7, 2, 4, 5, 1, 3, 10};
const int size = sizeof array / sizeof *array;

int findTwoMins(int *array, int size, int &firstMin, int &secondMin)
{
	if (array == NULL || size <= 0)
		return -1;

	if (size == 1)
	{
		firstMin = array[0];
		secondMin = array[0];
		return 0;
	}

	if (array[0] > array[1])
	{
		firstMin = array[1];
		secondMin = array[0];
	}
	else
	{
		firstMin = array[0];
		secondMin = array[1];
	}

	int index = 2;
	while (index < size)
	{
		if (array[index] < firstMin)
		{
			secondMin = firstMin;
			firstMin = array[index];
		}
		else if (array[index] < secondMin)
		{
			secondMin = array[index];
		}
		index++;
	}

	return 0;
}

void main()
{
	int firstMin;
	int secondMin;
	int result = findTwoMins(array, size, firstMin, secondMin);
	cout << "firstMin = " << firstMin << endl;
	cout << "secondMin = " << secondMin << endl;
}

仔细分析上面代码的比较次数,不难发现需要用到2n - 3次,显然这个比较次数太大了。。。

再来看思路2的代码:

#include <iostream>

using namespace std;

int array[] = {8, 7, 2, 4, 5, 1, 3, 10};
const int size = sizeof array / sizeof *array;

int findTwoMins(int *array, int leftIndex, int rightIndex, int &firstMin, int &secondMin)
{
	if (array == NULL || leftIndex < 0 || rightIndex < 0 || leftIndex > rightIndex)
		return -1;

	if (leftIndex == rightIndex)
	{
		firstMin = array[leftIndex];
		secondMin = array[rightIndex];
		return 0;
	}

	if (leftIndex == rightIndex - 1)
	{
		if (array[leftIndex] > array[rightIndex])
		{
			firstMin = array[rightIndex];
			secondMin = array[leftIndex];
		}
		else
		{
			firstMin = array[leftIndex];
			secondMin = array[rightIndex];
		}
		return 0;
	}

	int midIndex = (leftIndex + rightIndex) / 2;
	int firstMinLeft, secondMinLeft;
	int firstMinRight, secondMinRight;
	findTwoMins(array, leftIndex, midIndex, firstMinLeft, secondMinLeft);
	findTwoMins(array, midIndex + 1, rightIndex, firstMinRight, secondMinRight);

	if (firstMinLeft < firstMinRight)
	{
		firstMin = firstMinLeft;
		secondMin = min(secondMinLeft, firstMinRight);
	}
	else
	{
		firstMin = firstMinRight;
		secondMin = min(firstMinLeft, secondMinRight);
	}

	return 0;
}

void main()
{
	int firstMin;
	int secondMin;
	int result = findTwoMins(array, 0, size - 1, firstMin, secondMin);
	cout << "firstMin = " << firstMin << endl;
	cout << "secondMin = " << secondMin << endl;
}

上面的比较次数为1.5n - 2

思路3的代码就不写了,比较次数是1.5n - 2

对比三种思路,思路2的比较次数虽然和3相同,但是递归调用的时间开销显然比循环大,所以思路3才是最合适的解法~~


问题4.9 查找矩阵

已知有n列n行的矩阵M,它的元素满足一个很特殊的性质,即对于任一元素Mi,j而言,都会小于在它右边与下方的元素(如果存在的话),换句话说,Mi,j < Mi,j+1,而且Mi,j < Mi+1,j,现在又有一个值K,请写一个程序,检查矩阵M中是否存在K。

这道题在那本剑指offer啥书上的前几题,比较简单,不说了~

问题4.10 表示成两个数平方和

已知一个正整数R,请写一个程序,找出所有满足x^2 + y^2 = R的正整数对X与Y。

#include <iostream>
#include <vector>
#include <cmath>
using namespace std;

struct Pair
{
	Pair(int i = 0, int j = 0) : x(i), y(j) {}
	int x;
	int y;
};

int findSquareSum(int number, vector<Pair> &pvec)
{
	if (number <= 1)
		return -1;

	int left = 1;
	int right = sqrt(double(number)) + 0.5;

	while (left <= right)
	{
		if (left * left + right * right == number)
		{
			pvec.push_back(Pair(left, right));
			left++;
			right--;
		}
		else if (left * left + right * right < number)
			left++;
		else
			right--;
	}

	if (pvec.size() == 0)
		return - 1;
	else
		return 0;
}

int i;
void main()
{
	vector<Pair> pvec;
	for (i = 2; i <= 100; i++)
	{
		int result = findSquareSum(i, pvec);
		if (result == 0)
		{
			for (int i = 0; i < pvec.size(); i++)
				cout << ::i << " = " << pvec[i].x << "*" << pvec[i].x << " + " << pvec[i].y << "*" << pvec[i].y << endl;
		}
		pvec.clear();
	}
}

问题4.11 最大方块区域

在一个矩阵中,相连在一起的一块正方形区域就叫做一个子区域,如M3,5、M3,6、M3,7、M4,5、M4,6、M4,7、M5,5、M5,6与M5,7就是矩阵M中从M3,5起的一个3*3的子区域(subblock)。请写一个程序,接受一个方阵(列数与行数相同),并且再接收一个已知的值,令其为K,请找出在给定方阵中值全部是K的最大一个子区域,并且报告出这个子区域在矩阵中的位置与它的大小。


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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值