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

题目

数字在排序数组中出现的次数 -- newcoder 37 

 

题目描述

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

 

思路

思路1:

     * 1、二分查找法查找到数字出现的最左侧位置
     * 2、二分查找法查找到数字出现的最右侧位置
     * 3、计算出现的次数

思路2:
     * 1、因为data中都是整数,所以可以稍微变一下,不是搜索k的两个位置,而是搜索k-0.5和k+0.5
     * 2、这两个数应该插入的位置,然后相减即可。

 

代码

package com.codinginterviews.array;

/**
 * 题目:
 * 数字在排序数组中出现的次数 -- newcoder 37 
 *  
 * 题目描述:
 * 统计一个数字在排序数组中出现的次数。
 */
public class GetNumberOfK {

	/**
	 * 思路: 
	 * 1、二分查找k在array中出现的最左侧的位置i
	 * 2、二分查找k在array中出现的最右侧的位置j
	 * 3、返回 j-i+1即可
	 */
    public int getNumberOfK(int [] array , int k) {
        if (array == null || array.length == 0) {
            return 0;
        }
        
        int len = array.length;
        int left = getFirstNumIndex(array, k, 0, len - 1);
        int right = getLastNumIndex(array, k, 0, len - 1);
        
        if (left >= 0 && right >= left) {
            return right - left + 1;
        }
        
        return 0;
    }
    
    // 递归 二分查找的变形 查找最左侧的k元素
    private int getFirstNumIndex(int[] arr, int k, int start, int end) {
        // 判断非法条件
        if (start > end) {
            return -1;
        }
        
        // 在数字没有溢出的前提下,对于正数和负数,左移一位都相当于乘以2的1次方,左移n位就相当于乘以2的n次方
        // 右移一位相当于除2,右移n位相当于除以2的n次方
        int mid = (start + end) >> 1;
        
        int cur = arr[mid];
        // 找到了num对应的首个索引
        if (cur == k ) {
            if (mid == 0 || (mid > 0 && arr[mid - 1] < cur)) {
                return mid;
            }
        } 
        // 没有找到
        int newStart = start;
        int newEnd = end;
        
        // 目标数据在右侧
        if (cur < k) {
            newStart = mid + 1;
        // 目标数据在左侧, 相等情况去左侧找         
        } else {
            newEnd = mid - 1;
        }
        
        // 重新查找下一区间
        return getFirstNumIndex(arr, k, newStart, newEnd);
    }
 
    // 非递归 二分查找的变形 查找最右侧的k元素
    private int getLastNumIndex(int[] arr, int k, int start, int end) {
        if (start > end) {
            return -1;
        }
        int len = arr.length;
        int mid = (start + end) >> 1;
        
        while(start <= end){
            if (arr[mid] > k){
                end = mid-1;
            } else if (arr[mid] < k){
                start = mid+1;
            } else if (mid+1 < len && arr[mid+1] == k){
                start = mid+1;
            } else {
                return mid;
            }
            mid = (start + end) >> 1;
        }
        return -1;
    }

    /**
     * 思路: 
     * 1、因为data中都是整数,所以可以稍微变一下,不是搜索k的两个位置,而是搜索k-0.5和k+0.5
     * 2、这两个数应该插入的位置,然后相减即可。
     */
    public int getNumberOfKII(int [] array , int k) {
        return biSearch(array, k+0.5) - biSearch(array, k-0.5) ;
    }
    
    private int biSearch(int [] array , double k) {
    	int start = 0, end = array.length - 1;     
        while(start <= end){
            int mid = (end + start) >> 1;
            if(array[mid] < k)
                start = mid + 1;
            else if(array[mid] > k)
                end = mid - 1;
        }
        return start;
    }
    
	public static void main(String[] args) {
		int[] arr = {1,2,3,3,3,3,4,5};
		int[] arr1 = {1,1,1,1,1,1,1,1};
		int[] arr2 = {3};
		System.out.println(new GetNumberOfK().getNumberOfK(arr, 6));
		System.out.println(new GetNumberOfK().getNumberOfK(arr1, 1));
		System.out.println(new GetNumberOfK().getNumberOfK(arr2, 3));
	}

}

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值