Leetcode 数组(简单题)[1000题之后]

答案摘抄自:
https://leetcode-cn.com/tag/array/

1002. 查找常用字符

给定仅有小写字母组成的字符串数组 A,返回列表中的每个字符串中都显示的全部字符(包括重复字符)组成的列表。例如,如果一个字符在每个字符串中出现 3 次,但不是 4 次,则需要在最终答案中包含该字符 3 次。

你可以按任意顺序返回答案。

示例 1:
输入:[“bella”,“label”,“roller”]
输出:[“e”,“l”,“l”]
示例 2:
输入:[“cool”,“lock”,“cook”]
输出:[“c”,“o”]

方法:hash 数组
定义一个整形数组 countercounter 记录交集字母出现的次数,下标为 keykey,值为交集字母的次数。
以第零个单词 A[0]A[0] 为基准,确定该单词的字母频次,记录于 countercounter 数组中。
然后从第一个单词 A[1]A[1] 开始遍历,并总结出这些单词的频次,记录于本次遍历的局部变量数组 tempArrtempArr 中。
因为题目要求统计的是所有单词中的出现频次多的字母,所以这些字母必须都在全部单词中出现过才能作为结果的一部分。
以字母 aa 在 A[0] = ccc 和 A[1]=cca 为出现次数为例:
在 A[0]A[0] 中出现 00 次,
在 A[1]A[1] 中出现 11 次。
那么字母 aa 在结果列表中将出现 00 次。
最后把 countercounter 的下标(即 keykey)与字符 aa 的 ASCIIASCII 码相加,转化为具体的字符存入 ListList 中。

综上所述,这本质上是一个在所有单词中求出现频次大于 11 的字母的交集的问题。

public List<String> commonChars(String[] A) {
    int[] counter = new int[26];
    for(char c : A[0].toCharArray())  // 统计第1个单词各个字母的词频
        counter[c - 'a']++;
    
    for (int i = 1; i < A.length; i++) {
        int[] tempArr = new int[26];
        for (char c : A[i].toCharArray()) {
            tempArr[c - 'a']++;      // 后一个单词的字母词频
        }
        for (int j = 0; j < 26; j++) {
            if(tempArr[j] < counter[j])
                counter[j] = tempArr[j];  // 字母频次的交集
        }
    }

    LinkedList<String> resList = new LinkedList<>();
    for (int i = 0; i < 26; i++) {
        while (counter[i]-- > 0) {  // counter[0] 记录字母a的出现次数
            resList.addLast("" + (char)('a' + i));
        }
    }
    return resList;
}

1010. 总持续时间可被 60 整除的歌曲

在歌曲列表中,第 i 首歌曲的持续时间为 time[i] 秒。
返回其总持续时间(以秒为单位)可被 60 整除的歌曲对的数量。形式上,我们希望索引的数字 i < j 且有 (time[i] + time[j]) % 60 == 0。
示例 1:
输入:[30,20,150,100,40]
输出:3
解释:这三对的总持续时间可被 60 整数:
(time[0] = 30, time[2] = 150): 总持续时间 180
(time[1] = 20, time[3] = 100): 总持续时间 120
(time[1] = 20, time[4] = 40): 总持续时间 60
示例 2:
输入:[60,60,60]
输出:3
解释:所有三对的总持续时间都是 120,可以被 60 整数。
方法一:暴力枚举(超时)
对于每一个数字,我的都检查其后面的所有数字,看是否有数字能与本数字组成符合要求的一对数。

public int numPairsDivisibleBy60(int[] time) {
    int count = 0;
    for (int i = 0; i < time.length; i++) {
        for (int j = i + 1; j < time.length; j++)
        if ((time[i] + time[j]) % 60 == 0)
            count++;
    }
    return count;
}

复杂度分析:
时间复杂度:O(N^2),对于每一个数,都要检查其末尾的所有数字。
空间复杂度:O(1),使用了常数级别的空间。
解法二:取余求差法
其实我们不必没经过一个数字就去检查后面的所有数字,而是采用数组存储每一个数字的补数。这样问题相当于特殊条件的两数之和问题:计算相加后等于 6060 的倍数的数字对的个数。
定义变量 remainder 记录当前余数,大小为60的counter数组计数,从头遍历这个数组[30,20,150,100,40],我们只需要统计某个数字 time[i] 的之前的数字 60-i 的数量。以下幻灯片体现了整个过程:

public int numPairsDivisibleBy60(int[] time) {

    int count = 0;
    int[] counter = new int[60];

    for (int t : time) {
        int remainder = t % 60;             //余数
        if (remainder == 0) 
            count += counter[remainder];    //如60,120,180这些只能相互成对。
        else if (counter[60-remainder] > 0){
            count += counter[60-remainder]; //如30,150
        }
        counter[remainder]++;
    }
    return count;
}

复杂度分析:
时间复杂度:O(N),N 为数组 time 的大小。
空间复杂度:O(1),使用了常数级别的空间。

1013. 将数组分成和相等的三个部分

给你一个整数数组 A,只有可以将其划分为三个和相等的非空部分时才返回 true,否则返回 false。

形式上,如果可以找出索引 i+1 < j 且满足 (A[0] + A[1] + … + A[i] == A[i+1] + A[i+2] + … + A[j-1] == A[j] + A[j-1] + … + A[A.length - 1]) 就可以将数组三等分。

示例 1:
输出:[0,2,1,-6,6,-7,9,1,2,0,1]
输出:true
解释:0 + 2 + 1 = -6 + 6 - 7 + 9 + 1 = 2 + 0 + 1
示例 2:
输入:[0,2,1,-6,6,7,9,-1,2,0,1]
输出:false
示例 3:
输入:[3,3,6,5,-2,2,5,1,-9,4]
输出:true
解释:3 + 3 = 6 = 5 - 2 + 2 + 5 + 1 - 9 + 4

方法:双指针
数组元素的总和 sum 不是3的倍数,直接返回false
使用双指针left,right, 从数组两头开始一起找,节约时间
当 left + 1 < right 的约束下,可以找到数组两头的和都是 sum/3,那么中间剩下的元素和就一定也是sum/3
(left + 1 < right的约束就是要中间有剩下的元素,使用left < right的约束,数组可能可以恰好被划分成两部分,中间没有元素)

class Solution {
    public boolean canThreePartsEqualSum(int[] A) {
        int sum = 0;
        for(int i : A){
            sum += i;
        }
        if(sum%3 != 0){
            // 总和不是3的倍数,直接返回false
            return false;
        }

        // 使用双指针,从数组两头开始一起找,节约时间
        int left = 0;
        int leftSum = A[left];
        int right = A.length - 1;
        int rightSum = A[right];

        // 使用left + 1 < right 的原因,防止只能将数组分成两个部分
        // 例如:[1,-1,1,-1],使用left < right作为判断条件就会出错
        while(left + 1 < right){
            if(leftSum == sum/3 && rightSum == sum/3){
                // 左右两边都等于 sum/3 ,中间也一定等于
                return true;
            }
            if(leftSum != sum/3){
                // left = 0赋予了初值,应该先left++,在leftSum += A[left];
                leftSum += A[++left];
            }
            if(rightSum != sum/3){
                // right = A.length - 1 赋予了初值,应该先right--,在rightSum += A[right];
                rightSum += A[--right];
            }
        }
        return false;  
    }
}

1018. 可被 5 整除的二进制前缀

给定由若干 0 和 1 组成的数组 A。我们定义 N_i:从 A[0] 到 A[i] 的第 i 个子数组被解释为一个二进制数(从最高有效位到最低有效位)。

返回布尔值列表 answer,只有当 N_i 可以被 5 整除时,答案 answer[i] 为 true,否则为 false。

示例 1:
输入:[0,1,1]
输出:[true,false,false]
解释:
输入数字为 0, 01, 011;也就是十进制中的 0, 1, 3 。只有第一个数可以被 5 整除,因此 answer[0] 为真。
示例 2:
输入:[1,1,1]
输出:[false,false,false]
示例 3:
输入:[0,1,1,1,1,1]
输出:[true,false,false,false,true,false]
示例 4:
输入:[1,1,1,0,1]
输出:[false,false,false,false,false]

class Solution {
    public List<Boolean> prefixesDivBy5(int[] A) {
        List<Boolean> res = new ArrayList<Boolean>();
    int tail = 0;
    for(int i: A) {
        tail = tail * 2 + i;
        tail = (tail > 9) ? tail - 10 : tail;
        if(tail == 0 || tail == 5) {
            res.add(true);
        } else {
            res.add(false);
        }
    }
    return res;
    }
}

1160. 拼写单词

给你一份『词汇表』(字符串数组) words 和一张『字母表』(字符串) chars。
假如你可以用 chars 中的『字母』(字符)拼写出 words 中的某个『单词』(字符串),那么我们就认为你掌握了这个单词。
注意:每次拼写时,chars 中的每个字母都只能用一次。
返回词汇表 words 中你掌握的所有单词的 长度之和。

示例 1:
输入:words = [“cat”,“bt”,“hat”,“tree”], chars = “atach”
输出:6
解释:
可以形成字符串 “cat” 和 “hat”,所以答案是 3 + 3 = 6。
示例 2:
输入:words = [“hello”,“world”,“leetcode”], chars = “welldonehoneyr”
输出:10
解释:
可以形成字符串 “hello” 和 “world”,所以答案是 5 + 5 = 10。
方法一:

//统计字符表中各自字符所出现的次数;
//对词汇表中的每个单词也做上述统计;
//比较1,2;如果1中各个字符出现的次数总是大于等于2,则符合要求;
class Solution {
    public int countCharacters(String[] words, String chars) {
        int[] charsOfCase = new int[26];//统计字符表中的字符
        int count = 0;//单词长度之和

        for(char c : chars.toCharArray()){//统计字符表中的字符
            charsOfCase[c-'a']++;
        }
        for(String str : words){
            int[] temp = new int[26];
            boolean flag = true;
            for(char c : str.toCharArray()){//存储统计词汇表中单词的字符
                temp[c-'a']++;
            }
            for(int i = 0; i < charsOfCase.length; i++){//对比两个统计数据
                if(temp[i] > charsOfCase[i]){
                    flag = false;
                    break;
                }
            }
            if(flag) count += str.length();
        }
        return count;
    }
}

方法二:HashMap

class Solution 
{
    public int countCharacters(String[] words, String chars) 
    {
        if(words.length==0||chars.length()==0)return 0;
        char[] ch=chars.toCharArray();
        HashMap<Character,Integer> map=new HashMap<>();
        int ans=0;
        for(char c:ch)
        {
            map.put(c,map.getOrDefault(c,0)+1);
        }
        int len=words.length;
        for(int i=0;i<=len-1;i++)
        {
            boolean flag=false;
            char[] cc=words[i].toCharArray();
            HashMap<Character,Integer> temp=(HashMap)map.clone();
            for(int j=0;j<=cc.length-1;j++)
            {
                if(!map.containsKey(cc[j])){flag=true;break;}//no such,break
                else
                {
                    if(temp.get(cc[j])>=1)temp.put(cc[j],temp.get(cc[j])-1);//enough,then -1
                    else {flag=true;break;}//not enough,break
                }
            }
            if(flag)continue;
            ans+=words[i].length();
        }
        return ans;
    }
}

1051. 高度检查器

学校在拍年度纪念照时,一般要求学生按照 非递减 的高度顺序排列。
请你返回能让所有学生以 非递减 高度排列的最小必要移动人数。
注意,当一组学生被选中时,他们之间可以以任何可能的方式重新排序,而未被选中的学生应该保持不动。

示例1:
输入:heights = [1,1,4,2,1,3]
输出:3
解释:
当前数组:[1,1,4,2,1,3]
目标数组:[1,1,1,2,3,4]
在下标 2 处(从 0 开始计数)出现 4 vs 1 ,所以我们必须移动这名学生。
在下标 4 处(从 0 开始计数)出现 1 vs 3 ,所以我们必须移动这名学生。
在下标 5 处(从 0 开始计数)出现 3 vs 4 ,所以我们必须移动这名学生。
示例 2:
输入:heights = [5,1,2,3,4]
输出:5
示例 3:
输入:heights = [1,2,3,4,5]
输出:0

class Solution {
    public int heightChecker(int[] heights) {
        int[] arr = new int[101];
        for (int height : heights) {
            arr[height]++;
        }
        int count = 0;
        for (int i = 1, j = 0; i < arr.length; i++) {
            while (arr[i]-- > 0) {
                if (heights[j++] != i) count++;
            }
        }
        return count;
    }
}

1089. 复写零

给你一个长度固定的整数数组 arr,请你将该数组中出现的每个零都复写一遍,并将其余的元素向右平移。

注意:请不要在超过该数组长度的位置写入元素。
要求:请对输入的数组 就地 进行上述修改,不要从函数返回任何东西。

示例 1:
输入:[1,0,2,3,0,4,5,0]
输出:null
解释:调用函数后,输入的数组将被修改为:[1,0,0,2,3,0,0,4]
示例 2:
输入:[1,2,3]
输出:null
解释:调用函数后,输入的数组将被修改为:[1,2,3]

class Solution {
    public void duplicateZeros(int[] arr) {
        int i = 0 , zero = 0 , l = arr.length-1;
        while(i <= l){
            if(arr[i] == 0)
                zero++;
            if(i + zero >= l)
                break;
            i++;
        }
        while(i >=0){
            arr[l--] = arr[i];
            if(arr[i] == 0 && (i + zero <= arr.length-1) )//<=用来判断最后一个如果是0是否超出边界
                 arr[l--] = arr[i];
            i--;
        }
    }
}

1122. 数组的相对排序

给你两个数组,arr1 和 arr2,
arr2 中的元素各不相同
arr2 中的每个元素都出现在 arr1 中
对 arr1 中的元素进行排序,使 arr1 中项的相对顺序和 arr2 中的相对顺序相同。未在 arr2 中出现过的元素需要按照升序放在 arr1 的末尾。

示例:
输入:arr1 = [2,3,1,3,2,4,6,7,9,2,19], arr2 = [2,1,4,3,9,6]
输出:[2,2,2,1,4,3,3,9,6,7,19]

方法:桶排序

class Solution {
    public int[] relativeSortArray(int[] arr1, int[] arr2) {
        //由于arr1的可能取值为0-1000,共1001个取值,不算一个很大的取值范围,所以可以利用桶式排序
        int[] bucket = new int[1001];
        //计数每个桶有多少个数,也就是每个值在数组中有几个
        for(int num:arr1){
            bucket[num]++;
        }
        //由于重新排序不会改变数组的长度,所以可以利用原数组,可以节省空间复杂度
        int i = 0;
        //由于排序是按照相对顺序进行排序,所以,首先根据arr2中的桶的顺序,依次从对应的桶中取数到arr1中
        //并注意,每拿出一个数,需要将对桶中的数的个数进行-1操作
        for(int num:arr2){
            while(bucket[num]-- > 0){
                arr1[i++] = num;
            }
        }
        //未在arr2中的桶中的数,按照桶号升序进行输出,所以进行二次遍历
        for(int j = 0; j < 1001; ++j){
            while(bucket[j]-- > 0){
                arr1[i++] = j;
            }
        }
        return arr1;
    }
}

1287. 有序数组中出现次数超过25%的元素

给你一个非递减的 有序 整数数组,已知这个数组中恰好有一个整数,它的出现次数超过数组元素总数的 25%。
请你找到并返回这个整数
示例:
输入:arr = [1,2,2,6,6,6,6,7,10]
输出:6

方法:双指针

class Solution {
    public int findSpecialInteger(int[] arr) {
        int before = arr.length / 4 + 1;
        for(int i = 0; before < arr.length; i++, before++){
            if(arr[i] == arr[before]) return arr[i];
        }
        return arr[arr.length-1];
    }
}

1128. 等价多米诺骨牌对的数量

给你一个由一些多米诺骨牌组成的列表 dominoes。

如果其中某一张多米诺骨牌可以通过旋转 0 度或 180 度得到另一张多米诺骨牌,我们就认为这两张牌是等价的。
形式上,dominoes[i] = [a, b] 和 dominoes[j] = [c, d] 等价的前提是 ac 且 bd,或是 ad 且 bc。
在 0 <= i < j < dominoes.length 的前提下,找出满足 dominoes[i] 和 dominoes[j] 等价的骨牌对 (i, j) 的数量。
示例:
输入:dominoes = [[1,2],[2,1],[3,4],[5,6]]
输出:1

class Solution {
    public int numEquivDominoPairs(int[][] dominoes) {
        int ans = 0;
        int[] cp = new int[100];
        for(int[] arr:dominoes){
            Arrays.sort(arr);
            ans+=cp[arr[0]*10+arr[1]]++;
        }
        return ans;
    }
}

1299. 将每个元素替换为右侧最大元素

给你一个数组 arr ,请你将每个元素用它右边最大的元素替换,如果是最后一个元素,用 -1 替换。

完成所有替换操作后,请你返回这个数组。

示例:
输入:arr = [17,18,5,4,6,1]
输出:[18,6,6,6,1,-1]

方法:逆序遍历
从右往左遍历,先记录右边最大值 rightMax 为最后一个值,向左每次更新 rightMax,使用变量 t 先记住当前 arr[i] 就可以了。

class Solution {
    public int[] replaceElements(int[] arr) {
        int rightMax = arr[arr.length - 1];
        arr[arr.length - 1] = -1;
        for (int i = arr.length - 2; i >= 0; i--) {
            int t = arr[i];
            arr[i] = rightMax;
            if (t > rightMax)
                rightMax = t;
        }
        return arr;
    }
}

1313. 解压缩编码列表

给你一个以行程长度编码压缩的整数列表 nums 。

考虑每对相邻的两个元素 freq, val] = [nums[2i], nums[2i+1]] (其中 i >= 0 ),每一对都表示解压后子列表中有 freq 个值为 val 的元素,你需要从左到右连接所有子列表以生成解压后的列表。
请你返回解压后的列表。

示例:
输入:nums = [1,2,3,4]
输出:[2,4,4,4]
解释:第一对 [1,2] 代表着 2 的出现频次为 1,所以生成数组 [2]。
第二对 [3,4] 代表着 4 的出现频次为 3,所以生成数组 [4,4,4]。
最后将它们串联到一起 [2] + [4,4,4] = [2,4,4,4]。
示例 2:
输入:nums = [1,1,2,3]
输出:[1,3,3]

解题思路:
//难点在于声明返回数组的大小;
//为了得到返回数组的大小,首先遍历一遍数组nums,计算索引为偶数的数字之和得到返回数组的大小len;
//声明返回数组result的大小为len;
//再遍历一遍数组,进行解码,解码原则为—索引为奇数i的数字nums[i]重复nums[i-1]次,并依次放入返回数组result中;
//最后返回out;

class Solution {
    public int[] decompressRLElist(int[] nums) {
        int i=0;
        int len=0;
        while(i<nums.length){
        	len+= nums[i];
            i+=2;
        }
        int[] result = new int[len];
        int index=0;
        i=0;
        while(i<nums.length){
            for(int k=0;k<nums[i];k++){
                result[index]=nums[i+1];
                index++;
            }
            i+=2;
        }
        return result;
    }
}

1331. 数组序号转换

给你一个整数数组 arr ,请你将数组中的每个元素替换为它们排序后的序号。
序号代表了一个元素有多大。序号编号的规则如下:
序号从 1 开始编号。
一个元素越大,那么序号越大。如果两个元素相等,那么它们的序号相同。
每个数字的序号都应该尽可能地小。
示例 1:
输入:arr = [40,10,20,30]
输出:[4,1,2,3]
解释:40 是最大的元素。 10 是最小的元素。 20 是第二小的数字。 30 是第三小的数字。
示例 2:
输入:arr = [100,100,100]
输出:[1,1,1]
解释:所有元素有相同的序号。
示例 3:
输入:arr = [37,12,28,9,100,56,80,5,12]
输出:[5,3,4,2,8,6,7,1,3]

class Solution {
    public int[] arrayRankTransform(int[] arr) {
        // 1. 边界条件判断
        if (arr == null || arr.length == 0) {
            return arr;
        }
        // 2. 去重
        Set<Integer> set = new HashSet<>();
        for (int i : Arrays.copyOf(arr, arr.length)) {
            set.add(i);
        }
        int[] copy = new int[set.size()];
        int index = 0;
        for (int i : set) {
            copy[index++] = i;
        }

        // 3. 排序
        Arrays.sort(copy);
        // 4. 装入Map
        Map<Integer, Integer> map = new HashMap<>();
        for (int i = 0; i < copy.length; i++) {
            map.put(copy[i], i + 1);
        }

        //5. 得到结果
        for (int i = 0; i < arr.length; i++) {
            int value = arr[i];
            arr[i] = map.get(value);
        }
        return arr;
    }
}

1170. 比较字符串最小字母出现频次

我们来定义一个函数 f(s),其中传入参数 s 是一个非空字符串;该函数的功能是统计 s 中(按字典序比较)最小字母的出现频次。

例如,若 s = “dcce”,那么 f(s) = 2,因为最小的字母是 “c”,它出现了 2 次。

现在,给你两个字符串数组待查表 queries 和词汇表 words,请你返回一个整数数组 answer 作为答案,其中每个 answer[i] 是满足 f(queries[i]) < f(W) 的词的数目,W 是词汇表 words 中的词。

示例 1:
输入:queries = [“cbd”], words = [“zaaaz”]
输出:[1]
解释:查询 f(“cbd”) = 1,而 f(“zaaaz”) = 3 所以 f(“cbd”) < f(“zaaaz”)。
示例 2:
输入:queries = [“bbb”,“cc”], words = [“a”,“aa”,“aaa”,“aaaa”]
输出:[1,2]
解释:第一个查询 f(“bbb”) < f(“aaaa”),第二个查询 f(“aaa”) 和 f(“aaaa”) 都 > f(“cc”)。

class Solution {
    public int[] numSmallerByFrequency(String[] queries, String[] words) {
        int[] result = new int[queries.length];
        for (int i = 0; i < queries.length; i++) {
            int countMinQuery = countMinCode(queries[i]);
            for (String word : words) {
                if (countMinQuery < countMinCode(word)) {
                    result[i] ++;
                }
            }
        }
        return result;
    }
     /**
     * 统计最小出现频次
     * @param s
     * @return
     */
    public static int countMinCode(String s) {
        char min = s.charAt(0);
        int  count = 0;
        for (int i = 0; i < s.length(); i++) {
            if (s.charAt(i) < min) {
                min = s.charAt(i);
                count = 0;
            }
            if (s.charAt(i) == min) {
                count++;
            }
        }
        return count;
    }
}
  1. 公交站间的距离
    环形公交路线上有 n 个站,按次序从 0 到 n - 1 进行编号。我们已知每一对相邻公交站之间的距离,distance[i] 表示编号为 i 的车站和编号为 (i + 1) % n 的车站之间的距离。

环线上的公交车都可以按顺时针和逆时针的方向行驶。
返回乘客从出发点 start 到目的地 destination 之间的最短距离。

示例 1:
Leetcode 数组(简单题)[1000题之后]_第1张图片
输入:distance = [1,2,3,4], start = 0, destination = 1
输出:1
解释:公交站 0 和 1 之间的距离是 1 或 9,最小值是 1。
示例 2:
Leetcode 数组(简单题)[1000题之后]_第2张图片
输入:distance = [1,2,3,4], start = 0, destination = 2
输出:3
解释:公交站 0 和 2 之间的距离是 3 或 7,最小值是 3。
示例 3:

Leetcode 数组(简单题)[1000题之后]_第3张图片入:distance = [1,2,3,4], start = 0, destination = 3
输出:4
解释:公交站 0 和 3 之间的距离是 6 或 4,最小值是 4。

解题思路:
求两点之间的距离就只有两种方式,一种是从 start 到 destination,另一种是从 destination 到 start。
求两者的较小者即可。

class Solution {
    public int distanceBetweenBusStops(int[] distance, int start, int destination) {
        // 记录从 start 到 destination 的距离
        int len1 = 0;
        // 记录从 destination 到 start 的距离
        int len2 = 0;
        int cur1 = start;
        int cur2 = destination;
        while(cur1 != destination) {
            // 进行记录
            len1 += distance[cur1];
            // 更新起点
            cur1 = (cur1 + 1) % distance.length;
        }
        while(cur2 != start) {
            // 进行记录
            len2 += distance[cur2];
            // 更新起点
            cur2 = (cur2 + 1) % distance.length;
        }
        return Math.min(len1, len2);
    }
}

1185. 一周中的第几天

给你一个日期,请你设计一个算法来判断它是对应一周中的哪一天。
输入为三个整数:day、month 和 year,分别表示日、月、年。
您返回的结果必须是这几个值中的一个 {“Sunday”, “Monday”, “Tuesday”, “Wednesday”, “Thursday”, “Friday”, “Saturday”}。

示例 1:
输入:day = 31, month = 8, year = 2019
输出:“Saturday”
示例 2:
输入:day = 18, month = 7, year = 1999
输出:“Sunday”
示例 3:
输入:day = 15, month = 8, year = 1993
输出:“Sunday”

解题思路:
1、设置一个num变量计算总天数
2、计算71年到现在经过的所有年份的天数,闰年366.平年365,加到num中
3、计算1月到现在每个月的总天数,加到num中
4、num中加上day
5、将num转化为星期即可

class Solution {
    public String dayOfTheWeek(int day, int month, int year) {
         int num=0;
		     int days[]=new int[]{31,28,31,30,31,30,31,31,30,31,30,31};
		     //计算年份
		     for(int i=1971;i<year;i++) {
		     if(i%4==0&&i%100!=0||i%400==0)
		    	 num+=366;
		     else
		    	 num+=365;
		     }
		     
		     if(year%4==0&&year%100!=0||year%400==0)
		    	 days[1]=29;  	 
		     for(int i=1;i<month;i++)
		     {
		    	 num+=days[i-1];
		     }
		     num+=day;
		     String[] ans_day=new String[]{"Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"};
	         return ans_day[(num+4)%7];
    }
}

1200. 最小绝对差

给你个整数数组 arr,其中每个元素都 不相同。

请你找到所有具有最小绝对差的元素对,并且按升序的顺序返回。

示例 1:
输入:arr = [4,2,1,3]
输出:[[1,2],[2,3],[3,4]]
示例 2:
输入:arr = [1,3,6,10,15]
输出:[[1,3]]
示例 3:

输入:arr = [3,8,-10,23,19,-4,-14,27]
输出:[[-14,-10],[19,23],[23,27]]

class Solution {
    public List<List<Integer>> minimumAbsDifference(int[] arr) {
        ArrayList<List<Integer>> list=new ArrayList<>();
      int min=Integer.MAX_VALUE;
      Arrays.sort(arr);
      for(int i=0;i<arr.length-1;i++){
          if(Math.abs(arr[i]-arr[i+1])<min)
          min=Math.abs(arr[i]-arr[i+1]);
      }
      for (int i = 1; i < arr.length; i++) {
            if (Math.abs(arr[i] - arr[i - 1]) == min) {
                 ArrayList<Integer>list1=new ArrayList<>();
                list1.add(arr[i - 1]);
                list1.add(arr[i]);
                list.add(list1);
            }
      }
      System.out.println(min);
      return list;
    }
}

1217. 玩筹码

数轴上放置了一些筹码,每个筹码的位置存在数组 chips 当中。

你可以对 任何筹码 执行下面两种操作之一(不限操作次数,0 次也可以):

将第 i 个筹码向左或者右移动 2 个单位,代价为 0。
将第 i 个筹码向左或者右移动 1 个单位,代价为 1。
最开始的时候,同一位置上也可能放着两个或者更多的筹码。

返回将所有筹码移动到同一位置(任意位置)上所需要的最小代价。

示例 1:
输入:chips = [1,2,3]
输出:1
解释:第二个筹码移动到位置三的代价是 1,第一个筹码移动到位置三的代价是 0,总代价为 1。
示例 2:
输入:chips = [2,2,2,3,3]
输出:2
解释:第四和第五个筹码移动到位置二的代价都是 1,所以最小总代价为 2。

这道题其实就是统计奇数和偶数的个数,并且返回个数较少的。
因为移动2次代价为0,那么可以看成是
1)把全部的奇数移动一个位为偶数,此处不考虑怎么移动,
因为移动为偶数后全部移动到一个地方的步数肯定是偶数,那么不计算代价。
2)把全部的偶数移动一个位为奇数,此处不考虑怎么移动,
因为移动为奇数后全部移动到一个地方的步数肯定是偶数,那么不计算代价。
综上所述,代价就是把奇数/偶数移动为偶数/奇数的代价,
也就是奇数/偶数的个数,那么要想代价最小的话,就看奇数少还是偶数少。

class Solution {
    public int minCostToMoveChips(int[] chips) {
        int odd = 0,even = 0;
        for(int data : chips)
            if(data % 2 == 1)
                odd++;
            else
                even++;
        
        return Math.min(odd,even);
    }
}

1232. 缀点成线

在一个 XY 坐标系中有一些点,我们用数组 coordinates 来分别记录它们的坐标,其中 coordinates[i] = [x, y] 表示横坐标为 x、纵坐标为 y 的点。

请你来判断,这些点是否在该坐标系中属于同一条直线上,是则返回 true,否则请返回 false。

示例 1:
Leetcode 数组(简单题)[1000题之后]_第4张图片
输入:coordinates = [[1,2],[2,3],[3,4],[4,5],[5,6],[6,7]]
输出:true
示例 2:
Leetcode 数组(简单题)[1000题之后]_第5张图片
输入:coordinates = [[1,1],[2,2],[3,4],[4,5],[5,6],[7,7]]
输出:false

public boolean checkStraightLine(int[][] coordinates) {
       int k1 = coordinates[1][0] - coordinates[0][0];
       int k2 = coordinates[1][1] - coordinates[0][1];
       for(int i = 0; i < coordinates.length - 1; i++) {
           if((coordinates[i + 1][0] - coordinates[i][0]) * k2 != (coordinates[i + 1][1] - coordinates[i][1]) * k1) {
               return false;
           }
       }
       return true;
   }

1252. 奇数值单元格的数目

给你一个 n 行 m 列的矩阵,最开始的时候,每个单元格中的值都是 0。

另有一个索引数组 indices,indices[i] = [ri, ci] 中的 ri 和 ci 分别表示指定的行和列(从 0 开始编号)。
你需要将每对 [ri, ci] 指定的行和列上的所有单元格的值加 1。
请你在执行完所有 indices 指定的增量操作后,返回矩阵中 「奇数值单元格」 的数目。

示例 1:
Leetcode 数组(简单题)[1000题之后]_第6张图片
输入:n = 2, m = 3, indices = [[0,1],[1,1]]
输出:6
解释:最开始的矩阵是 [[0,0,0],[0,0,0]]。
第一次增量操作后得到 [[1,2,1],[0,1,0]]。
最后的矩阵是 [[1,3,1],[1,3,1]],里面有 6 个奇数。
示例 2:
Leetcode 数组(简单题)[1000题之后]_第7张图片
输入:n = 2, m = 2, indices = [[1,1],[0,0]]
输出:0
解释:最后的矩阵是 [[2,2],[2,2]],里面没有奇数。

直接记一下行数和列数出现的次数即可。

class Solution {
    public int oddCells(int n, int m, int[][] indices) {
        int[] row=new int[n];
        int[] col=new int[m];
        
        for(int i=0;i<indices.length;i++) {
        	row[indices[i][0]]++;
        	col[indices[i][1]]++;
        }
        
        int ans=0;
        for(int i=0;i<n;i++)
        	for(int j=0;j<m;j++) {
        		if((row[i]+col[j])%2>0)
        			ans++;
        	}
        return ans;
    }
}

1266. 访问所有点的最小时间

平面上有 n 个点,点的位置用整数坐标表示 points[i] = [xi, yi]。请你计算访问所有这些点需要的最小时间(以秒为单位)。
你可以按照下面的规则在平面上移动:
每一秒沿水平或者竖直方向移动一个单位长度,或者跨过对角线(可以看作在一秒内向水平和竖直方向各移动一个单位长度)。
必须按照数组中出现的顺序来访问这些点。

示例 1:
Leetcode 数组(简单题)[1000题之后]_第8张图片
输入:points = [[1,1],[3,4],[-1,0]]
输出:7
解释:一条最佳的访问路径是: [1,1] -> [2,2] -> [3,3] -> [3,4] -> [2,3] -> [1,2] -> [0,1] -> [-1,0]
从 [1,1] 到 [3,4] 需要 3 秒
从 [3,4] 到 [-1,0] 需要 4 秒
一共需要 7 秒
示例 2:
输入:points = [[3,2],[-2,2]]
输出:5

class Solution {

    public int minTimeToVisitAllPoints(int[][] points) {
        int min = 0;
        for (int i = 1; i < points.length; ++i)
            min += findMinStep(points[i - 1][0], points[i - 1][1], points[i][0], points[i][1]);
        return min;
    }

    private static int findMinStep(int x1, int y1, int x2, int y2) {
        int minSlope = Math.min(Math.abs(x1 - x2), Math.abs(y1 - y2));
        return Math.abs(x1 - x2) + Math.abs(y1 - y2) - minSlope;
    }
}

1.先计算移动到同一行或同一列所需最少步数: Math.min(Math.abs(x1 - x2), Math.abs(y1 - y2))
2.然后计算整体步数: 整体步数 = (x方向移动 - 最小步数) + (y方向移动 - 最小步数) + 最小步数, 化简完即为: Math.abs(x1 - x2) + Math.abs(y1 - y2) - minSlope

1275. 找出井字棋的获胜者

A 和 B 在一个 3 x 3 的网格上玩井字棋。

井字棋游戏的规则如下:
玩家轮流将棋子放在空方格 (" ") 上。
第一个玩家 A 总是用 “X” 作为棋子,而第二个玩家 B 总是用 “O” 作为棋子。
“X” 和 “O” 只能放在空方格中,而不能放在已经被占用的方格上。
只要有 3 个相同的(非空)棋子排成一条直线(行、列、对角线)时,游戏结束。
如果所有方块都放满棋子(不为空),游戏也会结束。
游戏结束后,棋子无法再进行任何移动。
给你一个数组 moves,其中每个元素是大小为 2 的另一个数组(元素分别对应网格的行和列),它按照 A 和 B 的行动顺序(先 A 后 B)记录了两人各自的棋子位置。

如果游戏存在获胜者(A 或 B),就返回该游戏的获胜者;如果游戏以平局结束,则返回 “Draw”;如果仍会有行动(游戏未结束),则返回 “Pending”。

你可以假设 moves 都 有效(遵循井字棋规则),网格最初是空的,A 将先行动。

示例 1:
输入:moves = [[0,0],[2,0],[1,1],[2,1],[2,2]]
输出:“A”
解释:“A” 获胜,他总是先走。
"X " "X " "X " "X " "X "
" " -> " " -> " X " -> " X " -> " X "
" " "O " "O " "OO " “OOX”
示例 2:
输入:moves = [[0,0],[1,1],[0,1],[0,2],[1,0],[2,0]]
输出:“B”
解释:“B” 获胜。
"X " "X " "XX " “XXO” “XXO” “XXO”
" " -> " O " -> " O " -> " O " -> "XO " -> "XO "
" " " " " " " " " " "O "
示例 3:
输入:moves = [[0,0],[1,1],[2,0],[1,0],[1,2],[2,1],[0,1],[0,2],[2,2]]
输出:“Draw”
输出:由于没有办法再行动,游戏以平局结束。
“XXO”
“OOX”
“XOX”
示例 4:
输入:moves = [[0,0],[1,1]]
输出:“Pending”
解释:游戏还没有结束。
"X "
" O "
" "

public String tictactoe(int[][] moves) {
	int m = moves.length;
	// 用数组记录0-2行、0-2列、正对角线、副对角线是否已满3个棋子
	// count[0-2]对应0-2行、count[3-5]对应0-2列、count[6]对应正对角线、count[7]对应副对角线
	int[] count = new int[8];
	// 思路第2步已解释为何只需考虑最后一个落棋的人
	// 倒序统计此人走棋情况
	for(int i = m - 1; i >= 0; i -= 2) {
		// 此棋对行的影响
		count[moves[i][0]]++;
		// 此棋对列的影响
		count[moves[i][1] + 3]++;	
		// 此棋对正对角线的影响
		if(moves[i][0] == moves[i][1])
			count[6]++;
		// 此棋对副对角线的影响 (
		// 此处为3x3的情况,其余大小的棋盘可以类推
		if(moves[i][0] + moves[i][1] == 2)
			count[7]++;
		// 满3个棋子则胜利
		if(count[moves[i][0]] == 3 || count[moves[i][1] + 3] == 3 || 
				count[6] == 3 || count[7] == 3) 
			// A先B后 则总长度为偶时 最后为B 反之为A
			return m % 2 == 0 ? "B" : "A";	
		}
	// 未胜时,棋盘未下满则继续
	if(moves.length < 9)
		return "Pending";
	// 未胜时,棋盘下满则平局结束
	return "Draw";
}

1295. 统计位数为偶数的数字

给你一个整数数组 nums,请你返回其中位数为 偶数 的数字的个数。

示例 1:
输入:nums = [12,345,2,6,7896]
输出:2
解释:
12 是 2 位数字(位数为偶数)
345 是 3 位数字(位数为奇数)
2 是 1 位数字(位数为奇数)
6 是 1 位数字 位数为奇数)
7896 是 4 位数字(位数为偶数)
因此只有 12 和 7896 是位数为偶数的数字
示例 2:
输入:nums = [555,901,482,1771]
输出:1
解释:
只有 1771 是位数为偶数的数字。

解题思路:
将int转为String,调用.length,然后%2==0即为偶数

class Solution {
    public int findNumbers(int[] nums) {
        int res=0;
        for(int i:nums){
            if(String.valueOf(i).length()%2==0){
                res++;
            }
        }

        return res;
    }
}

1304. 和为零的N个唯一整数

给你一个整数 n,请你返回 任意 一个由 n 个 各不相同 的整数组成的数组,并且这 n 个数相加和为 0 。

示例 1:
输入:n = 5
输出:[-7,-1,1,3,4]
解释:这些数组也是正确的 [-5,-1,1,2,3],[-3,-1,2,-2,4]。
示例 2:
输入:n = 3
输出:[-1,0,1]
示例 3:
输入:n = 1
输出:[0]
方法一:枚举

public int[] sumZero(int n) {
    int sum = 0;
    int[] arr = new int[n];
    int len = 0;
    for (int i = 0; i <= n - 2; i++) {
        arr[len++] = i;
        sum += i;
    }
    arr[len] = -sum;
    return arr;
}

方法二:双指针迭代

public int[] sumZero(int n) {
    int[] arr = new int[n];
    int l = 0, r = n-1;     //左边界与右边界
    int i = 1;
    while (l < r) {
        arr[l++] = i;
        arr[r--] = -i;
        i++;
    }
    return arr;
}

1337. 方阵中战斗力最弱的 K 行

给你一个大小为 m * n 的方阵 mat,方阵由若干军人和平民组成,分别用 0 和 1 表示。

请你返回方阵中战斗力最弱的 k 行的索引,按从最弱到最强排序。

如果第 i 行的军人数量少于第 j 行,或者两行军人数量相同但 i 小于 j,那么我们认为第 i 行的战斗力比第 j 行弱。

军人 总是 排在一行中的靠前位置,也就是说 1 总是出现在 0 之前。

示例 1:
输入:mat =
[[1,1,0,0,0],
[1,1,1,1,0],
[1,0,0,0,0],
[1,1,0,0,0],
[1,1,1,1,1]],
k = 3
输出:[2,0,3]
解释:
每行中的军人数目:
行 0 -> 2
行 1 -> 4
行 2 -> 1
行 3 -> 2
行 4 -> 5
从最弱到最强对这些行排序后得到 [2,0,3,1,4]
示例 2:
输入:mat =
[[1,0,0,0],
[1,1,1,1],
[1,0,0,0],
[1,0,0,0]],
k = 2
输出:[0,2]
解释:
每行中的军人数目:
行 0 -> 1
行 1 -> 4
行 2 -> 1
行 3 -> 1
从最弱到最强对这些行排序后得到 [0,2,3,1]

	public int[] kWeakestRows(int[][] mat, int k) {
        int[][] tmp = new int[mat.length][2];
        for(int i = 0; i < mat.length; i++){
            for(int j = 0; j < mat[0].length; j++){
                tmp[i][0] = i;
                if(mat[i][j] == 1)  tmp[i][1] += 1;
            }
        }
        Arrays.sort(tmp, (o1, o2) -> o1[1] - o2[1]);
        int[] res = new int[k];
        for(int i =0; i < k; i++){
            res[i] = tmp[i][0];
        }
        return res;
    }

1346. 检查整数及其两倍数是否存在

给你一个整数数组 arr,请你检查是否存在两个整数 N 和 M,满足 N 是 M 的两倍(即,N = 2 * M)。

更正式地,检查是否存在两个下标 i 和 j 满足:
i != j
0 <= i, j < arr.length
arr[i] == 2 * arr[j]

示例 1:
输入:arr = [10,2,5,3]
输出:true
解释:N = 10 是 M = 5 的两倍,即 10 = 2 * 5 。
示例 2:
输入:arr = [7,1,14,11]
输出:true
解释:N = 14 是 M = 7 的两倍,即 14 = 2 * 7 。
示例 3:
输入:arr = [3,1,7,11]
输出:false
解释:在该情况下不存在 N 和 M 满足 N = 2 * M 。

方法:HashMap
前提设定:
用HashMap map存放遍历arr时当前tempNum的<2倍值,tempNum>和<1/2倍值,tempNum>(若tempNum为偶数),注意,2tempNum、1/2tempNum均作为key值。

遍历开始:

  1. 查看map中是否存在key==tempNum的键值对,是:转2;否:转3;
  2. 返回true(表明已遍历的元素中存在tempNum1/2或tempNum2);
  3. 添加到map,若tempNum为偶数,再将添加到map;
  4. 遍历结束,程序仍未返回,说明不存在一个整数是另外一个整数的两倍,返回false;
    注:
  • 此解中map中的value无关紧要,也可存放当前遍历元素的索引值;
  • 进阶:若题目改为:返回所有构成2倍关系的数组元素的索引对,或元素对,即可直接将上述方法中map的value改为index,返回值改为;
class Solution {
    public boolean checkIfExist(int[] arr) {
        Map<Integer,Integer> map = new HashMap<>();
        for(int tempInt : arr){
            //Integer tempRst = map.get(tempInt);
            if(map.keySet().contains(tempInt)){
                return true;
            }else{
                map.put(tempInt*2,tempInt);
                if(tempInt%2 == 0){
                    map.put(tempInt/2,tempInt);
                }
            }
        }
        return false;
    }
}

1351. 统计有序矩阵中的负数

给你一个 m * n 的矩阵 grid,矩阵中的元素无论是按行还是按列,都以非递增顺序排列。
请你统计并返回 grid 中 负数 的数目。
示例 1:
输入:grid = [[4,3,2,-1],[3,2,1,-1],[1,1,-1,-2],[-1,-1,-2,-3]]
输出:8
解释:矩阵中共有 8 个负数。
示例 2:
输入:grid = [[3,2],[1,0]]
输出:0
示例 3:
输入:grid = [[1,-1],[-1,-1]]
输出:3
示例 4:
输入:grid = [[-1]]
输出:1

方法:非递增序列,遇到负数,该 行/列 后面就全是负数

class Solution {
    public int countNegatives(int[][] grid) {
        int cnt = 0;
        for(int i=0;i<grid.length;i++){

            //按列非递增
            if(grid[i][0]<0){
                cnt += (grid.length-i)*grid[0].length;
                break;
            }

            for(int j=0;j<grid[i].length;j++){

                //按行非递增
                if(grid[i][j]<0){
                    cnt += grid[i].length-j;
                    break;
                }
            }
        }
        return cnt; 
    }
}

1365. 有多少小于当前数字的数字

给你一个数组 nums,对于其中每个元素 nums[i],请你统计数组中比它小的所有数字的数目。

换而言之,对于每个 nums[i] 你必须计算出有效的 j 的数量,其中 j 满足 j != i 且 nums[j] < nums[i] 。

以数组形式返回答案。

示例 1:
输入:nums = [8,1,2,2,3]
输出:[4,0,1,1,3]
解释:
对于 nums[0]=8 存在四个比它小的数字:(1,2,2 和 3)。
对于 nums[1]=1 不存在比它小的数字。
对于 nums[2]=2 存在一个比它小的数字:(1)。
对于 nums[3]=2 存在一个比它小的数字:(1)。
对于 nums[4]=3 存在三个比它小的数字:(1,2 和 2)。
示例 2:
输入:nums = [6,5,4,8]
输出:[2,1,0,3]
示例 3:
输入:nums = [7,7,7,7]
输出:[0,0,0,0]

解题思路:
temp[]数组的作用是先将nums[]排序,最后temp[]返回结果
排序后每个元素的索引为小于等于这个元素的元素数量,我们需要单独处理相邻元素相等的情况
只需要将相邻相等元素的value设为相等即可

class Solution {
    public int[] smallerNumbersThanCurrent(int[] nums) {
        int n = nums.length;
        int[] temp = new int[n];
        temp = Arrays.copyOf(nums, n);
        Arrays.sort(temp);
        Map<Integer, Integer> map = new HashMap<>();
        
        for(int i = 0; i < n; i++){
            if(i == 0){
                map.put(temp[i],0);
            }else{
                if(temp[i] > temp[i-1]){
                    map.put(temp[i],i);
                }else{
                    map.put(temp[i],map.get(temp[i-1]));
                }
            }
        }
        for(int i = 0; i < n; i++){
            temp[i] = map.get(nums[i]);
        }
        return temp;
    }
}

你可能感兴趣的:(Leetcode)