题目
求数字在排序数组中出现的次数。
例如,输入排序数组{1,2,3,3,3,3,4,5}和数字3
输出4
分析
首先题目中指出数组是排序的,所以我们想到使用二分查找。
在上面给出的例子中,我们使用二分查找找到3。由于3可能出现多次,即找到的3之前可能还有3,之后也可能存在3。因此还要在找到的3的前后进行扫描查找。分别找到第一个和最后一个3。
这种做法存在一个问题,那就是当我们向前或者向后扫描找k的时候,这种顺序扫描的时间复杂度是O(n),和从头到尾扫描的算法的时间复杂度是一样的
优化的方式:
利用二分算法查找第一个k的位置和最后一个k的位置。代码如下:
代码
package com.jie.chapter6;
/**
* Created by lujie on 2018/9/18.
*/
public class GetKInSortedListSolution {
public static void main(String[] args) {
int[] arr = new int[]{1,2,3,3,3,3,4,5};
int k = 3;
int result = getK(arr,3);
System.out.println(result);
}
public static int getK(int[] arr,int k) {
int result = 0;
if(arr == null || arr.length <=0) {
return result;
}
int first = getFirstK(arr,k,0,arr.length-1);
int last = getLastK(arr,k,0,arr.length-1);
// 如果k只出现一次,则fist和last相等
if(first > -1 && last > -1){
result = last - first +1;
}
return result;
}
public static int getFirstK(int[] arr, int k ,int start, int end)
{
if(start > end) {
return -1;
}
int mid = start + ((end - start) >> 1);
int midData = arr[mid];
if(midData == k) {
// mid的前一个元素不是k,证明如果还有k则都在后半段
// 即当前k就是第一个k
if((mid > 0 && arr[mid-1]!=k) || mid == 0) {
return mid;
}else {
// 前一个元素也是k,则第一个k在前半段,end = mid-1,往前半段找
end = mid - 1;
}
}else if(midData > k) {
end = mid - 1;
}else{
start = mid + 1;
}
return getFirstK(arr,k,start,end);
}
public static int getLastK(int[] arr, int k ,int start, int end)
{
if(start > end) {
return -1;
}
int mid = start + ((end - start) >> 1);
int midData = arr[mid];
if(midData == k) {
// mid的后一个元素不是k,证明如果还有k则都在前半段
// 即当前k就是最后一个k
if((mid <arr.length - 1 && arr[mid+1]!=k) || mid == arr.length - 1) {
return mid;
}else {
// 后一个元素也是k,则最后一个k在后半段,start = mid+1,往后半段找
start = mid+1;
}
}else if(midData > k) {
end = mid - 1;
}else{
start = mid + 1;
}
return getLastK(arr,k,start,end);
}
}