0
点赞
收藏
分享

微信扫一扫

给你一堆需要完成的任务,请问你能按时完成所有任务吗?

给你一堆需要完成的任务,请问你能按时完成所有任务吗?

提示:华为202204之前的面试题
类似于安排会议,排队洗完,排队做咖啡等问题,考验小根堆的熟练运用


文章目录

题目

给你一个N*2的数组arr,代表N个任务,2维信息分别是任务的起始时间,任务的截止时间,就跟大厂笔试题一样,只能在start时间开始做题,也必须在end时间做完,做不完就不合格,完不成任务。注意,这里每一个任务的完成时间都需要1单位时间,请问你能完成这个N个任务吗?


一、审题

目前有这么几个任务:13,12,35,37,1时刻需要加入2个任务,3时刻需要加入俩任务
我们在遇到需要核实任务有没有完成的截止时间时,就需要核实这个截止时间下,之前的时间够不够解决那些需要完成的任务。
显然,
2时刻是一个截止时间,上次开始做任务起始于1,距离上一次可以做任务的时间差为2-1=1,恰好够做一个任务,完成12
3时刻是一个截止时间,上次开始做任务起始于1,距离上一次可以做任务的时间差为3-2=1,恰好够做一个任务,完成13
5时刻是一个截止时间,上次开始做任务起始于3,距离上一次可以做任务的时间差为5-3=2,完全够做一个任务,完成35
7时刻是一个截止时间,上次开始做任务起始于3,距离上一次可以做任务的时间差为7-4=2,完全够做一个任务,完成37
返回true
示例:看图
图1

那完不成的的例子呢?
上面那个案例,在1时刻,还多一个任务13的话,在3时刻,需要核验,那显然时差只有1的情况下,俩13任务,只能完成一个,根本无法完成第二个任务,所以返回false
图2
现在理解题意了吧!


二、解题

这种题,典型的贪心策略:
咱们需要在可以做任务的时候,尽快把截止时间早的干掉,然后在每一个截止时间那检查一下,所有截止时间之前的任务都全部完成了没有,一旦发现还有任务在截止时间之前必须要完成,但是又完不成,必然false。

首先,我们将时间点按照开始时间,和截止时间来做时刻的序列化,目的是绕开所有t+=1的时刻【这算是舍弃思想 ,加速算法】,咱们只在关键start点假如任务,在截止时间点核验任务是否已经完成?

每一个任务建立2个时间点对象【TimePoint】,属性有仨:time,add,end
一个以start作为时间点time,并标记这个时间点是add任务行为吗?是add即true,否则就是截止时间add=false;
如果是add行为,那这个任务截止时间end就有意义,如果是false,那end就没啥用【end是一个伴随数据】
代码如下:

public static class TimePoint{
        public int time;
        public int end;//伴随加任务的end时间
        public boolean isAdd;

        public TimePoint(int t, int e, boolean a){
            time = t;
            end = e;
            isAdd = a;
        }
    }

然后就是算法大流程:
(1)把起始时间和截止时间都序列化,放入时间点对象
(2)然后遍历整个时间点,在起始时间处加入任务,将这个起始任务的伴随截止时间end放入,小根堆堆顶;
(3)途中,只要遇到截止时间点,清算核验是否完成任务?与上一次的时间间隔有多长,就能完成几个任务,完不成就false;

举例:12,12,13,25
(1)序列化时间对象为8长度时间对象:
1【起始伴随截止时间为3】1【起始伴随2】1【起始伴随3】2【截止】2【起始伴随5】3【截止】3【截止】5【截止】
(2)遍历时间点,前3个1,加入heap,排序为233,然后下一次i遇到2,是一个截止时间,curTime=2,preTime=1,d=1,只需要弹出一个任务,代表完成了一个,2弹出;
i继续增加到2,加入heap为5,然后i继续增加到3,是一个截止时间,curTime=3,preTime= 2,d=1,只需要弹出一个任务,这是这段时间内你只能完成的个数,now发现,heap堆顶的截止时间3<=3,你马上过了3这个时间点,还有截止时间为3的任务,你就完不成任务,false;
图2

看代码:

//按照time排序
    public static class timeComparator implements Comparator<TimePoint>{
        @Override
        public int compare(TimePoint o1, TimePoint o2){
            return o1.time - o2.time;//这个时间可能是起始时间,可能是截止时间
        }
    }

    //判断任务可以完成吗?
    public static boolean canDoFinishTask(int[][] arr){
        if (arr == null || arr.length == 0) return true;

        int N = arr.length;
        //先变数组对象,再排序--2倍
        TimePoint[] time = new TimePoint[N << 1];
        for (int i = 0; i < N; i++) {
            time[i] = new TimePoint(arr[i][0], arr[i][1], true);//加任务add为true,end有效
            time[i + N] = new TimePoint(arr[i][1], arr[i][1], false);//非add,end无效
        }
        //Arrays.sort(time,(a, b)->a.time - b.time);//这难道又是一种新的玩意?
        Arrays.sort(time, new timeComparator());//这难道又是一种新的玩意?

        PriorityQueue<Integer> heap = new PriorityQueue<>();
        int preTime = time[0].time;//此前的时间点
        //核验一遍所有的时间点,是加就让任务的end时间进heap,否则核验
        for (int i = 0; i < 2*N; i++) {
            if (time[i].isAdd) heap.add(time[i].end);
            else {
                //否则就是截止时间,加入是不干
                int curTime = time[i].time;//这种是截止时间
                //与此前的  时间差  就是我们可以完成的任务数
                for (int j = preTime; j < curTime; j++) {
                    heap.poll();//不断地完成截止时间早的任务
                    if (heap.isEmpty()) break;//没了提前出来
                }
                //然后看看还有任务竟然比我curTime早吗,早的话就完蛋
                if (heap.peek() <= curTime) return false;

                preTime = curTime;
            }
        }

        //全部核验完成都没有false
        return true;
    }

测试:

public static void test(){
        int[][] arr = {
                {1,3},{1,2},{1,3},{1,4}
        };
        System.out.println(canDoFinishTask(arr));
    }

    public static void main(String[] args) {
        test();
    }

总结

提示:重要经验:

1)理解类似起止时间的对象这种任务,往往是堆来解决
2)贪心策略,多练习,多熟悉;

举报

相关推荐

0 条评论