最小路径和 基本思路:动态规划,每个格子依次计算,计算当前格子的最小路径和,因为每次移动必定是往下或者往右,因此每个格子的路径和必定来自于上边格子或者左边格子的数字加上当前格子的数字 时间复杂度分析:O(mn),会遍历所有格子,因此复杂度是n m
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 public class MinPathSum { static int minPathSum (int [][] grid) { if (grid == null || grid.length == 0 || grid[0 ].length == 0 ) return 0 ; int n = grid.length ; int m = grid[0 ].length; for (int i = 0 ; i < n; i++) { for (int j = 0 ; j < m; j++) { if (i == 0 && j== 0 ) continue ; if (i == 0 ) { grid[0 ][j] += grid[0 ][j-1 ]; continue ; } if (j == 0 ) { grid[i][0 ] += grid[i-1 ][0 ]; continue ; } if (grid[i-1 ][j] > grid[i][j-1 ]) { grid[i][j] += grid[i][j-1 ]; } else { grid[i][j] += grid[i-1 ][j]; } } } return grid[n-1 ][m-1 ]; } public static void main (String[] args) { System.out.println(minPathSum(new int [][]{})); } }
排序矩阵查找 基本思路:从右上角开始遍历,左边是比它小的,下边是比它大的,相当于是一个二叉树遍历 时间复杂度分析:O(mn),会遍历所有格子,因此复杂度是n m
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 public class SearchMatrix { static boolean searchMatrix (int [][] matrix, int target) { if (matrix == null || matrix.length == 0 || matrix[0 ].length == 0 ) return false ; int n = 0 ; int m = matrix[0 ].length - 1 ; while (true ) { if (matrix[n][m] == target) return true ; if (matrix[n][m] > target) m--; else n++; if (m < 0 || n >= matrix.length) return false ; } } public static void main (String[] args) { System.out.println(searchMatrix(new int [][]{{}}, 18 )); } }
合并若干三元组以形成目标三元组 基本思路:根据题设可知,每次合并都会取各个位置更大的那个数,因此如果一个数组中的任一位置存在比目标数更大的数,则这个数组一定不能进行合并;反之,如果一个数组的任一位置存在目标数,且其他各位置的数都比目标数小,则一定能进行合并
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 public class MergeTriplets { static boolean mergeTriplets (int [][] triplets, int [] target) { if (triplets == null || target == null || triplets.length == 0 || triplets[0 ].length == 0 || target.length == 0 ) return false ; int one = target[0 ]; int two = target[1 ]; int three = target[2 ]; boolean oneJudge = false ; boolean twoJudge = false ; boolean threeJudge = false ; for (int i = 0 ; i < triplets.length; i++) { if (!oneJudge && triplets[i][0 ] == one && triplets[i][1 ] <= two && triplets[i][2 ] <= three) oneJudge = true ; if (!twoJudge && triplets[i][1 ] == two && triplets[i][0 ] <= one && triplets[i][2 ] <= three) twoJudge = true ; if (!threeJudge && triplets[i][2 ] == three && triplets[i][0 ] <= one && triplets[i][1 ] <= two) threeJudge = true ; if (oneJudge && twoJudge && threeJudge) return true ; } return false ; } public static void main (String[] args) { System.out.println(mergeTriplets(new int [][]{{2 ,5 ,3 },{1 ,8 ,4 },{1 ,7 ,5 }}, new int []{2 ,5 ,3 })); } }
二叉搜索树与双向链表 基本思路:本质上是一个将二叉树按大小输出的问题,使用中序遍历可以实现。同时需要将头尾连接,因此存储头部结点,直到中序遍历结束,将最后的结点指向头部结点。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 class Node { public int val; public Node left; public Node right; public Node () {} public Node (int _val) { val = _val; } public Node (int _val,Node _left,Node _right) { val = _val; left = _left; right = _right; } } public class TreeToDoublyList { static Node head, pre; static Node treeToDoublyList (Node root) { if (root == null ) return root; dfs(root); head.left = pre; pre.right = head; return head; } static void dfs (Node node) { if (node == null ) return ; dfs(node.left); if (head == null ) head = node; else { pre.right = node; node.left = pre; } pre = node; dfs(node.right); } }
两个相同字符之间的最长子字符串 基本思路: 1.题设只有小写字母,因此可以用一个容量26的数组来进行存储。 2.按顺序遍历数组,将每个字母出现的位置索引存储在数组中,当找到重复的字母时,将其位置和数组中的索引进行相减,得到的结果和已存储的最大值比较,如果当前结果更大,将最大值替换 3.返回最大值
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 public class MaxLengthBetweenEqualCharacters { static int maxLengthBetweenEqualCharacters (String s) { if (s == null || s.length() == 0 ) return -1 ; int [] alpha = new int [26 ]; char [] characters = s.toCharArray(); int result = -1 ; int temp; int ch; for (int i = 0 ; i < characters.length; i++) { ch = characters[i] - 97 ; if (alpha[ch] > 0 ) { temp = i - alpha[ch]; if (temp > result) result = temp; } else { alpha[ch] = i + 1 ; } } return result; } }
替换隐藏数字得到的最晚时间 基本思路:由于固定是XX:XX的格式字符串,因此需要处理的无非是四个位置的字符。后两位的字符如果出现’?’,那一定是可以替换为5和9。前两位出现’?’,需要考虑: 1.第一位是’?’,需要看第二位的值,如果大于3,那第一位只能是1,因为不存在24-29点这样的值;如果第二位是1,2,3,那第一位可以是2 2.第二位是’?’,需要看第一位的值,如果是2,那第二位最大是3;如果是第一位是1,那第二位最大可以是9
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 public class MaximumTime { static String maximumTime (String time) { char [] chars = time.toCharArray(); if (chars[0 ] == '?' ) { if (chars[1 ] > '3' && chars[1 ] <= '9' ) chars[0 ] = '1' ; else chars[0 ] = '2' ; } if (chars[1 ] == '?' ) { if (chars[0 ] == '1' || chars[0 ] == '0' ) chars[1 ] = '9' ; else chars[1 ] = '3' ; } if (chars[3 ] == '?' ) chars[3 ] = '5' ; if (chars[4 ] == '?' ) chars[4 ] = '9' ; return String.valueOf(chars); } public static void main (String[] args) { System.out.println(maximumTime("?4:03" )); } }
拆分字符串使唯一子字符串的数目最大 基本思路:需要遍历所有的子字符串可能,用到递归+回溯
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 public class MaxUniqueSplit { int ct = 1 ; int length = 1 ; int maxUniqueSplit (String s) { Set<String> set = new HashSet <>(16 ); length = s.length(); maxUniqueSplit(0 , 0 , s, set); return ct; } void maxUniqueSplit (int index, int split, String s, Set<String> set) { if (ct < split) ct = split; for (int i = index; i < length; i++) { if (length - i + split < ct) break ; String substring = s.substring(index, i+1 ); if (set.add(substring)) { maxUniqueSplit(i + 1 , split + 1 , s, set); set.remove(substring); } } } int count=0 ; char [] arr; Set<String> lookUp; public int maxUniqueSplit_2 (String s) { arr=s.toCharArray(); lookUp=new HashSet <>(); dfs(0 ,0 ,arr,s.length()); return count; } private void dfs (int begin,int cur_depth,char [] arr,int remain_length) { if (remain_length==0 ){ count=Math.max(cur_depth,count); return ; } for (int start=begin,end=begin;end<arr.length;end++){ int left_length=arr.length-end-1 ; if (cur_depth+1 +left_length<=count) break ; String temp=new String (arr,start,end-start+1 ); if (lookUp.contains(temp)) continue ; lookUp.add(temp); dfs(end+1 ,cur_depth+1 ,arr,left_length); lookUp.remove(temp); } } }
两个链表的第一个公共节点 基本思路:两个链表的长度可能不一,长度分别为m和n。必然有m+n=n+m。 情况一:两个链表相交
链表 headA 和 headB 的长度分别是 m 和 n。假设链表 headA 的不相交部分有 a 个节点,链表 headB 的不相交部分有 b 个节点,两个链表相交的部分有 c 个节点,则有 a+c=m ,b+c=n。
如果 a=b,则两个指针会同时到达两个链表的第一个公共节点,此时返回两个链表的第一个公共节点;
如果 a≠b,则指针 pA 会遍历完链表 headA,指针 pB 会遍历完链表 headB,两个指针不会同时到达链表的尾节点,然后指针 pA 移到链表 headB 的头节点,指针 pB 移到链表 headA 的头节点,然后两个指针继续移动,在指针 pA 移动了 a+c+b 次、指针 pB 移动了 b+c+a 次之后,两个指针会同时到达两个链表的第一个公共节点,该节点也是两个指针第一次同时指向的节点,此时返回两个链表的第一个公共节点。
情况二:两个链表不相交
链表 headA\textit{headA}headA 和 headB\textit{headB}headB 的长度分别是 mmm 和 nnn。考虑当 m=nm=nm=n 和 m≠nm \ne nm=n 时,两个指针分别会如何移动:
如果 m=n,则两个指针会同时到达两个链表的尾节点,然后同时变成空值 null,此时返回 null;
如果 m≠n,则由于两个链表没有公共节点,两个指针也不会同时到达两个链表的尾节点,因此两个指针都会遍历完两个链表,在指针 pA 移动了 m+n 次、指针 pB 移动了 n+m 次之后,两个指针会同时变成空值 null,此时返回 null。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 public class GetIntersectionNode { ListNode getIntersectionNode (ListNode headA, ListNode headB) { if (headA == null || headB == null ) return null ; ListNode head1 = headA; ListNode head2 = headB; while (head1 != head2) { head1 = head1 != null ? head1.next:headB; head2 = head2 != null ? head2.next:headA; } return head1; } }
修改后的最大二进制字符串 基本思路:根据题设可知,最佳方案应当是将所有的0都移到一起,从而将00变成10,这样将是最大的可能,因此我们可以将第一个0右边的所有1都移到最右边,从而得到两边是1,中间是0的结构,然后再将中间的00依次转成10即可
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 public class MaximumBinaryString { String maximumBinaryString (String binary) { if (binary == null || binary.length() <= 1 ) return binary; char [] chars = binary.toCharArray(); int index; int len = chars.length; for (int i = 0 ; i < len;) { if (chars[i++] == '0' ) { index = i; for (int j = len - 1 ; j >= i;) { if (chars[i] == '1' ) { if (chars[j] == '0' ) { chars[i] = '0' ; chars[j] = '1' ; } else j--; } else i++; if (chars[index] == '0' ) { chars[index - 1 ] = '1' ; index++; } } break ; } } return new String (chars); } public static void main (String[] args) { MaximumBinaryString maximumBinaryString = new MaximumBinaryString (); System.out.println(maximumBinaryString.maximumBinaryString("00010" )); } }
检查两个字符串是否几乎相等 基本思路:维护一个数组arr,随后对word1和word2两个字符串进行遍历,word1中出现的字符在arr中存储ascii值加1,word2种出现的字符在arr中存储ascii值减1,最后遍历arr,出现绝对值大于3的说明不符合要求。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 public class CheckAlmostEquivalent { public boolean checkAlmostEquivalent (String word1, String word2) { char [] chars1 = word1.toCharArray(); char [] chars2 = word2.toCharArray(); int [] chArr1 = new int [26 ]; int len = chars1.length; for (int i = 0 ; i < len; i++) { chArr1[chars1[i]-97 ]++; chArr1[chars2[i]-97 ]--; } for (int i = 0 ; i < 26 ; i++) { if (Math.abs(chArr1[i]) > 3 ) return false ; } return true ; } public static void main (String[] args) { CheckAlmostEquivalent checkAlmostEquivalent = new CheckAlmostEquivalent (); System.out.println(checkAlmostEquivalent.checkAlmostEquivalent("dfgcbehcifihghedhffbggdcebbbghigfhddhiigcgfeiih" , "cdcgbeeceifbgchhfiffhifghiebfchbcbfhggchfbbhddb" )); } }
转换字符串的最少操作次数 基本思路:贪心算法,每次遇到x时,选中包含X以及之后的三个字符,进行转换;换句话说,相当于每次碰到X,将计数器+1,同时遍历数组的索引+3
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 public class MinimumMoves { public int minimumMoves (String s) { char [] chars = s.toCharArray(); int result = 0 ; for (int i = 0 ; i < chars.length;) { if (chars[i] == 'X' ) { result++; i = i + 3 ; } else i++; } return result; } public static void main (String[] args) { MinimumMoves minimumMoves = new MinimumMoves (); System.out.println(minimumMoves.minimumMoves("XXXX" )); } }
有序矩阵中第 K 小的元素 基本思路:由于是有序矩阵,可以利用该特性进行二分查找,具体做法是,每次找到中间的位置时,比这个位置小的数字只可能是左上的方向,往左上方向查找比当前位置小的数字的个数,如果个数比k小,则需要往右下方向二分,如果个数比k大,则需要往左上二分。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 public class KthSmallest { public int kthSmallest (int [][] matrix, int k) { int len = matrix.length; int [] numArr = new int [len*len]; for (int i = 0 ; i < len; i++) { for (int j = 0 ; j < len; j++) { numArr[i*len + j] = matrix[i][j]; } } Arrays.sort(numArr); return numArr[k-1 ]; } public int kthSmallest_v2 (int [][] matrix, int k) { int n = matrix.length; int left = matrix[0 ][0 ]; int right = matrix[n - 1 ][n - 1 ]; while (left < right) { int mid = left + ((right - left) >> 1 ); if (check(matrix, mid, k, n)) { right = mid; } else { left = mid + 1 ; } } return left; } public boolean check (int [][] matrix, int mid, int k, int n) { int i = n - 1 ; int j = 0 ; int num = 0 ; while (i >= 0 && j < n) { if (matrix[i][j] <= mid) { num += i + 1 ; j++; } else { i--; } } return num >= k; } public static void main (String[] args) { KthSmallest kthSmallest = new KthSmallest (); System.out.println(kthSmallest.kthSmallest_v2(new int [][]{{1 ,2 ,2 },{2 ,2 ,7 },{3 ,4 ,8 }},5 )); } }