题目描述:
统计一个数字在排序数组中出现的次数
思路分析:
已下代码均通过牛客测试,主要工作只是在时间复杂度上的优化。
看到排序数组,然后又看到查找。首先就想到二分查找,开始的思路是先通过二分查找找出需要统计的那个数字,找到后再从那个位置依次向后遍历。代码如下:
public class Solution {
public int GetNumberOfK(int [] array , int k) {
if(array == null || array.length < 1){
return 0;
}
int left = 0;
int right = array.length - 1;
while(right >= left){
int mid = (left+right)/2;
if(array[mid]<k){
left = mid+1;
}else if(array[mid] > k){
right = mid-1;
}else{
return findNumOfK(array,mid,k);//找到该数字,并向前后依次遍历,统计出现的次数。
}
}
return 0;
}
public int findNumOfK(int[] array, int index, int k){
int num = 0;
int lindex = index-1;
while(lindex >= 0 && array[lindex] == k){
num++;
lindex--;
}
while(index < array.length && array[index] == k){
num++;
index++;
}
return num;
}
}
但是假如数组中的值全部相同的话,这时候开始的二分查找就没有任何意义了,时间复杂度和直接遍历数组一样,为O(N)。所以找到该数组后还得继续二分查找,采用递归来实现。
改进后的算法
public class Solution {
public int GetNumberOfK(int [] array , int k) {
if(array == null || array.length < 1){
return 0;
}
int left = 0;
int right = array.length - 1;
while(right >= left){
int mid = (left+right)/2;
if(array[mid]<k){
left = mid+1;
}else if(array[mid] > k){
right = mid-1;
}else{
int leftnum = findNumOfK(array,left,mid,k);//统计右边出现的次数
int rightnum = findNumOfK(array,mid+1,right,k);//统计左边出现的次数。
return leftnum+rightnum;
}
}
return 0;
}
public int findNumOfK(int[] array, int start, int end , int k){
if(start == end && array[end] ==k){
return 1;
}
while(end >= start){
int mid = (start+end)/2;
if(array[mid]<k){
start= mid+1;
}else if(array[mid] > k){
end = mid-1;
}else{
int leftnum = findNumOfK(array,start,mid,k);统计左边出现的次数。
int rightnum = findNumOfK(array,mid+1,end,k);统计右边出现的次数
return leftnum+rightnum;
}
}
return 0;
}
}
但是又想到一个问题,该递归方法有个严重的bug,忽视了排序数组这个关键的点。假设数组为111111时,只需要判断两段端是否为相等,直接指针相减即可得到出现次数,而不需要继续递归下去。当数组为01111110时,这时则无法判断左右两侧出现的1的次数,这时需要继续向下递归,统计出现次数。
再次优化后的代码:
public class Solution {
public int GetNumberOfK(int [] array , int k) {
if(array == null || array.length < 1){
return 0;
}
int left = 0;
int right = array.length - 1;
while(right >= left){
int mid = (left+right)/2;
if(array[mid]<k){
left = mid+1;
}else if(array[mid] > k){
right = mid-1;
}else{
int leftnum = array[left] == k? mid-left+1 : findNumOfK(array,left,mid,k);
int rightnum = array[right] == k? right-mid : findNumOfK(array,mid+1,right,k);
return leftnum+rightnum;
}
}
return 0;
}
public int findNumOfK(int[] array, int start, int end , int k){
while(end >= start){
int mid = (start+end)/2;
if(array[mid]<k){
start= mid+1;
}else if(array[mid] > k){
end = mid-1;
}else{
int leftnum = array[start] == k? mid-start+1 : findNumOfK(array,start,mid,k);
int rightnum = array[end] == k? end-mid : findNumOfK(array,mid+1,end,k);
return leftnum+rightnum;
}
}
return 0;
}
}
以上均为本人的思路分析,不足之处希望大家批评指正。