题目1:
二分查找:
int BinarySearch(int array[], int n, int value) { int left = 0; int right = n - 1; //如果这里是int right = n 的话,那么下面有两处地方需要修改,以保证一一对应: //1、下面循环的条件则是while(left < right) //2、循环内当 array[middle] > value 的时候,right = mid while (left <= right) //循环条件,适时而变 { int middle = left + ((right - left) >> 1); //防止溢出,移位也更高效。同时,每次循环都需要更新。 if (array[middle] > value) { right = middle - 1; //right赋值,适时而变 } else if(array[middle] < value) { left = middle + 1; } else return middle; //可能会有读者认为刚开始时就要判断相等,但毕竟数组中不相等的情况更多 //如果每次循环都判断一下是否相等,将耗费时间 } return -1; }题目二:
杨氏矩阵查找
解法一:
分治法
以查找数字6为例,因为矩阵的行和列都是递增的,所以整个矩阵的对角线上的数字也是递增的,故我们可以在对角线上进行二分查找,如果要找的数是6介于对角线上相邻的两个数4、10,可以排除掉左上和右下的两个矩形,而在左下和右上的两个矩形继续递归查找,如下图所示:
解法二:
定位法:
首先直接定位到最右上角的元素,再配以二分查找,比要找的数(6)大就往左走,比要找数(6)的小就往下走,直到找到要找的数字(6)为止,这个方法的时间复杂度O(m+n)。如下图所示:
代码:
#define ROW 4
#define COL 4
bool YoungMatrix(int data[][COL], int serchValue)
{
int i=0,j=COL-1;
int var = data[i][j];
while(true)
{
if(var == serchValue)
return true;
else if(var < serchValue && i<ROW-1)
var = data[++i][j];
else if(var > serchValue && j>0)
var = data[i][--j];
else
return false;
}
}
举一反三
1、给定 n×n 的实数矩阵,每行和每列都是递增的,求这 n^2 个数的中位数。
2、我们已经知道杨氏矩阵的每行的元素从左到右单调递增,每列的元素从上到下也单调递增的矩阵。那么,如果给定从1-n这n个数,我们可以构成多少个杨氏矩阵呢?
例如n = 4的时候,我们可以构成1行4列的矩阵:
1 2 3 4
2个2行2列的矩阵:
1 2
3 4
和
1 3
2 4
还有一个4行1列的矩阵
1
2
3
4
因此输出4。
题目三:
数组中有一个数字出现的次数超过了数组长度的一半,找出这个数字
解法一:先排序,快排,遍历,统计次数,输出,时间复杂度O(nlogn + n)。
解法二:排序,输出中间值,时间复杂度O(nlogn)。
解法三:hash表,统计次数,输出,时间复杂度O(1),空间复杂度O(n)。
解法四:
由于题目假定数组中有出现次数超过一半的数字,因此可以通过两个变量,一个是数组中的数字,一个是次数,遍历数组,变量使用的规则是:
- 如果下一个数字和我们之前保存的数字相同,则次数加1;
- 如果下一个数字和我们之前保存的数字不同,则次数减1,我们需要保存下一个数字,并把次数重新设为1。
下面,举例:
-
第一个例子,假定数组为{5, 5, 5, 5, 1}
不同的相消,相同的累积。遍历到第四个数字时,candidate 是5, nTimes 是4;遍历到第五个数字时,candidate 是5, nTimes 是3;nTimes不为0,那么candidate就是超过半数的。
-
第二个例子,假定数组为{0, 1, 2, 1, 1}
开始时,保存candidate是数字0,nTimes为1;遍历到数字1后,与数字0不同,则nTimes减1变为零;接下来,遍历到数字2,2与1不同,candidate保存数字2,且nTimes重新设为1;继续遍历到第4个数字1时,与2不同,nTimes减1为零,同时candidate保存为1;最终遍历到最后一个数字还是1,与我们之前candidate保存的数字1相同,nTimes加1为1。最后返回的是之前保存的candidate为1。
int findNumber(int* data , int n)
{
int condidate = data[0];
int nTimes=0;
for(int i=0;i<n;i++)
{
if(condidate == data[i])
{
nTimes++;
}else
{
nTimes--;
}
if(nTimes==0)
{
condidate = data[i];
nTimes = 1;
}
}
return condidate;
}
举一反三
加强版水王:找出出现次数刚好是一半的数字
分析:我们知道,水王问题:有N个数,其中有一个数出现超过一半,要求在线性时间求出这个数。那么,我的问题是,加强版水王:有N个数,其中有一个数刚好出现一半次数,要求在线性时间内求出这个数。
因为,很明显,如果是刚好出现一半的话,如此例: 0,1,2,1 :
遍历到0时,candidate为0,times为1
遍历到1时,与candidate不同,times减为0
遍历到2时,times为0,则candidate更新为2,times加1
遍历到1时,与candidate不同,则times减为0;我们需要返回所保存candidate(数字2)的下一个数字,即数字1。
思路:
与上题相同,不过这次使用四个变量,由于数组中刚好有数字出现的次数是数组长度一半,因此其他数字的次数都不会比该数字的次数长,所以使用四个变量,两个数字,两个次数,分别记录不同数字的次数,最后选择次数最大者。
代码如下:
int findHalfNumber(int* data, int n)
{
int condidate1,condidate2;
int nTime1,nTime2;
for(int i=nTime1=nTime2=0;i<n;i++)
{
if(nTime1==0)
{
condidate1=data[i];
nTime1=1;
}else if(nTime2==0)
{
condidate2=data[i];
nTime2=1;
}else
{
if(condidate1==data[i])
{
nTime1++;
}else if(condidate2==data[i])
{
nTime2++;
}else
{
nTime1--;
nTime2--;
}
}
}
return nTime1>nTime2 ? condidate1 : condidate2;
}