给一个有序的int数组和数组当中元素k,输出元素k在数组当中出现的次数。
例如:[2,3,4,5,5,5,6,8] k=5 output:3 要去算法的时间复杂度不超过O(n),也就是说不能去遍历数组了
/**
* 是排序数组,那么第一个K 之前的数都会比K 小,最后一个K之后的数都会比K大
* 思路:
* 分两步解决:
* 二分法找到第一个K
* 二分法找到第二个K
* 实现?
* 二分:
* 1.如果中间的数比K大,那么K出现在前半段
* 2.如果中间的数比K小,那么K出现在后半段
* 确定第一个K的位置:
* 1.判断K-1位置的数是否等于K:等于K,则第一个K还在前,不等于K,则是第一个K
* 2.或者判断K的位置是否已经达到数组的起始位置,下标0
* 确定第二个K的位置:
* 1.判断K+1位置的数是否等于K:等于K,则第二个K还在后,不等于K,则是第二个K
* 2.或者判断K的位置是否已经达到数组的终止位置,下标:长度-1
*/
public class TestKCount {
public static void main(String[] args) {
int[] array = {2, 3, 4, 5, 5, 5, 6, 8};
int K = 8;
int count = 0;
//第一个K的位置下标
int first = getFirstKIndex(array, K, 0, array.length - 1);
//第二个K的位置下标
int last = getLastKIndex(array, K, 0, array.length - 1);
//K在数组中出现的次数
if (first > -1 && last > -1) {
count = last - first + 1;
}
System.out.println(count);
}
/**
* 确定第一个K的位置
*
* @param a
* @param K
* @param start
* @param end
* @return
*/
public static int getFirstKIndex(int[] a, int K, int start, int end) {
if (start > end) {
return -1;
}
int midIndex = (start + end) / 2;//中点
int midData = a[midIndex];//中点值
//当中点值等于K
if (midData == K) {
//判断中点前一个数是不是等于K,不等于K则是第一个,或者已经达到边界就是第一个
if ((midIndex > 0 && a[midIndex - 1] != K) || midIndex == 0) {
return midIndex;
} else {//等于K,说明第一个K还在前
end = midIndex - 1;
}
} else if (midData > K) {//当中点值大于K,说明两个K都在前
end = midIndex - 1;
} else {//当中点值小于K,说明两个K都在后
start = midIndex + 1;
}
return getFirstKIndex(a, K, start, end);
}
/**
* 确定第二个K的位置
*
* @param a
* @param K
* @param start
* @param end
* @return
*/
public static int getLastKIndex(int[] a, int K, int start, int end) {
if (start > end) {
return -1;
}
int midIndex = (start + end) / 2;//中点
int midData = a[midIndex];//中点值
//当中点值等于K
if (midData == K) {
//判断中点后一个数是不是等于K,不等于K则是最后一个,或者已经达到边界就是最后一个
if ((midIndex < a.length - 1 && a[midIndex + 1] != K) || midIndex == a.length - 1) {
return midIndex;
} else {//等于K,说明第二个K还在后
start = midIndex + 1;
}
} else if (midData > K) {//当中点值大于K,说明两个K都在前
end = midIndex - 1;
} else {//当中点值小于K,说明两个K都在后
start = midIndex + 1;
}
return getLastKIndex(a, K, start, end);
}
}