剑指offer(39):数字在排序数组中出现的次数

题目描述

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

分析

直接思路:顺序扫描直接统计3出现的次数,时间复杂度为 O(n)

推荐思路:参考二分查找的思路,可以实现时间复杂度为 O(logn) 。假设需要查找数字k,由于数组已经有序,利用二分查找的思想,可以在 O(logn) 时间内找到第一个k的索引firstIndex和最后一个k的索引lastIndex,如果两个索引均存在(k至少出现1次),则出现次数times = lastIndex - firstIndex + 1

第一个k的查找:二分查找中,先找到k,如果k**前面**的数字不为k,则该处的k就是第一个k;如果k前面的数字等于k,则在该处的k前面部分继续寻找。

第二个k的查找:二分查找中,先找到k,如果k**后面**的数字不为k,则该处的k就是最后一个k;如果k后面的数字等于k,则在该处的k后面部分继续寻找。

牛客AC:

package com.problem;

/**
 * 统计一个数字在排序数组中出现的次数。
 * 例如输入排序数组{1, 2, 3, 3, 3, 3, 4, 5}和数字3,
 * 由于数字3在排序数组中出现了4次,则输出4。
 * @author Administrator
 *
 */
public class TimesOfNumber {
    public static void main(String[] args) {
        int[] arr = {1,2,3,3,3,3,4,5};
        TimesOfNumber timesOfNumber = new TimesOfNumber();
        System.out.println(timesOfNumber.GetNumberOfK(arr, 3));
    }

    public int GetNumberOfK(int[] arr , int k) {
        if(arr == null || arr.length <= 0)
            return 0;

        // 二分查找获取第1个k和最后1个k的索引
        int firstIndex = getFirstIndexOfK(arr, k, 0, arr.length - 1);
        int lastIndex = getLastIndexOfK(arr, k, 0, arr.length - 1);

        // 计算k出现次数
        int times = 0;
        if(firstIndex > -1 && lastIndex > -1)
            times = lastIndex - firstIndex + 1;

        return times;
    }

    // 获取第1个k的索引,没有返回-1
    public int getFirstIndexOfK(int[] arr, int k, int start, int end) {
        if(start > end)
            return -1;

        while(start <= end) {
            int mid = (start + end) / 2;
            if(arr[mid] == k) {
                // mid大于0,并且mid前面的数不等于k;或者mid等于0时,找到了第1个k
                if((mid > 0 && arr[mid - 1] != k) || mid == 0) {
                    return mid;
                } else {
                    end = mid - 1;
                }
            } else if(arr[mid] > k) {
                end = mid - 1;
            } else {
                start = mid + 1;
            }
        }

        return -1;
    }

    // 获取最后1个k的索引,没有返回-1
    public int getLastIndexOfK(int[] arr, int k, int start, int end) {
        if(start > end)
            return -1;

        while(start <= end) {
            int mid = (start + end) / 2;
            if(arr[mid] == k) {
                // mid小于end,并且mid后面的数不等于k;或者mid等于end时,找到了最后一个k
                if((mid < end && arr[mid + 1] != k) || mid == end) {
                    return mid;
                } else {
                    start = mid + 1;
                }
            } else if(arr[mid] > k) {
                end = mid - 1;
            } else {
                start = mid + 1;
            }
        }

        return -1;
    }
}

参考
1. 何海涛,剑指offer名企面试官精讲典型编程题(纪念版),电子工业出版社

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值