LeetCode题号[200,299]刷题总结


201. 数字范围按位与

给定范围 [m, n],其中 0 <= m <= n <= 2147483647,返回此范围内所有数字的按位与(包含 m, n 两端点)。

思路

  • 找出m,n两个端点的共同公共前缀即可
public int rangeBitwiseAnd(int m, int n) {
    int ans = 0;
    int key = 1<<31;
    while(key != 0){
        if ((m & key) != 0 && (n & key) != 0){
            ans += key;
        } else if ((m & key) != 0 || (n & key) != 0){
            break;
        }
        key >>>= 1;
    }
    return ans;
}
  • 有种相对更简单的方法
  • 每次将n的最右端的1变为0,直到(n <= m)为止,此时与运算结果就是共同公共前缀
public int rangeBitwiseAnd(int m, int n) {
    while (m < n){
        n &= n-1;
    }
    return m & n;
}

207. 课程表

你这个学期必须选修 numCourse 门课程,记为 0 到 numCourse-1 。

在选修某些课程之前需要一些先修课程。 例如,想要学习课程 0 ,你需要先完成课程 1 ,我们用一个匹配来表示他们:[0,1]

给定课程总量以及它们的先决条件,请你判断是否可能完成所有课程的学习?

思路

  • 拓扑排序
class Solution {
    private Map<Integer, List<Integer>> map = new HashMap<>();
    private Queue<Integer> queue = new LinkedList<>();
    private int[] inDegree ;
    public boolean canFinish(int numCourses, int[][] prerequisites) {
        map.clear();
        queue.clear();
        inDegree = new int[numCourses];
        for (int i = 0;i < numCourses;i++){
            map.put(i,new ArrayList<>());
            inDegree[i] = 0;
        }
        for (int i = 0;i < prerequisites.length;i++){
            map.get(prerequisites[i][1]).add(prerequisites[i][0]);
            inDegree[prerequisites[i][0]]++;
        }
        for (int i = 0;i < numCourses;i++){
            if (inDegree[i] == 0){
                queue.offer(i);
            }
        }
        while (!queue.isEmpty()){
            List<Integer> list = map.get(queue.poll());
            for (Integer i : list){ 
                inDegree[i]--;
                if (inDegree[i] == 0){
                    queue.offer(i);
                }
            }
        }
        for (int i = 0;i < numCourses;i++){
            if (inDegree[i] != 0){
                return false;
            }
        }
        return true;
    }
}

209. 长度最小的子数组

给定一个含有 n 个正整数的数组和一个正整数 s ,找出该数组中满足其和 ≥ s 的长度最小的 连续 子数组,并返回其长度。如果不存在符合条件的子数组,返回 0。

思路

  • 二分+后缀和
  • 时间复杂度(O(nlogn))
  • 遍历整个数组,当前位置为最右端,二分后缀和
class Solution {
    public int binarySearch(int s,int left,int right,int sum[]){
        while (left + 1 < right){
            int mid = (left+right) >> 1;
            if (sum[mid] < s){
                right = mid;
            }
            else{
                left = mid;
            }
        }
        return sum[left] >= s ? left : -sum.length;
    }

    public int minSubArrayLen(int s, int[] nums) {
        if (nums.length == 0){
            return 0;
        }
        int ans = Integer.MAX_VALUE;
        int[] sum = new int[nums.length];
        sum[nums.length - 1] = nums[nums.length - 1];
        for (int i = nums.length - 2;i >= 0;i--){
            sum[i] = sum[i+1] + nums[i];
        }
        for (int i = 0;i < nums.length;i++){
            ans =  Integer.min(ans,i - 
                    binarySearch(s + (i < nums.length - 1 ? sum[i+1] : 0),0,i+1,sum) + 1);
        }
        return ans > sum.length ? 0 : ans ;
    }
}
  • 双指针
  • 涉及连续子数组这种的题目,一般都能用双指针解决,时间复杂度和空间复杂度通常也是最优的
  • 时间复杂度(O(n))
class Solution {
    public int minSubArrayLen(int s, int[] nums) {
        int left = 0,right = 0,sum = 0,ans = Integer.MAX_VALUE;
        while (left <= right && right < nums.length){
            sum += nums[right++];
            while (sum - nums[left] >= s && left < right){
                sum -= nums[left++];
            }
            if (sum >= s){
                ans = Integer.min(ans,right - left);
            }
        }
        return ans == Integer.MAX_VALUE ? 0 : ans;
    }
}

210. 课程表 II

现在你总共有 n 门课需要选,记为 0 到 n-1。

在选修某些课程之前需要一些先修课程。 例如,想要学习课程 0 ,你需要先完成课程 1 ,我们用一个匹配来表示他们: [0,1]

给定课程总量以及它们的先决条件,返回你为了学完所有课程所安排的学习顺序。

可能会有多个正确的顺序,你只要返回一种就可以了。如果不可能完成所有课程,返回一个空数组。

思路

  • 跟207题基本一致,等同于将其队列中的数值按照顺序放入数组即可
class Solution {
    private Map<Integer, List<Integer>> map = new HashMap<>();
    private Queue<Integer> queue = new LinkedList<>();
    private int[] inDegree ;
    public int[] findOrder(int numCourses, int[][] prerequisites) {
        map.clear();
        queue.clear();
        inDegree = new int[numCourses];
        int[] ans = new int[numCourses];
        int cnt = 0;
        for (int i = 0;i < numCourses;i++){
            map.put(i,new ArrayList<>());
            inDegree[i] = 0;
        }
        for (int i = 0;i < prerequisites.length;i++){
            map.get(prerequisites[i][1]).add(prerequisites[i][0]);
            inDegree[prerequisites[i][0]]++;
        }
        for (int i = 0;i < numCourses;i++){
            if (inDegree[i] == 0){
                queue.offer(i);
                ans[cnt++] = i;
            }
        }
        while (!queue.isEmpty()){
            List<Integer> list = map.get(queue.poll());
            for (Integer i : list){ 
                inDegree[i]--;
                if (inDegree[i] == 0){
                    queue.offer(i);
                    ans[cnt++] = i;
                }
            }
        }
        return cnt == numCourses ? ans : new int[0];
    }

}

214. 最短回文串(Hard)

给定一个字符串 s,你可以通过在字符串前面添加字符将其转换为回文串。找到并返回可以用这种方式转换的最短回文串。

思路

  • KMP
  • 由于要求只能在最前面添加字符,所以就先找出原始字符串的前面的最长回文串,再将后面的那段逆转后放到最前面即可
  • 例如,"aacecaaa"的前面最长回文串为"aacecaa",那么我们只需要将最后面的"a"逆转后反到最前面即可
  • 如果用暴力法,那么,时间复杂度为(O(n^{2}))
  • 但是,如果采用KMP,可以使得时间复杂度降为(O(n))
  • KMP就是求最长的相同前后缀,那么如何转换成KMP?
  • 构建一个新的字符串,即s + "#" + s.reverse()
  • 例如,"aacecaaa#aaacecaa"
  • 那么,他的最长公共前后缀就是"aacecaa"
class Solution {
    public String shortestPalindrome(String s) {
        String str = s + "#" + new StringBuilder(s).reverse();
        int[] next = new int[str.length()];
        int maxLength = 0;
        next[0] = 0;
        for (int i = 1;i < str.length();i++){
            while (maxLength > 0 && str.charAt(i) != str.charAt(maxLength) ){
                maxLength = next[maxLength - 1];
            }
            if (str.charAt(maxLength) == str.charAt(i)){
                maxLength++;
            }
            next[i] = maxLength;
        }
        return new StringBuilder(s.substring(next[str.length()-1])).reverse() + s;
    }
}

215. 数组中的第K个最大元素

在未排序的数组中找到第 k 个最大的元素。请注意,你需要找的是数组排序后的第 k 个最大的元素,而不是第 k 个不同的元素。

思路

  • 最小堆
  • PriorityQueue默认为最小堆
class Solution {
    public int findKthLargest(int[] nums, int k) {
        PriorityQueue<Integer> queue = new PriorityQueue<>();
        int ans = 0;
        for (int num : nums){
            if (queue.size() < k) {
                queue.add(num);
            } else if (queue.peek() < num){
                queue.poll();
                queue.add(num);
            }
        }
        return queue.peek();
    }
}
原文地址:https://www.cnblogs.com/MMMMMMMW/p/13289324.html