剑指offer 面试题38 数字在排序数组中出现的次数

剑指offer 面试题38 数字在排序数组中出现的次数

统计一个数字在排序数组中出现的次数。例如输入排序数组 {1,2,3,3,3,3,4,5} 和数字 3,
由于 3 在这个数组中出现了 4 次,因此输出 4。

package algorithm.foroffer.top40;

import org.junit.Test;

import java.util.Arrays;

/**
 * description:
 *
 * @author liyazhou
 * @create 2017-05-31 17:29
 *
 * 面试题:数字在排序数组中出现的次数
 *
 * 题目:
 *      统计一个数字在排序数组中出现的次数。例如输入排序数组 {1,2,3,3,3,3,4,5} 和数字 3,
 *      由于 3 在这个数组中出现了 4 次,因此输出 4。
 *
 * 考查点:
 *      1. 二叉查找
 *
 * 思路:
 *      1. 反复二叉查找,确定这个数字的边界
 *
 */
public class Test38 {

    public int countNumOfK(int[] array, int k){
        if (array == null) return -1;

        int firstIdx = idxOfFirstK(array, k);
        int lastIdx = idxOfLastK(array, k);
        int number = 0;
        if (firstIdx != -1) number = lastIdx - firstIdx + 1;
        return number;
    }

    private int idxOfFirstK(int[] array, int k){
        int first = 0;
        int last = array.length-1;
        while(first <= last){ // 等号
            int mid = (first + last) / 2;
            // System.out.printf("first, last, mid = %d, %d, %d \n", first, last, mid);
            // 与标准二分查找不同的退出条件
            if (array[mid] == k && (mid-1 >= 0 && array[mid-1] != k || mid == 0)) return mid;
            if (array[mid] < k) first = mid + 1;
            else if (array[mid] == k) last = mid; // 如果二分后的元素与查找元素相等,则移动右边界last
            else if (array[mid] > k) last = mid - 1;
        }
        return -1;
    }

    private int idxOfLastK(int[] array, int k){
        int first = 0;
        int last = array.length - 1;
        while (first <= last){
            int mid = (first + last) / 2;
            // System.out.printf("first, last, mid = %d, %d, %d \n", first, last, mid);
            if (array[mid] == k && (mid+1 < array.length && array[mid+1] != k || mid == array.length-1)) return mid;
            if (array[mid] < k) first = mid + 1;
            else if (array[mid] == k) first = mid;
            else if (array[mid] > k) last = mid - 1;
        }
        return -1;
    }


    @Test
    public void test(){
        int[] array = {1, 2, 3, 3, 3, 3, 4, 5};
        // int[] array = {1, 2, 3, 4, 5};
        int k = 1;
        int firstIdx = idxOfFirstK(array, k);
        int lastIdx = idxOfLastK(array, k);
        System.out.printf("firstIdx, lastIdx = %d, %d", firstIdx, lastIdx);
    }

    @Test
    public void test02(){
        int[] array = {1, 2, 3, 3, 3, 3, 4, 5};
        for (int k = 1; k < 6; k++){
            int numOfK = countNumOfK(array, k);
            System.out.println(Arrays.toString(array));
            System.out.printf("%d: %d\n\n", k, numOfK);
        }
    }
}

另一种稍微低效的解法:
先二分查出该元素的位置,然后向前后两边扫描,确定边界。根据边界计算出该数字出现的次数。

 public int GetNumberOfK(int [] array , int k) {
     int pos = posOfK(array, k);
     if (pos == -1) return 0;
     int i = pos-1;
     int j = pos+1;
     for (; i >= 0 && array[i] == k; i --);
     for (; j < array.length && array[j] == k; j ++);
     return j - i - 1;

 }

 private int posOfK(int[] array, int k){
     int low = 0;
     int high = array.length - 1;
     while (low <= high){
         int mid = (low + high) / 2;
         if (array[mid] > k) high = mid - 1;
         else if (array[mid] < k) low = mid + 1;
         else return mid;
     }
     return -1;
 }

你可能感兴趣的:(剑指offer,剑指offer)