leetcode mt simple

Leetcode-MT-Simple

文章实际写于2021年,那个炎热的夏天。


  Leet Code 美团题库简单类总结,题目按照解法可大致分为数学法、计数法、位运算、双指针法、字符串、哈希表、栈、递归/迭代、排序法、匹配法、记忆化法、二分法、分治法、摩尔投票法、前缀和、模拟法、动态规划几大类。大部分题目可以有多种解法。

leetcode-mt-simple

数学法、计数法、位运算、双指针法、字符串、哈希表、栈、递归/迭代、排序法、匹配法、记忆化法、二分法、分治法、摩尔投票法、前缀和、模拟法、动态规划

  • 数学法: 即利用 +、-、*、/、% 等数学思想来解决,一般结合递归/迭代使用。
  • 计数法: 即通过统计元素个数来解决。如字符串中的字母个数,数组中的元素个数。
  • 位运算: 即通过 &、|、^、<<、>> 等位运算来解决。
  • 双指针法: 即通过两个指针来解决。两个指针可以同时从前往后,也可以从后往前,也可以一个双向奔赴,还有快慢指针法。
  • 字符串: 即利用字符串相关类的相关函数来解决。如 String 中的 substring()、replace()、split() 等方法;StringBuilder 的 reverse() 等方法。
  • 哈希表: 即利用哈希表的相关特性来解决。如 map key 的唯一性、set 的去重及其 contains() 方法等功能。
  • 栈: 即利用栈先进后出的特性来解决。
  • 递归/迭代: 递归/迭代可以配合大部分解法使用,在链表、树等数据结构中最常用。
  • 排序法: 即利用排序或排序的某些特性来解决。如归并排序中的合并功能,快排思想等。
  • 匹配法: 即字符串匹配。
  • 记忆化法: 记忆化搜索与动态规划类似。
  • 二分法: 即二分法。
  • 分治法: 即分治法,分而治之。
  • 摩尔投票法: 类似于计数法,一换一的思想。
  • 前缀和: 就是前缀和。
  • 模拟法: 那也就是模拟法。
  • 动态规划: 遇事不决,动态规划。

1、数学法

1.1、整数
  • 9、回文数
    回文数指一个整数,正序读和倒序读都是一样的整数。

    // 数学法、双指针法、哈希表
    // 数学法
    public static boolean isPalindrome(int n) {
        if (n < 0) {
            return false;
        }
        if (n < 10) {
            return true;
        }
        int temp = 0, m = n;
        while ( m != 0) {
            temp = temp * 10 + m % 10;
            m /= 10;
        }
        return temp == n;
    }
    
  • 202、快乐数
    对于一个整数,每一次都将其替换为其每个位置上的数字的平方和,直到变为 1,则说明其是快乐数,也有可能便不到 1,一直无限循环。

    // 数学法、双指针法、哈希表
    // 数学法 + 哈希表
    public static boolean isHappy(int n) {
        List<Integer> list = new ArrayList<>();
        while (true) {
            if (n == 1) {
                return true;
            }
            if (list.contains(n)) {
                return false;
            }
            list.add(n);
            int sum = 0;
            while (n > 0) {
                sum += Math.pow(n % 10, 2);
                n /= 10;
            }
            n = sum;
        }
    }
    
  • interview-16-05、阶乘尾数
    给定一个整数 n,求其 n 阶乘有多少个尾随零。

    // 数学法、递归/迭代
    // 数学法 + 迭代
    // 只有 2 和 5 相乘等于 10,也就是只有 2 和 5 的乘积结果会出现 0,0 ~ 10 以内 5 不可以作为任何树的因数,所以化解为求 n 由多少个 5 组成
    public static int trailingZeroes(int n) {
        int temp = 0;
        while (n > 0) {
            n /= 5;
            temp += n;
        }
        return temp;
    }
    
1.2、字符串
  • 415、字符串相加;二进制相加、链表相加
    给定两个字符串形式的数,求其和。

    // 数学法
    // 字符串相加、二进制相加、链表相加 都可以用此方法求解
    public static String add(String a, String b) {
        char[] m = a.toCharArray();
        char[] n = b.toCharArray();
        StringBuilder sb = new StringBuilder();
        int i = m.length - 1, j = n.length - 1, carry = 0;
        while (i >= 0 || j >= 0 || carry != 0) {
            carry += i >= 0 ? m[i--] - '0' : 0;
            carry += j >= 0 ? n[j--] - '0' : 0;
            sb.append(carry % 10);
            carry /= 10;
        }
        return sb.reverse().toString();
    }
    
  • 461、汉明距离
    汉明距离是值两个整数,其对应二进制的值的位不同的个数。

    // 数学法、动态规划
    // 数学法
    public static int hanmingDistance(int m, int n) {
        int length = 0;
        while (m != 0 || n != 0) {
            if (m % 2 != n % 2) {
                length++;
            }
            m /= 2;
            n /= 2;
        }
        return length;
    }
    
1.3、数组
  • Offer61、扑克牌顺子
    在若干副扑克牌中,随意抽取五张,判断其是不是顺子,即是不是连续。其中 A 用 1 表示,J、Q、K 分别用 11、12、13 表示,大小王用 0 表示,且可以代表任意数字。

    // 数学法、哈希表
    // 数学法 + 哈希表/集合
    public static boolean isStraight(int[] nums) {
        List<Integer> list = new ArrayList<>(5);
        int max = 0, min = 13;
        for (int item : nums) {
            if (item == 0) {
                continue;
            }
            if (list.contanins(item)) {
                return false;
            }
            list.add(item);
            max = Math.max(max, item);
            min = Math.min(min, item);
        }
        return max - min <= 4;
    }
    
  • Offer62、约瑟夫环
    圆圈中最后一个数字,给定 n 个数,0 ~ n - 1,从 0 开始,删除第 m 个数字,然后从下一个元素开始,再删除第 m 个数字,求最后剩下的那个数字。即约瑟夫环。

    // 数学法、递归/迭代
    // 数学法
    // f(n, m) = (f(n - 1, m) + m) % n 表示 再 n 个数字中,删除第 m 个数字最后剩下的数字
    public static boolean lastRemaining(int n, int m) {
        int temp = 0;
        for (int i = 2; i <= n; i++) {
            temp = (temp + m) % i;
        }
        return temp;
    }
    

2、计数法

2.1、字符串
  • 409、最长回文串
    给定一个由大小写字母构成的字符串 s,求由这些字母构成的最长回文串的长度。

    // 计数法、哈希表
    // 计数法
    public static int longestPalindrome(String s) {
        char[] c = s.toCharArray();
        int[] temp = new int[58];   // ASCII 码表中 A ~ z 的长度
        for (char item : c) {
            temp[item - 'A']++;
        }
        int length = 0;
        for (int item : temp) {
            if (item != 0) {
                length = (item / 2) * 2;
            }
        }
        return length < c.length ? length + 1 : length;
    }
    
2.2、数组
  • 645、错误的集合
    设定一个数据集 nums,其中包含了 1 ~ n 的元素,但由于复制错误导致其中一个元素重复,一个元素丢失,求重复的元素和丢失的元素。

    // 计数法、位运算、哈希表、排序法
    // 计数法
    public static int[] findErrorNums(int[] nums) {
        int[] temp = new int[nums.length + 1];
        int[] res = new int[2];
        for (int item : nums) {
            temp[item]++;
        }
        for (int item : temp) {
            if (item == 2) {
                res[0] = item;
            }
            if (item == 0) {
                res[1] = item;
            }
            if (res[0] > 0 && res[1] > 0) {
                break;
            }
        }
        return res;
    }
    
  • 997、小镇法官
    小镇有 n 个人,分别用 1 ~ n 表示,传言称小镇里有一个法官,法官不信任任何人,但其他人都信任这个法官,请找出他。trust[2, 4] 表示 2 信任 4。

    // 计数法、哈希表
    // 计数法
    public static int findJudge(int n, int[][] trust) {
        int[] temp = new int[n + 1];
        for (int[] item : trust) {
            temp[item[0]]--;
            temp[item[1]]++;
        }
        for (int i = 1; i < temp.length; i++) {
            if (temp[i] == n - 1) {
                return i;
            }
        }
        return -1;
    }
    
  • 1207、独一无二的出现次数
    给定一个整数数组 nums,请统计数组中元素的出现次数,若每个元素的出现次数都是独一无二的,则返回 true,否则返回 false。

    // 计数法、哈希表
    // 哈希表
    public static boolean uniqueOccurrences(int[] nums) {
        Map<Integer, Integer> map = new HashMap<>();
        for (int item : nums) {
            map.put(item, map.getOrDefault(item, 0) + 1);
        }
        return map.size() == new HashSet<>(map.values()).size();
    }
    
  • Offer03、数组中重复的数字
    给定一个长度为 n 的数组,数组元素为 0 ~ n - 1,数组中某些数字是重复的,请找出任意一个重复的元素。

    // 计数法、哈希表、排序法
    // 剑指 offer 原解
    // 1、若要求低时间,则用集合法(也就是字典法)
    // 2、若要求低空间,则用排序法(冒泡、选择、插入等空间复杂度为 O(1)的)或如下示例法
    // 3、若要求低空间且不能修改原数组,则使用二分法
    public static int findRepeatNumber(int[] nums) {
        int length = nums.length;
        for (int i = 0; i < length; i++) {
            int k = nums[i];
            if (k < 0) {
                k += length;
            }
            if (nums[k] < 0) {  
                return k;
            }
            nums[k] -= length;
        }
        return -1;
    }
    

3、位运算

  • 231、2 的幂
    给定一个整数,判断其是否为 2 的幂次方。

    // 数学法、位运算
    // 位运算 2^n & (2^n - 1) == 0
    public static boolean isPowerOfTwo(int n) {
        if (n <= 0) {
            return false;
        }
        return (n & n - 1) == 0;
    }
    
  • Offer65、不用 +、-、*、/ 做加法
    不使用 +、-、*、/ 实现加法功能。

    // 数学法、位运算
    // 位运算
    // a ^ b 异域:表示 a 和 b 的无进位求和,如 23 ^ 7 = 10;a & b 与:表示 a 和 b 的和进位,如 23 & 7 = 3
    public static int add(int a, int b) {
        int m = a ^ b, n = a & b;
        if (n == 0) {
            return m;
        }
        return add(m, n << 1);
    }
    

4、双指针法

4.1、字符串
  • 125、回文串
    给定一个字符串,验证其是否为回文串,只考虑字母数字,忽略大小写。

    // 双指针法、哈希表
    // 双指针法
    public static boolean isPalindrome(String s) {
        String temp = s.replaceAll("[A-Za-z0-9]", "");
        for (int i = 0, j = temp.length() - 1; i < j; i++, j--) {
            if (temp.charAt(i) != temp.charAt(j)) {
                return false;
            }
        }
        return true;
    }
    
  • 344、反转字符串
    给定一个字符串,将其中的字符串反转过来。

    // 双指针法
    public static void reverseString(char[] c) {
        for (int i = 0, j = c.length - 1; i < j; i++, j--) {
            char temp = c[i];
            c[i] = c[j];
            c[j] = temp;
        }
    }
    
  • 459、重复的子字符串
    给定一个字符串 s,判断其是否可以由其某个子字符串重复多次构成。

    // 双指针、匹配
    // 双指针
    public static boolean repeatedSubstringPattern(String s) {
        char[] c = s.toCharArry();
        for (int i = 1; i < c.length / 2; i++) {
            if (c.length % i != 0) {
                continue;
            }
            int m = 0, n = m + i;
            while (n < c.length) {
                if (c[m] != c[n]) {
                    break;
                }
                m++;
                n++;
            }
            if (n == c.length) {
                return true;
            }
        }
        return false;
    }
    
  • 1047、删除字符串中所有的相邻重复项
    给定一个字符串,删除其中所有相邻重复项。

    // 双指针法、栈
    // 双指针
    public static String removeDuplicates(String s) {
        char[] c = s.toCharArray();
        int index = -1;
        for (int i = 0; i < c.length; i++) {
            if (index != -1 && c[index] == c[i]) {
                index--;
            } else {
                c[++index] = c[i];
            }
        }
        return String.copyValueOf(c, 0, index + 1);
    }
    
4.2、数组
  • 26、移除有序数组中的重复项
    给定一个升序数组 nums,请原地移除重复出现的元素,且使元素的相对顺序保持一致。若最终结果为 k 个,则不考虑 k 及之口的元素。

    // 双指针法
    public static int removeDuplicates(int[] nums) {
        int i = 0;
        for (int j = 1; j < nums.length; ) {
            if (nums[i] == nums[j]) {
                j++;
            } else {
                i++;
                if (i != j) {
                    nums[i] = nums[j];
                }
                j++;
            }
        }
        return i + 1;
    }
    
  • 27、移除指定元素
    给定一个数组及一个值,原地移除该元素。

    // 双指针法
    public static int removeElement(int[] nums, int n) {
        int index = 0;
        for (int i = 0; i < nums.length; i++) {
            if (nums[i] != n) {
                nums[index++] = nums[i];
            }
        }
        return index;
    }
    
  • 283、移动数组元素零
    给定一个数组,将元素 0 移至末尾。

    // 双指针法
    public static void moveZeroes(int[] nums) {
        int index = 0;
        for (int i = 0; i < nums.length; i++) {
            if (nums[i] != 0) {
                if (index != i) {
                	nums[index] = nums[i];
                	nums[i] = 0;
                }
                index++;
            }
        }
    }
    
  • 350、数组交集
    给定两个数组,返回数组交集,若元素出现多次,则以出现次数较小的为准。

    // 双指针法、哈希表、排序法
    // 双指针法
    public static int[] intersect(int[] m, int[] n) {
        Arrays.sort(m);
        Arrays.sort(n);
        List<Integer> list = new ArrayList<>();
        for (int i = 0, j = 0; i < m.length && j < n.length; ) {
            if (m[i] < n[j]) {
                i++;
            } else if (m[i] > n[j]) {
                j++;
            } else {
                list.add(m[i]);
                i++;
                j++;
            }
        }
        int[] temp = new int[list.size()];
        int index = 0;
        for (int item : list) {
            temp[index++] = item;
        }
        return temp;
    }
    
  • 557、反转字符串中的单词
    给定一个字符串,反转字符串中每个单词的字母顺序。

    // 双指针
    public static String reverseWords(String s) {
        char[] c = s.toCharArray();
        int i, j;
        for (i = 0, j = 0; j < c.length(); ) {
            if (c[j] != ' ') {
                j++;
            } else {
                reverse(c, i, j);
                i = j + 1;
                j = i;
            }
        }
        reverse(c, i, j - 1);
        return new String(c);
    }
    
    public static void reverse(char[] c, int start, int end) {
        while (start < end) {
            char temp = c[start];
            c[start] = c[end];
            c[end] = temp;
            start++;
            end--;
        }
    }
    
  • 1470、重新排列数组
    重新排列数组x1x2x3…xn y1y2y3…yn => x1y2x2y2x3yy3…xnyn。

    // 双指针
    public static int[] shuffle(int[] nums, int n) {
        int[] temp = new int[n * 2];
        int index = 0;
        for (int i = 0, j = n; j < nums.length; i++, j++) {
            temp[index++] = nums[i];
            temp[index++] = nums[j];
        }
        return temp;
    }
    
  • Offer21调整数组元素使奇数在偶数前面
    嗯,就像题目那样。

    // 双指针
    public static int[] exchange(int[] nums) {
        for (int i = 0, j = nums.length - 1; i < j;) {
            if (nums[i] % 2 != 0) {
                i++;
            }
            if (nums[j] % 2 == 0) {
                j--;
            }
            if (i < j && nums[i] % 2 == 0 && nums[j] % 2 != 0) {
                int temp = nums[i];
                nums[i] = nums[j];
                nums[j] = temp;
                i++;
                j--;
            }
        }
        return nums;
    }
    
  • Offer57-II、和为 s 的连续正整数序列
    和为 s 的连续正整数序列。

    // 双指针
    public static int[][] findContinuousSequence(int target) {
        int[][] res = new int[0][];
        for (int i = 1, j = 2; j < target; ) {
            int sum = ((i + j) * ( j - i + 1)) / 2;
            if (sum == target) {
                int[] temp = new int[j - 1];
                int index = 0, start = i;
                while (start <= j) {
                    temp[index++] = start++;
                }
                res = Arrays.copyOf(res, res.length + 1);
                res[res.length - 1] = temp;
                i++;
            } else if (sum < target) {
                j++;
            } else {
                i++;
            }
        }
        return res;
    }
    
4.3、链表
  • 141、环形链表
    给定一个链表,判断其是否为环形链表。

    // 双指针、哈希表
    // 双指针(快慢指针)
    public static boolean hasCycle(Node node) {
        Node slow = node, fast = node;
        while (slow != null && fast != null && fast.next != null) {
            slow = slow.next;
            fast = fast.next.next;
            if (slow == fast) {
                return true;
            }
        }
        return false;
    }
    
  • 160、Offer52、相交链表
    给定两个链表,判断其是否相交,若相交则返回相交节点,若不想交则返回 null。

    // 双指针、哈希表
    // 双指针
    public static Node getIntersectionNode(Node node1, Node node2) {
        Node first = node1, second = node2;
        while (first != second) {
            first = first != null ? first.next : node2;
            second = second != null ? second.next : node1;
        }
        return first;
    }
    
  • Offer22、链表中倒数第 k 个节点
    给定一个链表,返回链表中倒数第 k 个节点。

    // 双指针
    public static Node getKthFromEnd(Node node, int k) {
        Node first = node, second = node;
        while (k > 0 && second != null) {
            second = second.next;
            k--;
        }
        while (second != null) {
            first = first.next;
            second = second.next;
        }
        return first;
    }
    

5、字符串

  • Offer58-I、反转字符串中的单词顺序
    给定一个字符串,反转字符串中的单词顺序。

    // 双指针、字符串
    // 字符串
    public static String reverseWords(String s) {
        if (s == null || s.length() == 0 || s.replaceAll(" ", "").length() == 0) {
            return "";
        }
        String[] temp = s.split(" ");
        StringBuilder sb = new StringBuilder();
        for (int i = temp.length - 1; i >= 0; i--) {
            if (!temp[i].equals("")) {
                sb.append(temp[i]).append(" ");
            }
        }
        return sb.substring(0, sb.length() - 1);
    }
    
  • 14、最长公共前缀
    给定一个字符串数组,求其最长公共前缀。

    // 字符串
    public static String longestCommonPrefix(String[] s) {
        String prefix = s[0];
        for (String item : s) {
            while (!item.startsWith(prefix)) {
                if (prefix.length() == 0) {
                    return "";
                }
                prefix = prefix.substring(0, prefix.length() - 1);
            }
        }
        return prefix;
    }
    
  • 1556、千位分隔数
    给定一个整数 n,返回其千位分隔后的字符串。

    // 字符串
    public static String thousandSeparator(int n) {
        StringBuilder sb = new StringBuilder(String.valueOf(n));
        if (sb.length() <= 3) {
            return sb.toString();
        }
        for (int i = sb.length() - 1 - 3; i >= 0; i -= 3) {
            sb.insert(i + 1, '.');
        }
        return sb.toString();
    }
    

6、哈希表

  • 771、宝石与石头
    给定两个字符串 j 和 s,j 中的每个字符代表了一种宝石,s 代表你拥有的石头,统计你拥有的石头中的宝石的数量。

    // 哈希表
    public static int numJewelsInStones(String j, String s) {
        Set<Character> set = new HashSet<>(j.length());
        for (char c : j.toCharArray()) {
            set.add(c);
        }
        int num = 0;
        for (char c: s.toCharrray()) {
            if (set.contains(c)) {
                num++;
            }
        }
        return num;
    }
    
  • 859、亲密字符串
    给定两个字符串 s 和 g,如果只交换 s 中两个字符的位置久能得到与 g 相等的结果,那就认为 s 和 g 是亲密字符串。

    // 哈希表
    public static boolean buddyStrings(String s, String g) {
        if (s.length() != g.length()) {
            return false;
        }
        if (s.equals(g)) {
            int[] temp = new int[26];  // s g 只包含小写字母
            for (char c : s.toCharAray()) {
                temp[c - 'a']++;
                if (temp[c - 'a'] > 1) {
                    return true;
                }
            }
            return false;
        } else {
            int first = -1, second = -1;
            for (int i = 0; i < s.length(); i++) {
                if (s.charAt(i) != g.charAt(i)) {
                    if (first == -1) {
                        first = i;
                    } else if (second == -1) {
                        second = i;
                    } else {
                        return false;
                    }
                }
            }
            return first != -1 && second != -1 && s.charAt(second) == g.charAt(first) && s.charAt(first) == g.charAt(second);
        }
    }
    
  • 1、两数之和
    给定一个整数数组和一个目标整数,请返回数组中和为目标整数的元素下标。

    // 哈希表
    public static int[] twoSum(int[] nums, int target) {
        Map<Integer, Integer> map = new HashMap<>(nums.length());
        int[] res = new int[2];
        for (int i = 0; i < nums.length; i++) {
            if (map.containsKey(nums[i])) {
                res[0] = map.get(nums[i]);
                res[1] = i;
            } else {
                map.put(target - nums[i], i);
            }
        }
        return res;
    }
    

7、栈

7.1、链表
  • Offer06、从尾到头打印链表
    给定一个链表,从尾到头打印链表。

    // 双指针、栈、递归/迭代
    // 栈
    public static int[] reversePrint(Node node) {
      Stack<Integer> stack = new Stack<>();
        while (node != null) {
            stack.push(node.value);
            node = node.next;
        }
        int[] res = new int[stack.size()];
        int index = 0;
        while (!stack.isEmpty()) {
            res[index++] = stack.pop();
        }
        return res;
    }
    
7.2、字符串
  • 20、有效的括号
    给定一个只包含 ‘(’、‘)’、‘[’、‘]’、‘{’、‘}’ 字符的字符串,判断其是否有效。有效定义为,左括号必须在对应位置上有正确的右括号。

    // 栈
    public static boolean isValid(String s) {
        Stack<Character> stack = new Stack<>();
        for (char c : s.toCharArray()) {
            if (c == '(') {
                stack.push(')');
            } else if (c == '[') {
                stack.push(']');
            } else if (c == '{') {
                stack.push('}');
            } else if (stack.isEmpty() || c!= stack.pop()) {
                return false;
            }
        }
        return stack.isEmpty();
    }
    
7.3、栈
  • 716、最大栈;Offer30、最小栈
    设计一个最大栈数据结构,既支持栈操作,又能查找栈中最大元素。

    // 双栈
    // 一个栈用来提供栈操作,另一个栈用来提供最大元素操作,利用栈的先进后出的特性
    
  • 232、Offer09、实现队列
    用双栈实现队列。

    // 双栈
    // 一个栈用来入列,一个栈用来出列。
    

8、递归/迭代

8.1、链表
  • 83、删除排序链表中的重复元素
    给定一个排序链表,删除其中重复的元素。

    // 栈、递归/迭代
    // 递归/迭代
    public static Node deleteDuplicates(Node node) {
        Node last = node;
        Node temp = last.next;
        while (last != null && temp != null) {
            if (last.value != temp.value) {
                last.next = temp;
                last = last.next;
            }
            temp = temp.next;
        }
        last.next = null;
        return node;
    }
    
  • 206、Offer24、反转链表
    给定一个链表,反转链表,返回反转后的头节点。

    // 栈、递归/迭代
    // 迭代
    public static Node reverseList(Node node) {
        Node prev = null, curr = node, temp;
        while (curr != null) {
            temp = curr.next;
            curr.next = prev;
            prev = curr;
            curr = temp;
        }
        return prev;
    }
    
  • 237、Offer18、删除链表节点
    给定一个链表及一个目标值,删除链表中节点值等于目标值的一个节点。

    // 栈、递归/迭代
    // 迭代
    public static Node deleteNode(Node node, int value) {
        if (node == null) {
            return null;
        }
        Node root;
        if (node.value == value) {
            root = node.next;
        } else {
            root = node;
        }
        while (node.next != null) {
            if (node.next.value == value) {
                node.next = node.next.next;
                break;
            }
            node = node.next;
        }
        return root;
    }
    
8.2、二叉树
  • 144、前;94、中;145、后;深;广遍历

    // 前中后深广遍历就不用多说了吧
    
  • 108、有序数组转化为二叉搜索树
    给定一个有序数组,将其转化为二叉搜索树。

    // 递归/迭代、分治
    // 递归
    public static Tree sortedArrayToBST(int[] nums) {
        return nums == null ? null : build(nums, 0, nums.length - 1);
    }
    
    public static Tree build(int[] ums, int left, int right) {
        if (left > right) {
            return null;
        }
        int mid = left + (right - left) / 2;
        Tree tree = new Tree(nums[mid]);
        tree.left = build(nums, left, mid - 1);
        tree.right = build(nums, mid + 1, right);
        return tree;
    }
    
  • 104、二叉树的最大;111、最小深度;112、路径总和
    给定一个二叉树,求其最大深度。

    // 递归/迭代、深、广
    public static int maxDepth(Tree tree) {
        return tree == null ? 0 : Math.max(tree.left, tree.right) + 1;
    }
    
    // 最小深度
    public static int minDepth(Tree tree) {
        if (tree == null) {
            return 0;
        }
        if (tree.left != null && tree.right == null) {
            return minDepth(tree.left) + 1;
        }
        if (tree.left == null && tree.right != null) {
            return minDepth(tree.right) + 1;
        }
        return Math.min(minDepth(tree.left), minDepth(tree.right)) + 1;
    }
    
    // 路径总和
    // 给定一个二叉树根节点和一个目标值,判断二叉树的每个叶子节点的总和中是否有和目标值相等的。
    public static boolean hasPathSum(Tree tree, int target) {
        if (tree == null) {
            return false;
        }
        if (tree.left == null && tree.right == null) {
            return targte - tree.value == 0;
        }
        return hasPathSum(tree.left, target - tree.value) || hasPathSum(tree.right, target - tree.value);
    }
    
  • 101、Offer28、对称二叉树
    给定一个二叉树,判断其是否为对称二叉树。

    // 递归
    public static boolean isSymmetric(Tree tree) {
        return tree == null ? true : check(tree.left, tree.right);
    }
    
    public static boolean check(Tree left, Tree right) {
        if (left == null || right == null) {
            return false;
        }
        return left.value == right.value && check(left.left, right.right) && check(left.right, right.left);
    }
    
  • 226、翻转二叉树
    给定一颗二叉树,翻转其左右节点。

    // 前、中、后、深、广、笨比
    // 前
    public static Tree before(Tree tree) {
        if (tree == null) {
            return null;
        }
        Tree temp = tree.left;
        tree.left = brfore(tree.right);
        tree.right = before(temp);
        return tree;
    }
    
    // 中
    public static Tree middle(Tree tree) {
        if (tree == null) {
            return null;
        }
        middle(tree.left);
        Tree temp = tree.left;
        tree.left = tree.right;
        tree.right = temp;
        middle(tree.left);
        return tree;
    }
    
    // 后
    public static Tree after(Tree tree) {
        if (tree =null) {
            return null;
        }
        Tree left = after(tree.left);
        Tree right = after(tree.right);
        tree.left = right;
        tree.right = left;
        return tree;
    }
    
    // 广
    public static Tree bfs(Tree tree) {
            if (tree == null) {
                return null;
            }
            Queue<Tree> queue = new LinkedList<>();
            queue.add(tree);
            while (!queue.isEmpty()) {
                Tree tree1 = tree.poll();
                Tree temp = tree1.left;
                tree1.left = tree1.right;
                tree1.right = temp;
                if (tree1.left != null) {
                    queue.add(tree1.left);
                }
                if (tree1.right != null) {
                    queue.add(tree1.right);
                }
            }
            return tree;
        }
    
  • 617、合并二叉树
    给定两个二叉树,合并二叉树。

    // 递归
    public static Tree mergeTrees(Tree tree1, Tree tree2) {
        if (tree1 == null) {
            return tree2;
        }
        if (tree2 == null) {
            return tree1;
        }
        tree1.value += tree2.value;
        tree1.left = mergeTrees(tree1.left, tree2.left);
        tree1.right = mergeTrees(tree1.right, tree2.right);
        return tree1;
    }
    
  • 257、二叉树的所有路径(深)
    给定一颗二叉树,返回其所有路径。

    // 深
    public static List<String> binaryTreePaths(Tree tree) {
    	List<String> list = new ArrayList<>();
        StringBuildr sb = new StringBuilder();
        path(tree, list, sb);
        return list;
    }
    
    public static void path(Tree tree, List<String> list, StringBuilder sb) {
        if (tree == null) {
            return;
        }
        sb.append(tree.value).append('->');
        if (tree.left != null) {
            path(tree.left, list, new StringBuilder(sb));
        }
        if (tree.right != null) {
            path(tree.right, list, new StringBuilder(sb));
        }
        if (tree.left == null && tree.right == null) {
            list.add(sb.substring(0, sb.length() - 2));
            return;
        }
    }
    
  • 543、二叉树的直径(深)
    给定一颗二叉树,求其直径。二叉树直径为任意两个叶节点之间的路径长度。

    // 深
    int max = 0;
    public static int diameterOfBinaryTree(Tree tree) {
        depth(tree);
        return max;
    }
    
    public static int depth(Tree tree) {
        if (tree == null) {
            return 0;
        }
        int leftDepth = tree.left != null ? depth(tree.left) + 1 : 0;
        int rightDepth = tree.right != null ? depth(tree.right) + 1 : 0;
        max = Math.max(max, leftDepth + rightDepth);
        return Math.max(leftDepth, rightDepth);
    }
    
  • 572、另一棵树的子树(深)
    给定两颗二叉树,判断第二颗是否为第一颗的子树。

    // 深
    public static boolean isSubtree(Tree, tree1, Tree tree2) {
        if (tree1 == null) {
            return false;
        }
        return isSame(tree1, tree2) || isSame(tree1.left, tree2) || isSame(tree1.right, tree2);
    }
    
    public static boolean isSame(Tree tree1, Tree tree2) {
        if (tree1 == null && tree2 == null) {
            return treu;
        }
        if (tree1 == null || tree2 == null) {
            return false;
        }
        return tree1.value == tree2.value && isSame(tree1.left, tree2.left) && isSame(tree1.right, tree2.right);
    }
    
  • Offer55-II、平衡二叉树(深)
    给定一颗二叉树,若其任意节点的左右子树的深度差不超过 1,则称为平衡二叉树。

    // 深(自顶向下递归)
    public static boolean isBalanced(Tree tree) {
        if (tree == null) {
            return treu;
        }
        if (Math.abs(depth(tree.left) - depth(tree.right)) > 1) {
            return false;
        }
        return isBalanced(tree.left) && isBalanced(tree.right);
        
        // 自底向上递归
        return depth1(tree) >= 0;
    }
    
    // 自底向上递归
    public static int depth1(Tree tree) {
        if (tree == null) {
            return 0;
        }
        int leftDepth = depth1(tree.left);
        int rightDepth = depth1(tree.right);
        if (leftDepth == -1 || rightDepth == -1 || Math.abs(leftDepth - rightDepth) > 1) {
            return -1;
        }
        return Math.max(leftDepth, rightDepth) + 1;
    }
    
    public static int depth(Tree) {
        return tree == null ? 0 : Math.max(tree.left, tree.right) + 1;
    }
    
  • 637、二叉树的层平均值(广)
    给定一颗二叉树,返回其层平均值。

    // 广
    // 广度优先遍历 求每一层平均值 代码过于简单 不予展示
    
  • Offer54、二叉搜索树的第 k 大节点(反中序)
    给定一颗二叉搜索树,返回其第 k 大节点。

    // 反中序
    int count = 0, value = 0;
    public static int k(Tree tree, int k) {
        if (tree == null) {
            return -1;
        }
        middle(tree, k);
        return value;
    }
    
    public static void middle(Tree tree, int k) {
        if (tree.right != null) {
            middle(tree.right, k);
        }
        if (++count == k) {
            value = tree.value;
            break;
        }
        if (tree.left != null) {
            middle(tree.left, k);
        }
    }
    
  • 559、N 叉树的最大深度(深、广)
    给定一颗 n 叉树,返回其最大深度。

    // 深/广
    // 深
    public static int dfs(Tree tree) {
        if (tree == null) {
            return 0;
        }
        int depth = 0;
        for (Tree item : tree.children) {
            depth = Math.max(depth, dfs(item));
        }
        return depth + 1;
    }
    
    // 广
    public static int bfs(Tree tree) {
    	if (tree == null) {
            return 0;
        }
        if (tree.children.size == 0) {
            return 1;
        }
        int depth = 0;
        Queue<Tree> queue = new LinkedList<>();
        queue.add(tree);
        while (!queue.isEmpty()) {
            depth++;
            int size = queue.size();
            while (size > 0) {
                Tree temp = queue.poll();
                if (temp.chiildren,.size > 0) {
                    queue.addAll(temp.children);
                }
                size--;
            }
        }
        return depth;
    }
    

9、排序法

9.1、数组
  • 88、合并有序数组
    给定 两个升序数组,合并数组,并使元素保持升序顺序。

    // 双指针法、排序法
    // 排序法(归并合并)
    public static int[] merge(int[] m, int[] n) {
        int[] res = new int[m.length + n.length];
      int i = 0, j = 0, index = 0;
        while (i < m.length && j < n.length) {
          res[index++] = m[i] < n[j] ? m[i++] : n[j++];
        }
        if (i < m.length) {
            res[index++] = m[i++];
        }
        if (j < n.length) {
            res[index++] = n[j++];
        }
        return res;
    }
    
  • Offer40、最小的 k 个数(快排)
    给定一个整数数组和一个整数 k,k 小于数组的长度,返回数组中最小的 k 个数。

    // 排序法(快排思想)
    public static int[] getLeastNumbers(int[] nums, int k) {
        if (nums.length == 0) {
            return new int[0];
        }
        return min(nums, 0, nums..length - 1, k);
    }
    
    public static int[] min(int[] nums, int left, int right, int k) {
        int index = quick(nums, left, right);
        if (index + 1 == k) {
            return Arrays.copyOf(nums, 0, k);
        }
        return index + 1 < k ? min(nums, index + 1, right, k) : min(nums, left, index - 1, k);
    }
    
    public static int quick(int[] nums, int left, int right) {
        int key = nums[left];
        int i = left, j = right;
        while (i < j) {
            while (i < j && nums[j] >= key) {
                j--;
            }
            while (i < j && nums[i] <= key) {
                i++;
            }
            if (i < j) {
                int temp = nums[i];
                nums[i] = nums[j];
                nums[j] = temp;
            }
        }
        nums[left] = nums[i];
        nums[i] = key;
        return i;
    }
    
9.2、链表
  • 21、Offer25、合并有序链表
    合并两个有序链表。

    // 排序法(归并合并思想)
    public static Node mergeTwoLists(Node node1, Node node2) {
        if (node1 == null) {
            return node2;
        }
        if (node2 == null) {
            return node1;
        }
        Node root = new Node(-1);
        Node node = root;
        while (node1 != null && node2 != null) {
            if (node1.value <= node2.value) {
                node.next = node1;
                node1 = nnode1.next;
            } else {
                node.next = node2;
                node2 = node2.next;
            }
            node = node.next;
        }
        if (node1 != null) {
            node.next = node1;
        }
        if (node2 != null) {
            node.next = node2;
        }
        return root.next;
    }
    

10、匹配法

  • 28、strStr()
    实现 strStr() 方法。

    // 字符串匹配 直接参考五大匹配算法(BF、RK、KMP、BM、Sunday)
    

11、记忆化法

// 记忆化法皆可用动态规划解决

12、二分法

  • 35、搜索插入位置
    给定一个有序数组和一个整数,返回该整数在数组中的索引,若不存在于数组,则返回其插入位置。

    // 二分法
    public static int searchInsert(int[] nums, int key) {
        if (key < nums[0]) {
            return 0;
        } else if (key > nums[nums.length - 1]) {
            return nums.length;
        } ele {
            return binary(nums, 0, nums.length - 1, key);
        }
    }
    
    public static int binary(int[] nums, int left, int right, int key) {
        if (left == right) {
            if (key <= nums[left]) {
                return left;
            } else {
                return left + 1;
            }
        } else if (left < right) {
            int mid = left + (right - left) / 2;
            if (key == nums[mid]) {
                return mid;
            } else if (key < nums[mid]) {
                return binary(nums, left, mid - 1, key);
            } else {
                return bianry(nums, mid + 1, right, key);
            }
        } else {
            return left;
        }
    }
    
  • 69、x 的平方根
    给定一个整数 x,在不使用任何库函数的情况下,返回 x 的算数平方根。

    // 二分法
    public static int mySqrt(int x) {
        int left = 0, right = x, res = -1;
        while (left <= right) {
            int mid = left + (right - left) / 2;
            if ((long) mid * mid <= x) {
                res = mid;
                left = mid + 1;
            } else {
                right = mid - 1;
            }
        }
        return res;
    }
    
  • 704、二分查找
    二分查找。

    // 二分查找 代码见七大查找算法之二分查找
    
  • 852、山脉数组的峰顶索引
    给定一个山脉数组,返回其峰顶索引值,即返回最大值索引。

    public static int peakIndexInMountainArray(int[] nums) {
        int left = 0, right = arr.length - 1;
        while (left < right) {
            int mid = left + (right - left) / 2;
            if (nums[mid] > nums[mid - 1] && nums[mid] > nums[mid + 1]) {
                return mid;
            } else if (nums[mid] < nums[mid - 1]) {
                right = mid - 1;
            } else {
                left = mid + 1;
            }
        }
        return - 1;
    }
    

13、分治法

// 皆可用其它方法解决

14、摩尔投票法

  • 169、Offer39、多数元素
    给定一个数组,求出现次数超过一半元素。

    // 计数法、哈希表、分治法、摩尔投票法
    // 摩尔投票法
    public static int majorityElement(int[] nums) {
        int count = 1, curr = nums[0];
        for (int i = 1; i < nums.length; i++) {
            if (nums[i] == curr) {
                count++;
            } else {
                count--;
                if (count == 0) {
                    if (i< nums.length - 1) {
                        curr = nums[i + 1];
                    } else {
                        return -1;
                    }
                }
            }
        }
        return count;
    }
    

15、前缀和

  • 303、区域检索-数组不可变
    利用前缀和来作为结果。

    // 代码被扣了
    

16、模拟法

  • 867、转置矩阵
    转置矩阵,即将矩阵的行换成列,列换成行。

    // 模拟法
    public static int[][] transpose(int[][] nums) {
        if (nums.length == 0) {
            return null;
        }
        int[][] res = new int[nums[0].length][nums.length];
        for (int i = 0; i < nums.length; i++) {
            for (int j = 0; j < nums[i].length; j++) {
                res[j][i] = nums[i][j];
            }
        }
        return res;
    }
    
  • Offer29、顺时针打印矩阵
    给定一个矩阵,顺时针打印矩阵内元素。

    // 模拟法
    public static int[] spiralOrder(int[][] nums) {
        if (nums.length == 0) {
            return new int[] {};
        }
        int high = nums.length, width = nums[0].length;
        int[] res = new int[high * width];
        int right = width - 1, down = high - 1, left = 0, up = 0;
        int index = 0, i = 0, j = 0;
    
        while (left <= right && up <= down) {
            // 对应存在以最中心元素中心对称的情况
            if ((right == left) && (down == up) && (right == down)) {
                res[index++] = nums[i][j];
                break;
            }
            while (j < right) {
                res[index++] = nums[i][j++];
            }
            right--;
            while (i < down) {
                res[index++] = nums[i++][j];
            }
            down--;
            while (j > left && index < res.length) {
                res[index++] = nums[i][j--];
            }
            left++;
            while (i > up && index < res.length) {
                res[index++] = nums[i--][j];
            }
            up++;
            i++;
            j++;
        }
        return res;
    }
    

17、动态规划

17.1、整数
  • 118、杨辉三角
    杨辉三角,在杨辉三角中,每个数字的值等于它左上方和右上方的和。给定一个整数 n,返回 n 行杨辉三角。

    // 动态规划
    public static int[][] generate(int n) {
        int[][] dp = new int[n][0];
        for (int i = 0; i < n; i++) {
            int[] temp = new int[i + 1];
            dp[i] = temp;
            for (int j = 0; j <= i; j++) {
                if (i == 0 || j == 0) {
                    dp[i][j] = 1;
                } else {
                    if (j <= i / 2) {
                        dp[i][j] = dp[i - 1][j - 1] + dp[i - 1][j];
                    } else {
                        dp[i][j] = dp[i][i - j];
                    }
                }
            }
        }
        return dp;
    }
    
  • 509、Offer10I、斐波那契数列
    斐波那契数列 f(n) = f(n - 2) + f(n + 1)。

    // 递归/迭代、动态规划
    // 动态规划
    public static int fibonacci(int n) {
        if (n < 2) {
            return n;
        }
        int prePre = 0, pre = 1, fn = 0;
        for (int i = 1; i < n; i++) {
            fn = (prePre + pre) % 1000000007;
            prePre = pre;
            pre = fn;
        }
        return fn;
    }
    
  • 70、爬楼梯;Offer10II、跳台阶
    小丸子爬楼梯与跳台阶,可以一次爬一个台阶,也可以一次爬两个台阶,求对于有 n 个台阶的楼梯,小丸子有多少中爬楼梯方案。

    // 动态规划
    public static int climbStairs(int n) {
        // 若是跳台阶 小丸子会原地蹦一下
        // if (n == 0) {
        //     return 1;
        // }
        
        if (n <= 2) {
            return n;
        }
        int a = 1, b = 2, temp = 0;
        for (int i = 3; i <= n; i++) {
            temp = (a + b) % 1000000007;
            a = b;
            b = temp;
        }
        return temp;
    }
    
  • interview-08-01、三步问题
    小丸子上楼梯,小丸子长大了,可以一次上一个,也可以一次上两个,也可以一次上三个。对于有 n 个台阶的楼梯,小丸子有多少中爬楼梯方案。

    // 动态规划
    public static int waysToStep(int n) {
        if (n < 3) {
            return n;
        }
        if (n == 3) {
            return 4;
        }
        int mod = 1000000007;
        int[] dp = new int[n + 1];
        dp[1] = 1;
        dp[2] = 2;
        dp[3] = 4;
        for (int i = 4; i <= n; i++) {
            dp[i] = (dp[i - 3] + dp[i - 2]) % mod + dp[i - 1];
            dp[i] %= mod;
        }
        return dp[n];
    }
    
17.2、数组
  • 53、Offer42、最大连续子段和
    给定一个数组,求其最大连续子段和。

    // 动态规划
    public static int maxSubArray(int[] nums) {
        int max = nums[0];
        for (int i = 1; i < nums.length; i++) {
            nums[i] += Math.max(nums[i], 0);
            max = Math.max(max, nums[i]);
        }
        return max;
    }
    
  • 121、买卖股票问题
    给定一个数组,nums[i] 表示第 i 天股票的价格。求股票买卖的最大利润。

    // 动态规划
    public static int maxProfit(int[] nums) {
        int max = 0, minPrice = nums[0];
        for (int i = 1; i < nums.length; i++) {
            max = Math.max(max, nums[i] - minPrice);
            minPrice = Math.min(minPrice, nums[i]);
        }
        return max;
    }
    
  • 392、判断子序列
    给定两个字符串 s 和 t,判断 s 是否为 t 的子序列。

    // 双指针、动态规划
    // 动态规划
    // 转化为最短编辑距离问题,最短编辑距离问题中 s => t 可以经过修改、插入、删除来完成,而在这个问题中 s => t 只能通过插入
    public static boolean isSubsequence(String s, String t) {
      char[] a = s.toCharArray();
        char[] b = t.toCharArray();
        int[][] dp = new int[a.length + 1][b.length + 1];
        for (int i = 1; i < a.length + 1; i++) {
            for (int j = 1; j < b.length + 1; j++) {
                if (a[i - 1] == b[j - 1]) {
                    dp[i][j] = dp[i - 1][j - 1] + 1;
                } else {
                    dp[i][j] = dp[i][j - 1];
                }
            }
        }
        return dp[a.length][b.length] == s.length();
    }
    
  • LCP07、传递信息
    给定一个整数 n,表示 n 个小朋友,编号从 0 ~ n - 1;给定一个二维数组,其中第二维 [2, 6] 表示信息可以从 2 传到 6;给定一个整数 k 表示传递的次数。求信息经过 k 次传播传到 n - 1 处的方案数。

    // 动态规划
    public static int numWays(int n, int[][] relation, int k) {
        int[][] dp = new int[k + 1][n];
        dp[0][0] = 1;  // 可以理解为刚开始信息的持有者
        for (int i = 1; i < k + 1; i++) {
            for (int[] temp : relation) {
                dp[i][temp[1]] += dp[i - 1][temp[0]];
            }
        }
        return dp[k][n - 1];
    }
    

@XGLLHZ - 吴奇隆 -《祝你一路顺风》.mp3

  • 21
    点赞
  • 21
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值