剑指offer系列之三十六:数字在排序数组中出现的次数

题目描述

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

因为是排序数组,自然联想到二分查找算法,这样我们在二分的时候可能会获取多个相同的数字。就是说,中间那个位置的值可能刚好是统计的那个值,假设为k。那么k还有可能在前面或者后面出现,在这个基础上继续二分当然也是可以的,如果能够在使用二分查找算法的时候统计出第一个k和最后一个k出现的位置,那么k出现的次数自然就确定了。第一个k出现的位置,可以使用二分查找算法,如果中间位置的值刚好是k,那么继续比较中间位置前面位置的值是不是也是k,如果不是那么该中间位置就是第一个k出现的位置,如果中间位置的前一个位置的值也是k,那么说明第一个k肯定出现在前半部分,继续二分即可。那么寻找最后一个k出现的位置也是一样的道理,比较中间位置后面一个位置的值是不是也是k,如果是说明最后一个k肯定出现在后半部分,继续二分即可,如果不是k的话,那么中间位置就是最后一个k出现的位置了,因为是排序数组,那么后面一个位置的值如果都不是k的话,其他位置更加不可能是k了。排序数组是一个关键

基于上述思路,实现的代码如下(已被牛客AC):

package com.rhwayfun.offer;

public class GetCountOfK {

    public int GetNumberOfK(int[] array, int k) {
        if (array == null || array.length <= 0)
            return 0;
        int count = 0;
        int firstIndexOfK = getFirstK(array, k, 0, array.length - 1);
        int lastIndexOfK = getLastK(array, k, 0, array.length - 1);
        if (firstIndexOfK >= 0 && lastIndexOfK >= 0)
            count = lastIndexOfK - firstIndexOfK + 1;
        return count;
    }

    private int getLastK(int[] array, int k, int start, int end) {
        if (start > end) return -1;
        int middleIndex = (start + end) / 2;
        int middleData = array[middleIndex];
        if (middleData == k) {
            if ((middleIndex < array.length - 1 && array[middleIndex + 1] != k)
                    || middleIndex == array.length - 1) {
                return middleIndex;
            } else {
                start = middleIndex + 1;
            }
        } else if (middleData > k) {
            end = middleIndex - 1;
        } else {
            start = middleIndex + 1;
        }
        return getLastK(array, k, start, end);
    }

    private int getFirstK(int[] array, int k, int start, int end) {
        if (start > end) return -1;
        int middleIndex = (start + end) / 2;
        int middleData = array[middleIndex];
        if (middleData == k) {
            if ((middleIndex > 0 && array[middleIndex - 1] != k)
                    || middleIndex == 0) {
                return middleIndex;
            } else {
                end = middleIndex - 1;
            }
        } else if (middleData > k) {
            end = middleIndex - 1;
        } else {
            start = middleIndex + 1;
        }
        return getFirstK(array, k, start, end);
    }

    public static void main(String[] args) {
        int[] array = new int[]{1,2,3,3,3,3,4,5};
        int count = new GetCountOfK().GetNumberOfK(array, 3);
        System.out.println(count);
    }
}
时间: 2024-12-03 16:40:09

剑指offer系列之三十六:数字在排序数组中出现的次数的相关文章

剑指offer系列之三十四:数组中的逆序对

题目描述 在数组中的两个数字,如果前面一个数字大于后面的数字,则这两个数字组成一个逆序对.输入一个数组,求出这个数组中的逆序对的总数. 要找到数组中的逆序对,一种思路是每遍历到一个元素的时候就和后面的元素进行一一比较,这种算法的时间复杂度是O(n2),所以不是很理想.我们可以借鉴归并排序的思想,把数组划分为子数组,然后对每个子数组中的逆序对数进行统计,统计完成后再并到一个新的数组中进行统计,这样就化整为零,实现了高效统计.总计起来思路就是:首先把数组划分为子数组,然后统计子数组中的逆序对数,然后

剑指offer系列之三十九:数组中只出现一次的数字

题目描述 一个整型数组里除了两个数字之外,其他的数字都出现了两次.请写程序找出这两个只出现一次的数字. 先考虑只有只有一个数字出现一次的情况,因为其他数字只出现了两次,所以对这两个数字进行异或运算的时候,其结果是0,那么那个只出现一次的数字进行异或运算的时候,其结果必然不是0,所以可以利用这点找出那个只出现一次的数字.现在考虑有两个数字出现一次的情况,仍然借鉴上面的思路,因为只出现一次的那个数字的异或结果不是0,遍历整个数组进行异或运算的结果也肯定不是0,现在可以对该数右边第一个是1的位的位置作

剑指offer系列之三十:整数中1出现的次数

题目描述 求出1~13的整数中1出现的次数,并算出100~1300的整数中1出现的次数?为此他特别数了一下1~13中包含1的数字有1.10.11.12.13因此共出现6次,但是对于后面问题他就没辙了.ACMer希望你们帮帮他,并把问题更加普遍化,可以很快的求出任意非负整数区间中1出现的次数. 有一种比较简洁的思路:由于是一个整数区间,所以可以对每一个数进行判断,那么如何判断某个数中1出现了多少次呢?注意到如果一个数一直被10除,如果被10整除后的余数是1,那么一定出现了数字1.举个例子,数字11

剑指offer系列之三十八:判断是否是平衡二叉树

题目描述 输入一棵二叉树,判断该二叉树是否是平衡二叉树. 所谓平衡二叉树就对某个结点来讲,其左子树的深度与右子树深度的绝对值不超过1.由于需要对每个节点进行判断,所以可以采用递归的思路进行解决.具体思路是:先求出根节点的左右子树的深度,并对两者进行判断,如果没有满足左右子树的深度的绝对值不超过1的条件,那么就不是平衡二叉树.下一步,自然就是分别对根节点的左右子树进行递归判断了,这样一直到叶子结点.整棵二叉树的所有节点都判断完毕,所以这个问题就解决了.下面是这种思路的实现代码(已被牛客AC): p

剑指offer系列之四十六:求1到n的和

题目描述 求1+2+3+-+n,要求不能使用乘除法.for.while.if.else.switch.case等关键字及条件判断语句(A?B:C). 如果不能使用上面的操作,那么只能使用递归操作了.使用递归操作的思路是让函数不断调用自己,每调用一次值就减少1,这样完成了递归操作.还有一个问题是如何实现n范围的判断呢?注意到递归调用的n的值最小是1,所以可以通过逻辑与运算,判断是否递归到1.如果递归调用到1,那么递归就结束,并返回最后的结果.下面是这样思路的实现代码(已被牛客AC): packag

剑指offer系列之十六:树的子结构

题目描述 输入两颗二叉树A,B,判断B是不是A的子结构. 这实际上二叉树遍历算法的一种应用,要在原二叉树中查找是否具有某课子树,只需要判断每个节点是否都在二叉树中是否出现即可.所以需要先判断头结点,只有头结点符合要求才继续比较其子树是否符合,一样依次从头结点开始比较直到其左右子树进行比较,如果都符合则说明B是A的子结构.下面是基于这种思路实现的代码(已被牛客AC): package com.rhwayfun.offer; public class SubTree { public class T

剑指offer系列之三十五:两个链表的第一个公共节点

题目描述 输入两个链表,找出它们的第一个公共结点. 由于是单链表,所以可以发现从第一个公共节点开始,后面的结点都是相同的,一种思路是从两个链表的尾部开始遍历,直到发现最后一个相同的结点为止,那么这最后一个相同的结点是单链表的角度看就是两个链表的第一个公共节点了.还有一种思路是不需要从尾部开始遍历,毕竟从尾部遍历单链表的话还得使用反转链表的方法进行操作,首先计算出两个链表的长度,让更长的那个单链表先移动两个链表长度差值个位置,然后两个链表同时移动,从更短的那个链表的第一个位置开始遍历,两个链表都往

剑指offer系列之五十六:对称二叉树的判断

题目描述 请实现一个函数,用来判断一颗二叉树是不是对称的.注意,如果一个二叉树同此二叉树的镜像是同样的,定义其为对称的. 前面有一题是打印输出某二叉树的镜像,回想其实现的思路是:采用层序遍历的思路对每一个遍历的节点,如果其有孩子节点,那么就交换两者.直到遍历的节点没有孩子节点为止,然而此题是对二叉树木镜像的判断,明显是更简单的,只需要进行两个判断:对节点的左孩子与其兄弟节点右孩子的判断以及对节点右孩子与其兄弟节点左孩子的判断.这样就完成对对一棵二叉树是否对称的判断.下面是具体的实现代码(已被牛客

剑指offer系列之三十二:寻找丑数

题目描述 把只包含因子2.3和5的数称作丑数(Ugly Number).例如6.8都是丑数,但14不是,因为它包含因子7. 习惯上我们把1当做是第一个丑数.求按从小到大的顺序的第N个丑数. 因为丑数只有2.3和5这三个因子,那么如果一个是丑数的话,一定是可以被这个三个因子整除的,所以我们可以通过把一个数一直被三个因子除,这样最后如果该数变成1的话(因为第一个丑数是1),那么就验证了该数就是丑数.那么如果想找出第k个丑数的话,就可以从第一个数开始循环,对每一个数进行丑数的判断,从而找到符合要求的第