【算法】解题总结:剑指Offer 31 整数中1出现的次数(5种想法 + 3 种方法)、剑指Offer 37 数字在升序数组中出现的次数

JZ31 整数中1出现的次数(从1到n整数中1出现的次数)

(中等)

题目

描述
输入一个整数 n ,求1~n这n个整数的十进制表示中1出现的次数
例如,1~13中包含1的数字有1、10、11、12、13因此共出现6次

示例
输入:
13
返回值:
6

解题思路与过程

1 找规律:失败的思路

对于此题,我一开始是想找出规律再去做的,如下图
在这里插入图片描述
但是,100以内的规律很好找,但是到了100多,200多,…,1000多,…,发现这规律也不是那么容易找的,于是我便打算换一种想法。

2 二进制&运算:失败的思路

因为题目中出现了一句 “ 十进制表示 ”,因此我便想通过转换一下进制,转为二进制的思路来想,但是,这样只能对于个位数是 1 的数是很方便找的,用一个 1 与运算(&)上当前数,即可判断出个位上的 1 ,但是对于十位,百位,更高位,虽然我们可以每次循环将当前数除以10,但是对于像是 3 这样的数,它的二进制最低为是为 1 的,但它的十进制数中却没有 1,因此,将数转为二进制用 & 来判断这种方法是行不通的。

3 暴力解法一:成功

最后,确实没想到比较方便和巧妙的方法,于是还是不得已用了暴力解法,将遍历过程中的每个数,转为字符串后,记录里面共有多少个 1 ,因此也没什么技术含量了,直接看代码就可以了。

【实现】

public class JZ31整数中1出现的次数 {
    public int NumberOf1Between1AndN_Solution(int n) {
        if (n < 1) {
            return 0;
        }

        int count = 0;
        String numStr = null;
        char[] numCharArray = null;

        for (int i = 1; i <= n; i++) {
            numStr = String.valueOf(i);
            numCharArray = numStr.toCharArray();
            for (char c : numCharArray) {
                if (c == '1') {
                    count++;
                }
            }
        }

        return count;
    }
}

在这里插入图片描述

4 暴力解法二:成功

在写完这种解法后,我便又想到了另一种暴力解法的实现形式,就是用求 10 的余数来判断,但其实也算是暴力解法,不过它的效率决定要高于第一种转字符的暴力解法,这里那我也顺便贴上代码吧。

【实现】

public class JZ31整数中1出现的次数 {
    public int NumberOf1Between1AndN_Solution2(int n) {
        if (n < 1) {
            return 0;
        }

        int count = 0;
        int x = 0;

        for (int i = 1; i <= n; i++) {
            x = i;
            while (x != 0) {
                if (x % 10 == 1) {
                    count++;
                }
                x /= 10;
            }
        }

        return count;
    }
}

可见,效率是高了不少
在这里插入图片描述

5 另一种方法

之后看题解时,发现了这样一个方法,确实没怎么看懂,而且它的执行速度,也和我的第二种方法差不多,不过看这位网友的代码,似乎很有研究价值,等以后哪天再刷一遍 剑指Offer 时再看吧。

(下文链接)
在这里插入图片描述

JZ37 数字在升序数组中出现的次数

(中等)

题目

描述
统计一个数字在升序数组中出现的次数。

示例
输入:
[1,2,3,3,3,3,4,5],3
返回值:
4

思路

题目已经交代的是否清楚了,又是有序数组,又是查找某个数,因此十分自然,直接想到二分查找

虽然待查找的数值的数量可能不止一个,但都是挨着的,因此我们用二分查出来的元素下标,向前、向后统计个数,再加上本来这二分中间这个,就是待求 k 值在数组中出现的次数。

(补充:关于四种基本查找算法,可参考之前文章:Java实现四种【查找算法】+图解+完整代码+分析

实现

public class JZ37数字在升序数组中出现的次数 {
    public int GetNumberOfK(int [] array , int k) {
        if (array == null || array.length == 0) {
            return 0;
        }

        int len = array.length;
        int low = 0;
        int high = len - 1;
        int mid = 0;

        while (low <= high) {
            mid = (low + high) / 2;
            if (k > array[mid]) {
                low = mid + 1;
            } else if (k < array[mid]) {
                high = mid - 1;
            } else {
                break;
            }
        }

        //判断是否是因为low和high而结束的循环
        if (array[mid] != k) {
            return 0;
        }

        //此时 array[mid] = k
        int count = 1;
        //再去 mid 之前和之后去统计 k 的个数
        //--之前
        int i = mid - 1;
        while (i >= 0 && array[i] == k) {
            count++;
            i--;
        }
        //--之后
        i = mid + 1;
        while (i < len && array[i] == k) {
            count++;
            i++;
        }

        return count;
    }
}

在这里插入图片描述

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

超周到的程序员

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值