java算法题-在区间范围内统计奇数数目

createh51周前 (05-16)技术教程4

在leetcode(https://leetcode-cn.com/)上看到一道有趣的算法题:

给你两个非负整数 low 和 high 。请你返回 low 和 high 之间(包括二者)奇数的数目。

示例 1:

输入:low = 3, high = 7

输出:3

解释:3 到 7 之间奇数数字为 [3,5,7] 。

示例 2:

输入:low = 8, high = 10

输出:1

解释:8 到 10 之间奇数数字为 [9] 。

提示:

  • 0 <= low <= high <= 10^9

这样的题你会怎么用java实现呢?

判断一个数x是否为奇数很容易,比如x%2>0时,则x为奇数,基于这个分享一下我的解题思路:

  • 普通

在给定的范围low到high之间从low往右遍历和从high往左遍历

public int countOdds(int low, int high) {
    if (low == high) {
        return low % 2 > 0 ? 1 : 0;
    }
    int sum = 0;
    for (; low <= high; ) {
        if (low == high) {
            if (low % 2 > 0) {
                sum += 1;
            }
        } else {
            if (low % 2 > 0) {
                sum += 1;
            }
            if (high % 2 > 0) {
                sum += 1;
            }
        }
        low++;
        high--;
    }
    return sum;
}
  • 进阶

上边的方法其实还不够优雅,毕竟需要一个一个数去遍历,虽然是从两个方向同时进行判断,但当给定数的值很大的时候,就比较耗时了,比如在low=327296043,high=769434803时,程序执行的耗时高达1152ms(以leetcode平台执行该函数所需的时间为参考),对于一个好的算法来说这是严重不合格的。那么我们换种思路,其实个位数分别为1,2,3,4,...8,9等10个数之间,奇数的个数是固定的,为5个,比如1,2,3,4,...8,9这几个数之间有5个奇数,对应的比如31,32,33,34,...,38,39,这几个数之间也有5个奇数,所以我们只需要将给定的两个数low和high处理一下变为两个个位数都为0的数,然后计算下这两个数之间包含多少个“长度为10,个位数从1到9的数”,然后乘以5,再加上两个数处理前的一些奇数就可以得到我们想要的结果了,代码如下:

public int countOdds(int low, int high) {
        if (low == high) {
            return low % 2 > 0 ? 1 : 0;
        }
        Long sum = 0L;
        int startLowUnitsDigit = low % 10;
        int startHighUnitsDigit = high % 10;
        long newLow = low;
        long newHigh = high;
        if (startLowUnitsDigit > 0) {
            newLow = low + (10 - startLowUnitsDigit);
        }
        if (startHighUnitsDigit > 0) {
            newHigh = high - startHighUnitsDigit;
        }
        if ((newHigh - newLow) % 10 == 0) {
            sum=(newHigh - newLow)*5/10;
            for (; low <= newLow; ) {
                if (low == newLow) {
                    if (low % 2 > 0) {
                        sum +=1;
                    }
                } else {
                    if (low % 2 > 0) {
                        sum +=1;
                    }
                    if (newLow % 2 > 0) {
                        sum +=1;
                    }
                }
                low++;
                newLow--;
            }
            for (; newHigh <= high; ) {
                if (high == newHigh) {
                    if (newHigh % 2 > 0) {
                        sum +=1;
                    }
                } else {
                    if (newHigh % 2 > 0) {
                        sum +=1;
                    }
                    if (high % 2 > 0) {
                        sum +=1;
                    }
                }
                newHigh++;
                high--;
            }
        } else {
            for (; low <= high; ) {
                if (low == high) {
                    if (low % 2 > 0) {
                        sum +=1;
                    }
                } else {
                    if (low % 2 > 0) {
                        sum +=1;
                    }
                    if (high % 2 > 0) {
                        sum +=1;
                    }
                }
                low++;
                high--;
            }
        }
        return sum.intValue();
    }

给定同样的值,low=327296043,high=769434803,好家伙,这里的执行时间一下子从1152ms降为0ms(以leetcode平台执行该函数所需的时间为参考),程序性能大大提升。

以long存储sum是因为计算的结果可能超过int值的最大范围

程序执行结果:

输入:low=327296043,high=769434803
输出:221069381

leetcode上对该答案的分析如下:

84 / 84 个通过测试用例

状态:通过

执行用时: 0 ms, 在所有 Java 提交中击败了100.00%的用户

内存消耗: 35.4 MB, 在所有 Java 提交中击败了14.80%的用户

执行用时分布图表


执行消耗内存分布图表



大家有更好的解题思路吗?欢迎在评论区作答哈~

相关文章

JVM 深度解析:运行时数据区域、分代回收与垃圾回收机制全攻略

共同学习,有错欢迎指出。JVM 运行时数据区域1. 程序计数器程序计数器是一块较小的内存空间,可看作当前线程所执行的字节码的行号指示器。在虚拟机概念模型里,字节码解释器通过改变这个计数器的值选取下一条...

图解常见的限流算法(计数器、滑动窗口计数、漏桶、令牌桶)

哈喽,大家好呀,我是呼噜噜,好久没有更新文章了,今天我们来聊聊在企业级项目中,常见的几种限流手段的原理及其实现什么场景需要限流随着互联网的业务发展,比如秒杀、双十一、618等这些我们耳熟能详,也有被人...

Java高并发解决方案:轻松应对海量请求

Java高并发解决方案:轻松应对海量请求在当今互联网时代,高并发问题已经成为每个Java开发者绕不开的话题。无论是电商平台的大促活动,还是社交平台的热门话题讨论,都可能瞬间产生海量请求。那么,我们该如...

Java并发工具:CountDownLatch

CountDownLatch是Java并发包(java.util.concurrent)中提供的一种同步工具,用于控制一个或多个线程等待其他线程完成操作。它是一个非常有用的工具类,常用于协调多个线程之...

踩坑!Java集合必学技能:Collection.size()方法深度解析与避坑

一、开发中遇到的问题:动态集合统计的"陷阱"在实际开发中,我们经常需要统计集合中的元素数量。例如,电商订单处理场景中需要实时统计待处理订单数。但如果不理解size()方法的底层逻辑,容...