剑指Offer - 数字在排序数组中出现的次数(Java实现)

题目描述:

统计一个数字在排序数组中出现的次数

思路分析:

已下代码均通过牛客测试,主要工作只是在时间复杂度上的优化。
看到排序数组,然后又看到查找。首先就想到二分查找,开始的思路是先通过二分查找找出需要统计的那个数字,找到后再从那个位置依次向后遍历。代码如下:

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;
    }
}

以上均为本人的思路分析,不足之处希望大家批评指正。

猜你喜欢

转载自blog.csdn.net/justlikeu777/article/details/85213526
今日推荐