解题思路:
如果用顺序查找,这道题的时间复杂度是O(n),肯定不符合面试官的要求。看到排序数组,并且在里面查找一个数出现的次数,很自然的可以想到利用二分查找,因此这道题是二分查找的一个应用。假设待查找元素为k,如果要是能找到第一个k出现的位置和最后一个k出现的位置,两者相减在加上1即为k出现的次数。
具体来说,现在可以先对整个数组进行一次二分查找,记中间元素的索引为midIndex,对应的元素为midData。若midData和k相等,则比较midIndex前面一个数即midIndex-1指向的元素是否是元素k,若不是,则证明此时的midIndex为k第一次出现的位置,若是,则对midIndex之前的元素进行一次查找第一次k出现的位置(递归调用),直到找到k第一次出现的位置。对于查找k最后一次出现的位置,思路与上面类似。对整个数组进行二分查找,比较midIndex后面一个数即midIndex+1指向的元素是否为k,若不是,则证明此时的midIndex为k最后一次出现的位置,若是,则对midIndex之后的元素进行一次查找最后一个k出现的位置(递归调用),直到找到k最后一次出现的位置。
public class Solution {
public int GetNumberOfK(int[] array, int k) {
int number = 0;
if (array != null && array.length != 0) {
int first = getFirstK(array, 0, array.length - 1, k);
int last = getLastK(array, 0, array.length - 1, k);
if (first > -1 && last > -1) {
number = last - first + 1;
}
}
return number;
}
//在数组中找到k出现的第一个位置
public int getFirstK(int[] array, int start, int end, int k) {
if (start > end) {
//此时表明未找到元素k
return -1;
}
int midIndex = (start + end) / 2;
int midData = array[midIndex];
//利用二分查找
if (midData == k) {
//当中间元素与待查找元素相等时,判断前面一个元素是不是和k相等
if (midIndex > 0 && array[midIndex - 1] != k || midIndex == 0) {
//如果前一个元素不等于k,则此时midIndex是k出现第一次的位置
return midIndex;
} else {
//如果前一个元素等于k,则此时需要在midIndex前面继续寻找第一个k出现的位置
end = midIndex - 1;
}
} else if (midData > k) {
//如果中间元素比k大,则k只可能出现在中间元素的前面,则下一次在数组的左半部分继续寻找k
end = midIndex - 1;
} else {
//如果中间元素比k小,则k只可能出现在中间元素的后面,则下一次在数组的右半部分继续寻找k
start = midIndex + 1;
}
return getFirstK(array, start, end, k);
}
public int getLastK(int[] array, int start, int end, int k) {
if (start > end) {
//表明在数组中未找到元素k
return -1;
}
int midIndex = (start + end) / 2;
int midData = array[midIndex];
//利用二分查找
if (midData == k) {
//当中间元素与待查找元素相等时,判断后面一个元素是不是和k相等
if (midIndex < array.length - 1 && array[midIndex + 1] != k
|| midIndex == array.length - 1) {
//如果前一个元素不等于k,则此时midIndex是k出现第一次的位置
return midIndex;
} else {
//如果后一个元素等于k,则此时需要在midIndex后面继续寻找第一个k出现的位置
start = midIndex + 1;
}
} else if (midData > k) {
//如果中间元素比k大,则k只可能出现在中间元素的前面,则下一次在数组的左半部分继续寻找k
end = midIndex - 1;
} else {
//如果中间元素比k小,则k只可能出现在中间元素的后面,则下一次在数组的右半部分继续寻找k
start = midIndex + 1;
}
return getLastK(array, start, end, k);
}
}