【力扣时间】【539】【中等】最小时间差

马上封版了,之后就可以开始躺平了吗?

1、先读题

这!

如何一句话让社畜想个三分钟。

2、审题

题目简洁到说不出话来,但是具体有什么重点呢?
细说几个:

  1. 时间字符串是24小时制的“HH:MM”。
  2. 要求返回的最小时间差以分钟数表示
  3. 2 <= timePoints.length <= 2 * 104

3、思路

实际上这题我做了两种做法,分别是简单朴素好想的暴力做法,和稍微动脑后的优化循环。

总之,在放出第二种解法前,先讲讲暴力做法吧。
毕竟是我的第一想法。

class Solution {
    public int findMinDifference(List<String> timePoints) {
        List<Integer> timeInts = new ArrayList<>();
        for (String time : timePoints) {
            int timeInt = this.timeToInt(time);

            timeInts.add(timeInt);
            //当时间小于12:00时,同时为他添加一个跨越24:00小时的版本
            if (timeInt <= 720) {
                timeInts.add(timeInt + 1440);
            }
        }
        timeInts.sort(Integer::compareTo);

        int min = Integer.MAX_VALUE;
        for (int i = 0; i < timeInts.size() - 1; i++) {
            min = Math.min(timeInts.get(i + 1) - timeInts.get(i), min);
            //由于排序后,后一个数字一定比前一个大,故当差值为0,即有两个数相等时,无需再判断后续
            if (min == 0) {
                break;
            }
        }
        return min;
    }

    private int timeToInt(String time) {
        String[] t = time.split(":");
        return Integer.parseInt(t[0]) * 60 + Integer.parseInt(t[1]);
    }
}

思路很简单,毕竟题目的要求是返回分钟数的差。于是,首先我们将所有时间字符串转换为分钟数即可。
实现逻辑见timeToInt()方法,没有什么好讲的。

之后我们就得到了一个整数列表,里面存放了每个时间的分钟数。
但是,不仅如此,由于诸如“23:59”分这种时间,可能会和“00:30”这种时间间得到最小时间差值。
所以,为了保证前一天较晚的时间和后一天较早的时间之间可以比较,我将小于“12:00”的时间,都添加了一个加24小时的副本在这个集合内。

for (String time : timePoints) {
    int timeInt = this.timeToInt(time);

    timeInts.add(timeInt);
    //当时间小于12:00时,同时为他添加一个跨越24:00小时的版本
    if (timeInt <= 720) {
        timeInts.add(timeInt + 1440);
    }
}

之后就是对这个列表进行排序,再两两比较时间差,取其差值了。

这个思路显然是可行的,但为什么我又做了第二种呢?
毕竟耗时排名实在是太难看了。
简单分析下时间复杂度就会发现,我经历了一次遍历和一次排序,时间复杂度来到了O(nLogn),而由于我还添加了额外的元素,所以实际的复杂度会更高。
在这里插入图片描述

然后此时,我才真正的开始思考题目的重点。
将所有时间换成分钟数的思路是保留的。
然后24小时制内总共只有1440分钟,但时间字符串列表长度却可达到2 * 104
这就很明显了,只要列表长度超过1440,就必然会有重复的时间字符串,而一旦重复,这题的答案就必定是0

于是,我决定用长度为1440的整数数组来存储所有出现的时间,而一旦发现有重复的时间,就立即返回0
除此之外,假如没有重复,我们也可以在这个长度为1440的数组上遍历,这个时间复杂度无论如何都是

4、来看第二版

class Solution {
    public int findMinDifference(List<String> timePoints) {
        //00:00~23:59,总共1440分钟
        byte[] timeInts = new byte[1440];

        int timeInt = 0;
        for (String time : timePoints) {
            timeInt = this.timeToInt(time);

            //当前分钟已经存在时,即有两个相同的时间时,直接返回0
            if (timeInts[timeInt] != 0) {
                return 0;
            }

            //设置当前时间为1
            timeInts[timeInt] = 1;
        }

        //时间差的最大值也不可能达到1440
        int min = 1440;
        //此时timeInt指向了原列表的最后一个时间
        int next = timeInt + 1;
        int diff = 1;
        //扫了一圈,回到最后一个时间时
        while (next != timeInt) {
            //扫到时间末尾时,回到开头
            if (next == 1440) {
                next = 0;
                continue;
            }

            if (timeInts[next] == 0) {
                //当前时间不存在时,继续查找
                diff++;
            } else {
                //遇到存在的时间时,计算差值
                min = Math.min(min, diff);

                //重新计数
                diff = 1;
            }

            next++;
        }

        min = Math.min(min, diff);
        return min;
    }

    private int timeToInt(String time) {
        String[] t = time.split(":");
        return Integer.parseInt(t[0]) * 60 + Integer.parseInt(t[1]);
    }
}

于是第二版就是这样了。

5、提交

在这里插入图片描述
无论是耗时排名还是内存消耗排名都好上不少。

6、咀嚼

经历了原列表的遍历和我们定长数组的遍历,时间复杂度为O(N + C), C=1440
空间复杂度为O(C )

7、学习大牛

其实,在第二种解法做完后不久,我就发现问题了。
由于分钟数仅有1440个,所以我完全可以根据输入的字符串列表长度是否超过1440来判断,这样的话甚至连循环都省去了。
而能进到我后续的逻辑的情况,肯定是n<=1440的情况,此时让时间复杂度仍然为O( C ) 的话反而增加了耗时。

所以,来看看大牛们的做法吧。

实际上,这次官解的做法就很好了。
这种快速判段的方法的官方名称是鸽巢原理
而第二种解法便是在第一种之上,加上了鸽巢原理的优化。

8、总结

总的来说,今天的中等题做得并不如意。
虽然不知道这种原理的名称,但我还是可以应用这种思想。但后续的判断逻辑就做得一般般了。

在这里插入图片描述

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值