LeetCode 621, 581, 503, 442

230 阅读3分钟

LeetCode 621

链接:leetcode.com/problems/ta…

方法:找规律

时间复杂度:O(tasks.length) 想法:参考leetcode-cn.com/problems/ta… 第一种情况:当mx比较大的时候,我们可以想象,这个总的interval基本上是最高频次的这个字母决定的。因为题目让求最少的interval使得所有任务都被做完,那么反正最高频次的这个任务你反正是要做完,比方说是'K'出现最多次,那么每两个'K'之间拉开了一定空间,总共拉开了足够多的空间,以至于可以让其他的任务基本上都放完,除了频次跟'K'一样的。那在这种情况下,结果就是(mx - 1) * (n + 1) + 跟'K'频次一样的字母有多少个。 第二种情况:讲完上述情况之后我们很快就能想到它的反例,假设说'K'出现的次数不是很多,比方说3次,n也比较小,比方说1,然后不同的字母特别特别多。这样的话'K'拉开的空隙不足以让其他的字母基本上放完,上一种情况就不对了。在这种情况下,我们就不再用第一种情况那样的策略放任务了,比方说我们有A4B4C3D3E3F3G2,n=2,那么数组总长22。在这种情况下,我们每一轮尽可能多的放字母的种类的策略来放,放的顺序是ABCDEFGABCDEFGABCDEFAB。这个时候因为字母的种类足够多,因此每个相同的字母之前被拉开的就足够远,超过了冷却时间,因此不会有idle,直接用tasks.length的时间放完。 代码:

class Solution {
    public int leastInterval(char[] tasks, int n) {
        int[] cnt = new int[26];
        for (char c : tasks) {
            cnt[c - 'A']++;
        }
        
        Arrays.sort(cnt);
        int mx = cnt[25], i = 25;
        for (; i >= 0; i--) {
            if (cnt[i] != mx) {
                break;
            }
        }
        
        return Math.max(tasks.length, (mx - 1) * (n + 1) + 25 - i);
    }
}

LeetCode 581

链接:leetcode.com/problems/sh…

方法:贪心、或单调栈

时间复杂度:O(n) 想法:单调栈的思路很好想,就是比方说求这段区间的左端点,搞一个栈,然后是单调递增栈,如果往后扫描一直在增长,就一直往栈里面放东西,如果某个时刻出现了一个值比单调栈栈顶小,那就while循环把栈顶弹出来,但是实际上新的元素也不会再往里放了,就这样。扫完之后栈里面就剩下了左边我们应该留的元素。同理可以搞右边,然后就找出来了中间需要排序的一段。 当然这题最简单的方法是贪心。注意到这段区间的右端点,是如果一直从左往右扫的话,最后一个不是之前所有元素最大值的地方。左端点,是从右往左扫,最后一个不是之前所有元素最小值的地方。注意到这一点就直接扫一遍就完了。 代码:

class Solution {
    public int findUnsortedSubarray(int[] nums) {
        int i = 0, j = -1, max = Integer.MIN_VALUE, min = Integer.MAX_VALUE;

        for (int l = 0, r = nums.length-1; r >= 0; l++, r--){
            max = Math.max(max, nums[l]);
            if (nums[l] != max) j = l;

            min = Math.min(min, nums[r]);
            if (nums[r] != min) i = r;
        }

        return (j - i + 1);
    }
}

LeetCode 503

链接:leetcode.com/problems/ne…

方法:单调栈

时间复杂度:O(n) 想法:又是单调栈。我一开始想到的做法就是想象把这个数组复制一份,然后把这俩接起来。我先把右边这个复制出来的放到栈里面,然后我左边这个数组,就可以倒着扫描,无脑找栈里面下一个比它大的就完了。beat率比较低,但我觉得主要是比较直观。 代码:

class Solution {
    public int[] nextGreaterElements(int[] nums) {
        Stack<Integer> stack = new Stack<>();
        int n = nums.length;
        
        for (int i = n - 1; i >= 0; i--) {
            stack.push(i);
        }
        
        int[] res = new int[n];
        for (int i = n - 1; i >= 0; i--) {
            while (!stack.isEmpty() && nums[stack.peek()] <= nums[i]) {
                stack.pop();
            }
            res[i] = stack.isEmpty() ? -1 : nums[stack.peek()];
            stack.push(i);
        }
        
        return res;
    }
}

改进版:反过来想,假如做一个单调递减栈,然后从左到右扫描,每次遇到比栈顶大的元素时,栈顶元素右边一个比它自己大的元素就是现在遍历到的这个值。这样的话不需要一开始搞一堆入栈的东西。

class Solution {
    public int[] nextGreaterElements(int[] nums) {
        Stack<Integer> stack = new Stack<>();
        int n = nums.length;
        int[] res = new int[n];
        Arrays.fill(res, -1);
        
        for (int i = 0; i < 2 * n; i++) {
            int num = nums[i % n];
            while (!stack.isEmpty() && nums[stack.peek()] < num) {
                res[stack.peek()] = num;
                stack.pop();
            }
            if (i < n) {
                stack.push(i);
            }
        }
        
        return res;
    }
}

LeetCode 442

链接:leetcode.com/problems/fi…

方法:头脑风暴

时间复杂度:O(n) 想法:我觉着其实这个题的头脑风暴并不是空穴来风,LeetCode里面有好几道题是限制了数组长为n,数字在[1,n]范围,以后会写一道把这类问题转变成linked list cycle II 的想法。但不管怎么样,这类题就是说,拿到一个地方的值之后,这个值反正是在[1,n]直接,那你把这个值-1当下标再去访问是不会越界的。对于本题,实质上是在原数组上做操作,既然不让新开哈希表,那就用原数组记录东西。他说这数组里面有的值出现1次,有的出现两次,只有这两种可能。那么套用上面的想法,你既然能出现两次,那我如果拿着值当下标再去访问另一个地方的时候,这个地方就会被访问两次。题目说初始值所有值都是正整数,那么我们就一旦访问到那个地方,就把那儿的值置成相反数,那下一次再跑过来的时候你就知道之前来过这里了,那么引导你来到这个地方的那个值,就出现了两次。 代码:

class Solution {
    public List<Integer> findDuplicates(int[] nums) {
        List<Integer> res = new ArrayList<>();
        
        for (int num : nums) {
            num = Math.abs(num);
            int target = nums[num - 1];
            if (target < 0) {
                res.add(num);
            }
            else {
                nums[num - 1] = -target;
            }
        }
        
        return res;
    }
}