题目:给定一个长度为 n 的非降序数组和一个非负数整数 k ,要求统计 k 在数组中出现的次数
解法一:暴力法
暴力法适用于所有场景,不要求数组已经排序。
public int GetNumberOfK(int [] array , int k) {
int sum=0;
for(int a :array){
if(a==k){
sum++;
}
}
return sum;
}
复杂度分析:
时间复杂度:O(N),遍历数组一次
空间复杂度:O(1),不涉及额外内存空间
解法二:二分递归法
二分法应注意两处边界,一处是退出迭代或递归的边界,一次是向左递归或迭代的右边界问题。
边界问题一:if(left>=right),此处这里必须有等号,当数组只有一个元素,且目标元素不在数组里面,如果没有等号,就会出现指针越界。
边界问题二: if(array[mid]>k) right = mid;而不是right=mid-1;
反例:array = [1,5,7,8] ,k=5
第一次:mid=(0+4)/2=2,arr[mid]=7>5,如果right=mid-1,则此时right=1,left=0;
第二次:mid=(0+1)/2=0,arr[0]=1<5,left=mid+1=0+1=1;
第三次:left=right,退出循环,返回-1。
所以,right=mid;
public int GetNumberOfK(int [] array , int k) {
int sum=0;
if(array==null||array.length==0){
return sum;
}
int index = binarySearch(array,k,0,array.length);
for(int i=index+1;i<array.length;i++){
if(array[i]==k){
sum++;
}else{
break;
}
}
for(int i=index;i>=0;i--){
if(array[i]==k){
sum++;
}else{
break;
}
}
return sum;
}
private int binarySearch(int [] array , int k,int left,int right){
if(left>=right){
return -1;
}
int mid=(left+right)/2;
if(array[mid]>k){
return binarySearch(array,k,left,mid);
}else if(array[mid]<k){
return binarySearch(array,k,mid+1,right);
}else{
return mid;
}
}
复杂度分析:
时间复杂度:O(log2n),遍历数组一次
空间复杂度:O(1),不涉及额外内存空间
解法三:二分迭代法
while(left<right),此处这里不能有等号,当数组只有一个元素,且目标元素不在数组里面,如果有等号,就会出现指针越界。其实相当于解法二的当left>=right就返回。
public int GetNumberOfK(int [] array , int k) {
int sum=0;
if(array==null||array.length==0){
return sum;
}
int index = binarySearch(array,k);
for(int i=index+1;i<array.length;i++){
if(array[i]==k){
sum++;
}else{
break;
}
}
for(int i=index;i>=0;i--){
if(array[i]==k){
sum++;
}else{
break;
}
}
return sum;
}
private int binarySearch(int[] array,int k){
int left =0;
int right=array.length;
while(left<right){
// 防止加法结果数值超出int范围
int mid = left+(right-left)/2;
if(array[mid]<k){
left = mid+1;
}else if(array[mid]>k){
right = mid;
}else{
return mid;
}
}
return -1;
}
复杂度分析:
时间复杂度:O(log2n),遍历数组一次
空间复杂度:O(1),不涉及额外内存空间
总结:
涉及数据结构:数组
涉及算法:二分法、迭代、递归