力扣(leetcode)第 237 场周赛,「 依图科技 & 力扣」联合主办。

这里写目录标题

    • 1.判断句子是否全为字母句
      • 题目描述
      • 示例
      • 思路
      • Java代码
    • 2.雪糕的最大数量
      • 题目描述
      • 示例
      • 思路
      • Java代码
    • 3.单线程 CPU
      • 题目描述
      • 示例
      • 思路
      • Java代码
    • 4.所有数对按位与结果的异或和
      • 题目描述
      • 示例
      • 思路
      • Java代码
    • 总结

本场竞赛由「 依图科技 & 力扣」联合主办
力扣(leetcode)第 237 场周赛,「 依图科技 & 力扣」联合主办。_第1张图片

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

题目描述

全字母句 指包含英语字母表中每个字母至少一次的句子。给你一个仅由小写英文字母组成的字符串 sentence ,请你判断 sentence 是否为 全字母句 。如果是,返回 true ;否则,返回 false 。

示例

示例 1:

输入:sentence = "thequickbrownfoxjumpsoverthelazydog"
输出:true
解释:sentence 包含英语字母表中每个字母至少一次。

示例 2:

输入:sentence = "leetcode"
输出:false

思路

看到题目大概就知道怎么做了,其实就是判断一个字符串中是否包含26个字母,包含的话就输出true,不包含就输出false。可以借助HashSet来解决,设置一个count来计数字符数,遍历字符串,将每一个字符都放入到HashSet中,如果可以顺利放入,就将count加一,如果不能顺利放入,说明此字符已经在HashSet中了,继续遍历,直到结束。最后判断count是否等于26,输出结果。

Java代码

class Solution {
     
    public boolean checkIfPangram(String sentence) {
     
        //使用HastSet来解决
        HashSet<Character> result = new HashSet<>();
        int len = sentence.length();
        int count = 0;
        for(int i=0;i<len;i++){
     
            if(result.add(sentence.charAt(i))!=false)//说明当前字符还未加入到HashSet中,那么count就加一
                count++;
        }
        if(count !=26)
            return false;
        else return true;
    }
}

2.雪糕的最大数量

题目描述

夏日炎炎,小男孩 Tony 想买一些雪糕消消暑。商店中新到 n 支雪糕,用长度为 n 的数组 costs 表示雪糕的定价,其中 costs[i] 表示第 i 支雪糕的现金价格。Tony 一共有 coins 现金可以用于消费,他想要买尽可能多的雪糕。给你价格数组 costs 和现金量 coins ,请你计算并返回 Tony 用 coins 现金能够买到的雪糕的最大数量 。

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

示例

示例 1:
输入:costs = [1,3,2,4,1], coins = 7
输出:4
解释:Tony 可以买下标为 0124 的雪糕,总价为 1 + 3 + 2 + 1 = 7

示例2:

输入:costs = [10,6,8,7,7,8], coins = 5
输出:0
解释:Tony 没有足够的钱买任何一支雪糕。

示例 3:

输入:costs = [1,6,3,1,2,5], coins = 20
输出:6
解释:Tony 可以买下所有的雪糕,总价为 1 + 6 + 3 + 1 + 2 + 5 = 18

提示

costs.length == n
1 <= n <= 105
1 <= costs[i] <= 105
1 <= coins <= 108

思路

本题考查的点是贪心。 首先先对costs数组进行排序,从小到大来排序,然后拿coins依次减,直到减不了为止,使用count来计数。

Java代码

class Solution {
     
    public int maxIceCream(int[] costs, int coins) {
     
        //先对数组排序,然后依次来减
        int len = costs.length;
        if(coins==0) return 0;  //自己的coins为0时,直接返回0
        Arrays.sort(costs);
        if(costs[0]>coins) return 0;//雪糕的最小价格都大于coins,自然是买不起任何一个
        int count = 0;
        for(int i =0;i<len;i++){
     
            if(coins>=costs[i]){
     
                count++;
                coins = coins-costs[i];
            }
        }
        return count;
    }
}

3.单线程 CPU

题目描述

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

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

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

示例

示例 1:

输入:tasks = [[1,2],[2,4],[3,2],[4,1]]
输出:[0,2,3,1]
解释:事件按下述流程运行: 
- time = 1 ,任务 0 进入任务队列,可执行任务项 = {
     0}
- 同样在 time = 1 ,空闲状态的 CPU 开始执行任务 0 ,可执行任务项 = {
     }
- time = 2 ,任务 1 进入任务队列,可执行任务项 = {
     1}
- time = 3 ,任务 2 进入任务队列,可执行任务项 = {
     1, 2}
- 同样在 time = 3 ,CPU 完成任务 0 并开始执行队列中用时最短的任务 2 ,可执行任务项 = {
     1}
- time = 4 ,任务 3 进入任务队列,可执行任务项 = {
     1, 3}
- time = 5 ,CPU 完成任务 2 并开始执行队列中用时最短的任务 3 ,可执行任务项 = {
     1}
- time = 6 ,CPU 完成任务 3 并开始执行任务 1 ,可执行任务项 = {
     }
- time = 10 ,CPU 完成任务 1 并进入空闲状态

示例 2:

输入:tasks = [[7,10],[7,12],[7,5],[7,4],[7,2]]
输出:[4,3,2,0,1]
解释:事件按下述流程运行: 
- time = 7 ,所有任务同时进入任务队列,可执行任务项  = {
     0,1,2,3,4}
- 同样在 time = 7 ,空闲状态的 CPU 开始执行任务 4 ,可执行任务项 = {
     0,1,2,3}
- time = 9 ,CPU 完成任务 4 并开始执行任务 3 ,可执行任务项 = {
     0,1,2}
- time = 13 ,CPU 完成任务 3 并开始执行任务 2 ,可执行任务项 = {
     0,1}
- time = 18 ,CPU 完成任务 2 并开始执行任务 0 ,可执行任务项 = {
     1}
- time = 28 ,CPU 完成任务 0 并开始执行任务 1 ,可执行任务项 = {
     }
- time = 40 ,CPU 完成任务 1 并进入空闲状态

思路

看到题目到理解题目,其实还是比较容易的,就是在实现起来有点复杂,代码编写的过程中需要注意一些细节的地方。

  1. 创建一个新的二维数组,在保留题目提供的tasks数据的基础上,再把下标存进去;
  2. 把task按任务起始时间排序;
  3. 建小顶堆,堆顶元素是时长最小的任务,时长相同的按下标排序;(使用PriorityQueue)
  4. 初始化当前时间now = 1,遍历task中的任务,当堆空或者now>=task[i][0] (即任务开始时间)时,将任务加入堆中,之后再弹出堆顶元素,根据弹出的新任务需要执行的时间来更新当前时刻now
  5. 重复4直道所有任务都被遍历
  6. 依次弹出堆中所有元素

PriorityQueue:基于优先级堆的无限优先级queue。优先级队列的元素根据它们的有序natural ordering,或由一个Comparator在队列构造的时候提供,这取决于所使用的构造方法。优先队列不允许null元素。依靠自然排序的优先级队列也不允许插入不可比较的对象

Java代码

解法一:借助数组

class Solution {
     
    public int[] getOrder(int[][] tasks) {
     
        int n = tasks.length;
        int[][] task = new int[n][3];//存原数组,并加入id
        for (int i = 0; i < tasks.length; i++) {
     
            task[i][0] = tasks[i][0];
            task[i][1] = tasks[i][1];
            task[i][2] = i;
        }

        Arrays.sort(task, (x,y)->x[0]-y[0]);//先按照起始时间排序
        PriorityQueue<Integer> heap = new PriorityQueue<>((x,y)->{
     //优先队列
            if(task[x][1]==task[y][1]){
     //当两者的需要的时间一样的时候,先执行id小的。
                return task[x][2]-task[y][2];
            }
            return task[x][1]-task[y][1];
        });
        int now = 1;
        int[] ret = new int[n];//存最终的结果
        int i = 0;//遍历线程任务
        int j = 0;//存线程执行顺序
        while(i<n){
     
            while(i<n&&(heap.isEmpty()||now>=task[i][0])){
     
                now = Math.max(now,task[i][0]);//获取当前时间
                heap.add(i++);
            }
            int[]task1 = task[heap.poll()];//栈顶为当前第i线程
            ret[j++] = task1[2];//将id加入到ret中
            now += task1[1];//此刻的时间为now+task1[1]
        }
        while(!heap.isEmpty()){
     //说明最后还有线程在栈顶中
            ret[j++] = task[heap.poll()][2];
        }
        return ret;
    }
}

解法二:借助类

class Solution {
     
    //构造任务类 Java面向对象编程
    class Task {
     
        public int index;//索引属性
        public int eTime;//线程开始时间
        public int pTime;//线程完成所需时间

        public Task(int index,int eTime, int pTime) {
     //有参构造
            this.index = index;
            this.eTime = eTime;
            this.pTime = pTime;
        }
    }

    public int[] getOrder(int[][] tasks) {
     
        PriorityQueue<Task> queue = new PriorityQueue<>((a,b) -> {
     if (a.pTime == b.pTime) return a.index - b.index;else return a.pTime - b.pTime;});//构造队列,如果线程完成所需时间一样,那么索引小的先开始执行
        int[] res = new int[tasks.length];//存放最终的执行顺序
        int count = 0;//计数
        int cur = 0;

        ArrayList<Task> list = new ArrayList<>();
        for (int i = 0; i < tasks.length; i++) 
            list.add(new Task(i, tasks[i][0], tasks[i][1]));
        
        // Collections.sort(list,Comparator.comparingInt(a->a.eTime));
        list.sort(Comparator.comparingInt(a -> a.eTime));//根据线程开始时间排序
        
        int nowTime = list.get(0).eTime;//第一个线程开始的时间,即为现在的时间。

        while (count < tasks.length && list.get(count).eTime <= nowTime)
            queue.add(list.get(count++));

        while (!queue.isEmpty()){
     
            Task t = queue.remove();
            nowTime += t.pTime;
            res[cur++] = t.index;

            if (queue.isEmpty() && count < tasks.length && list.get(count).eTime > nowTime)
                //以下三个均可
                nowTime += list.get(count).eTime;
            	//nowTime = list.get(count).eTime;
            	//nowTime +=list.get(count).pTime;
            while (count < tasks.length && list.get(count).eTime <= nowTime)//将线程加入队列中
                queue.add(list.get(count++));
        }

        return res;
    }
}

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

题目描述

列表的 异或和(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:

输入:arr1 = [1,2,3], arr2 = [6,5]
输出:0
解释:列表 = [1 AND 6, 1 AND 5, 2 AND 6, 2 AND 5, 3 AND 6, 3 AND 5] = [0,1,2,0,2,1] ,
异或和 = 0 XOR 1 XOR 2 XOR 0 XOR 2 XOR 1 = 0

示例 2:

输入:arr1 = [12], arr2 = [4]
输出:4
解释:列表 = [12 AND 4] = [4] ,异或和 = 4

思路

利用xor和and之间的分配律:(a xor b) and c = (a and c) xor (b and c)

来简化求解。

原式最终可以被化简为:

(a_1 xor a_2 xor … xor a_n) and (b_1 xor b_2 xor … xor b_m)

从而容易求得。
力扣(leetcode)第 237 场周赛,「 依图科技 & 力扣」联合主办。_第2张图片

Java代码

class Solution {
     
    public int getXORSum(int[] arr1, int[] arr2) {
     
        int sum = 0;
        int result = 0;
        for (int i : arr2) {
     //第一步
            sum^=i;
        }
        for(int i:arr1){
     //第二步
            result ^= (i&sum);
        }
        return result;
    }
}

总结

通过本次周赛,又加深了自己对Java的学习,有些题目从有思路->用编程的方式写出来,还是有一个过程的,说明思想到编程的转换做的害不够好。对一些比较常用的集合,在平时的编程中要多加的使用,这样可以加深自己的理解。

时间:2021/4/19

你可能感兴趣的:(#,力扣刷题笔记,算法编程学习,java,leetcode,数据结构,队列)