Leetcode-MT-Simple
文章实际写于2021年,那个炎热的夏天。
Leet Code 美团题库简单类总结,题目按照解法可大致分为数学法、计数法、位运算、双指针法、字符串、哈希表、栈、递归/迭代、排序法、匹配法、记忆化法、二分法、分治法、摩尔投票法、前缀和、模拟法、动态规划几大类。大部分题目可以有多种解法。
数学法、计数法、位运算、双指针法、字符串、哈希表、栈、递归/迭代、排序法、匹配法、记忆化法、二分法、分治法、摩尔投票法、前缀和、模拟法、动态规划
- 数学法: 即利用 +、-、*、/、% 等数学思想来解决,一般结合递归/迭代使用。
- 计数法: 即通过统计元素个数来解决。如字符串中的字母个数,数组中的元素个数。
- 位运算: 即通过 &、|、^、<<、>> 等位运算来解决。
- 双指针法: 即通过两个指针来解决。两个指针可以同时从前往后,也可以从后往前,也可以一个双向奔赴,还有快慢指针法。
- 字符串: 即利用字符串相关类的相关函数来解决。如 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