力扣第237场周赛题解

A. 判断句子是否为全字母句

题目描述:

全字母句指的是字符串中’a’~'z’全部出现至少一遍,判断一个字符串是否为全字母句。

分析:

遍历一遍’a’~'z’即可,若某个字母没有出现则返回false。

class Solution {
    public boolean checkIfPangram(String sentence) {
        char[] c = new char[]{'a','b','c','d','e','f','g','h','i','j','k','l','m','n','o','p','q','r','s','t','u','v','w','x','y','z'};
        for(int i=0;i<26;i++){
            if(sentence.indexOf(c[i])==-1)
                return false;
        }
        return true;
    }
}

B. 雪糕的最大数量

题目描述:

商店中新到 n 支雪糕,用长度为 n 的数组 costs 表示雪糕的定价,其中 costs[i] 表示第 i 支雪糕的现金价格。Tony 一共有 coins 现金可以用于消费,他想要买尽可能多的雪糕。

给你价格数组 costs 和现金量 coins ,请你计算并返回 Tony 用 coins 现金能够买到的雪糕的 最大数量 。

注意:Tony 可以按任意顺序购买雪糕。

分析:

一开始还以为是动态规划模板题,读了一遍才发现是典型的贪心,尽可能地去买最便宜的就好了,直到买不下为止。注意下临界条件。

class Solution {
    public int maxIceCream(int[] costs, int coins) {
        Arrays.sort(costs);
        int sum = 0;
        for(int i=0;i<costs.length;i++){
            if(sum+costs[i]<=coins){
                sum += costs[i];
            }else
                return i;
        }
        return costs.length;
    }
}

C. 单线程 CPU

题目描述:

给你一个二维数组 tasks ,用于表示 n​​​​​​ 项从 0 到 n - 1 编号的任务。其中 tasks[i] = [enqueueTimei, processingTimei] 意味着第 i​​​​​​​​​​ 项任务将会于 enqueueTimei 时进入任务队列,需要 processingTimei 的时长完成执行。

现有一个单线程 CPU ,同一时间只能执行 最多一项 任务,该 CPU 将会按照下述方式运行:

如果 CPU 空闲,且任务队列中没有需要执行的任务,则 CPU 保持空闲状态。
如果 CPU 空闲,但任务队列中有需要执行的任务,则 CPU 将会选择 执行时间最短 的任务开始执行。如果多个任务具有同样的最短执行时间,则选择下标最小的任务开始执行。
一旦某项任务开始执行,CPU 在 执行完整个任务 前都不会停止。
CPU 可以在完成一项任务后,立即开始执行一项新任务。
返回 CPU 处理任务的顺序。

分析:

  1. 每一个任务实际有3个参数,分别是编号、到达时间和持续时间,可以把它抽象成一个Task类。(为了编程方便而已)
  2. 由题目得到,若cpu空闲,优先选择时间最短的任务(时间相同时取下标最短的),把这个优先条件描述成一个比较器Comparator1。(稳定性排序)
  3. 脑内模拟一遍,能进行选取的集合元素也是随着时间的进行而不断变化的,要在变化中每次基于这个Comparator1取出最优的,那就很容易想到堆(优先队列)。
  4. 既然选择了使用优先队列,那么就考虑入队和出队的条件。只要入队了,就有可能被cpu选中,那么入队的条件只有一个,那就是它的到达时间大于当前时间。保证了队内的元素都满足这个条件,那么按照Comparator1排序规则出队的那个一定是当前cpu的执行目标。出队很好写,问题是入队,不管cpu怎么选择,所有的task的入队顺序应该是确定不变的,即入队的时刻大小的排序(同时刻前后关系可以忽略),用指针记录这个下一个该谁入队了,当当前时刻等于指针时就让它入队,并把指针+1。
  5. 当然这么做是很耗时的,有两个地方需要优化。第一,指针不必一个一个加,因为只有当cpu空闲时才有可能发生出队,因此在cpu空闲出队前入队即可,可以让指针直接”快进“到cpu空闲的时刻,把过去的这段时间内的Task一次入队。第二,cpu不空闲时,也没必要入队出队操作,所以任务进入cpu后可以直接给时间加上这个任务的执行时间。总之,会发生入队出队变化的仅仅会发生在cpu刚空闲即可。

这道题本质还是模拟+优化。

class Solution {
    class Task{//将任务抽象为对象
        int i;//编号
        int begin;//到达时间
        int time;//执行时间
        Task(int i,int begin,int time){
            this.i = i;
            this.begin = begin;
            this.time = time;
        }
    }
    public int[] getOrder(int[][] tasks) {
        int[] res = new int[tasks.length];//方法的返回数组
        //将所有的Task按到达时间排序
        List<Task> list = new ArrayList<>();
        for(int i=0;i<tasks.length;i++) 
            list.add(new Task(i,tasks[i][0],tasks[i][1]));
        list.sort(new Comparator<Task>() {
            @Override
            public int compare(Task o1, Task o2) {
                return o1.begin-o2.begin;
            }
        });
        int index = 0;//标记list里已经入队到哪一个了
        //创建优先队列,相当于cpu的“阻塞队列”,从中选取最合适的任务
        Queue<Task> queue = new PriorityQueue<Task>(new Comparator<Task>() {
            @Override
            public int compare(Task o1, Task o2) {
                if(o1.time!=o2.time)
                    return o1.time-o2.time;
                else{
                    return o1.i-o2.i;
                }
            }
        });

        int t = 1;//题目要求的时间是从1开始的,不是0
        int cnt = 0;//已经执行的任务数
        while(true){//每一次循环都相当于cpu空闲一次
            //检查是否有入队任务,如果有的话将能入队的一次性入队
            while(index<list.size()&&list.get(index).begin<=t){
                queue.add(list.get(index));
                index++;
            }
			//CPU空闲且优先队列为空的情况,快进到下一个能入队的起始时间
            if(queue.size()==0){
                t = list.get(index).begin;
                continue;
            }
            //出队元素,赋值,快进到下一次cpu空闲
            Task task = queue.poll();
            res[cnt++] = task.i;
            if(cnt==tasks.length)
                break;
            t += task.time;
        }
        return res;
    }
}

D. 所有数对按位与结果的异或和

题目描述:

列表的 异或和(XOR sum)指对所有元素进行按位 XOR 运算的结果。如果列表中仅有一个元素,那么其 异或和 就等于该元素。

例如,[1,2,3,4] 的 异或和 等于 1 XOR 2 XOR 3 XOR 4 = 4 ,而 [3] 的 异或和 等于 3 。
给你两个下标 从 0 开始 计数的数组 arr1 和 arr2 ,两数组均由非负整数组成。
根据每个 (i, j) 数对,构造一个由 arr1[i] AND arr2[j](按位 AND 运算)结果组成的列表。其中 0 <= i < arr1.length 且 0 <= j < arr2.length 。

返回上述列表的 异或和 。

分析:

  1. 最暴力的方法,按照题意模拟即可,当然会超时。先进行O(mn)的and运算,获得mn个元素,再进行(m*n-1)次的xor运算,总的算法复杂度是O(2mn-1)=O(mn)
  2. 考虑到是二进制运算,且传入参数为整形变量,我们可以对整形的最大32位分别求结果,具体方法为方法一中的mn个元素,我们不关心这些元素具体是多少,只关心它在第i位上是1的元素有多少个(i指的是0~31)。而and运算结果为1就要求两个加数都为1,相当于分别求arr1和arr2中元素在第i位上为1的个数。就比如arr1有5个数在第i位,arr2有6个,那么arr1与arr2的交叉相乘后肯定有30个数在i位上是1。考虑到xor的性质,如果是偶数个数的异或和,那结果一定是0,如果是奇数则为1。所以这个第i位肯定是0.同理i再取0到31的其他值,算出最终结果的二进制,转换为十进制即可。算法复杂度为(32(m+n)),对每一位,只用判断m+n个元素在该位是否为1,代码中的a1[i] * a2[i] % 2实际也只会带入最后一位进行计算进行取余。
public class Solution {
    public int getXORSum(int[] arr1, int[] arr2) {
        StringBuilder r= new StringBuilder();
        int[] a1 = new int[32];
        int[] a2 = new int[32];
        count(arr1, a1);
        count(arr2, a2);
        for(int i=31;i>=0;i--)
            r.append(a1[i] * a2[i] % 2);
        return Integer.parseInt(r.toString(),2);
    }

    private void count(int[] arr, int[] a2) {
        for(int e:arr){
            String s = Integer.toBinaryString(e);
            for(int i=0;i<s.length();i++){
                if(s.charAt(s.length()-i-1)=='1')
                    a2[i]++;
            }
        }
    }
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值