这篇文章主要是记录刷过的题以及里面一些需要注意的关键点(https://www.programmercarl.com/ )
1.做题记录
2.一些java用法 数组 215. 数组中的第K个最大元素 借用快排的思路即可
3.做题笔记 数组 1732. 找到最高海拔 i位置的真正海拔等于gain[0到i]的sum加和
88. 合并两个有序数组 把nums1当中的前m个数字串到最后再合并即可(注意是倒序)
219. 存在重复元素 II 这个问题是滑动窗口问题
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 class Solution { public boolean containsNearbyDuplicate (int [] nums, int k) { int len = nums.length; for (int left = 0 ; left < len; left++) { for (int right = left + 1 ; right <= left + k && right < len; right++) { if (nums[left] == nums[right]) { return true ; } } } return false ; int len = nums.length; Set<Integer> set = new HashSet <>(); for (int i = 0 ; i < len; i++) { if (set.contains(nums[i])) { return true ; } else { set.add(nums[i]); } if (i - k >= 0 ) { set.remove(nums[i - k]); } } return false ; } }
658. 找到 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 class Solution { public List<Integer> findClosestElements (int [] arr, int k, int x) { int index = search(arr, x); int len = arr.length; int left = index + 1 , right = index - 1 ; while (left - 1 >= 0 && right + 1 < len && right - left + 1 < k) { if (x - arr[left - 1 ] <= arr[right + 1 ] - x) { left--; } else { right++; } } if (left == 0 ) { right = k - 1 + left; } else if (right == len - 1 ) { left = -k + 1 + right; } List<Integer> result = new LinkedList <>(); for (int i = left; i <= right; i++) { result.add(arr[i]); } return result; } public int search (int [] arr, int x) { int left = 0 ; int right = arr.length - 1 ; while (left < right) { int mid = (right - left) / 2 + left; if (arr[mid] == x) { return mid; } else if (arr[mid] > x) { right = mid - 1 ; } else { left = mid + 1 ; } } return left; } }
链表 933. 最近的请求次数 为了减少遍历时间,可以让list只存储符合当前时间的时间戳
注意不能使用以下方式删除元素,因为for遍历过程当中,需要验证对list修改次数不变才能继续遍历
1 2 3 4 5 for (Integer i : log) { if (i < t - 3000 ) { log.remove(i); } }
2095. 删除链表的中间节点 快慢指针即可
61. 旋转链表 找到倒数第k%length个位置即可
82. 删除排序链表中的重复元素 II 注意:头结点可能被删除,所以需要构造一个假头结点开始遍历
哈希表 1679. K 和数对的最大数目 使用哈希表记录元素出现的次数即可
字符串 1768. 交替合并字符串 可以使用StringBuilder构造字符串,更加方便
387. 字符串中的第一个唯一字符 使用数组记录每一个字母的出现次数即可
125. 验证回文串 注意把大写字母转化成小写字母的两种方式即可:
1 2 originalString.toLowerCase(); Character.toLowerCase(c);
注意:如果是以下的形式,会得到ASCII码
动态规划 LCR 003. 比特位计数 1.dp[i]是指数字i的1的个数
2.dp[i]=dp[i-t]+1,t是2^(当前位数),例如dp[7]=dp[3]+1
3.dp[0]=0,t=1
4.i从小到大
264. 丑数 II 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 class Solution { public int nthUglyNumber (int n) { int [] dp = new int [n]; dp[0 ] = 1 ; int p2 = 0 , p3 = 0 , p5 = 0 ; for (int i = 1 ; i < n; i++) { dp[i] = Math.min(Math.min(dp[p2] * 2 , dp[p3] * 3 ), dp[p5] * 5 ); if (dp[i] == dp[p2] * 2 ) { p2++; } if (dp[i] == dp[p3] * 3 ) { p3++; } if (dp[i] == dp[p5] * 5 ) { p5++; } } return dp[n - 1 ]; } }
栈与队列 735. 小行星碰撞 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 class Solution { public int [] asteroidCollision(int [] asteroids) { Stack<Integer> stack = new Stack <>(); for (int asteroid : asteroids) { boolean valid = true ; while (!stack.isEmpty() && asteroid < 0 && stack.peek() > 0 && valid) { valid = Math.abs(asteroid) > Math.abs(stack.peek()); if (Math.abs(asteroid) >= Math.abs(stack.peek())) { stack.pop(); } } if (valid) { stack.push(asteroid); } } int [] result = new int [stack.size()]; for (int i = result.length - 1 ; i >= 0 ; i--) { result[i] = stack.pop(); System.out.println(result[i]); } return result; } }
贪心算法 回溯算法 数学 374. 猜数字大小 二分查找即可
1071. 字符串的最大公因子 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 class Solution { public String gcdOfStrings (String str1, String str2) { int n = this .gcd(str1.length(), str2.length()); String result = str1.substring(0 , n); String newString1 = result.repeat(str1.length() / n); String newString2 = result.repeat(str2.length() / n); if (newString1.equals(str1) && newString2.equals(str2)) { return result; } else { return "" ; } } public int gcd (int a, int b) { int c; if (a > b) { c = a % b; } else { c = b % a; } while (c != 0 ) { a = b; b = c; c = a % b; } return Math.min(a, b); } }
69. x 的平方根 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 class Solution { public int mySqrt (int x) { return search(x); } public int search (int x) { if (x == 0 || x == 1 ) { return x; } int begin = 0 ; int end = x; int result = 0 ; while (begin <= end) { int mid = (end - begin) / 2 + begin; if (mid <= x / mid) { result = mid; begin = mid + 1 ; } else { end = mid - 1 ; } } return result; } }
50. Pow(x, n) 用递归的思想即可,注意:这个方法超时了
1 2 int half = n / 2 ;return my(x, half) * my(x, n - half);
需要减少运算my(x, half)的时间
1 2 double result = my(x, n / 2 );return n % 2 == 0 ? result * result : result * result * x;
204. 计数质数 如果一个数字x是质数,那么2x,3x,,一定是合数。
为了简化时间复杂度:可以从x*x开始赋值,因为在考虑到x之前,遇到2的时候就已经把2x赋值过了
268. 丢失的数字 把问题转化成:所有数字nums和0-n当中,有一个数字只出现了1次,把这个数字找出来。因此,把nums[i]和0-i异或一遍即可
1979. 找出数组的最大公约数 辗转相除法即可
397. 整数替换 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 class Solution { public int integerReplacement (int n) { int [] dp = new int [n + 1 ]; dp[1 ] = 0 ; for (int i = 2 ; i < n + 1 ;) { dp[i] = dp[i / 2 ] + 1 ; i++; if (i < n + 1 ) { int x = (i - 1 ) / 2 , y = (i + 1 ) / 2 ; dp[i] = Math.min(dp[x], dp[y]) + 2 ; i++; } } return dp[n]; Map<Integer, Integer> map = new HashMap <>(); if (n == 1 ) { return 0 ; } if (!map.containsKey(n)) { if (n % 2 == 0 ) { map.put(n, integerReplacement(n / 2 ) + 1 ); } else { map.put(n, Math.min(integerReplacement(n / 2 ), integerReplacement(n / 2 + 1 )) + 2 ); } } return map.get(n); } }
单调栈 二叉树 1161. 最大层内元素和 层序遍历即可
100. 相同的树 前序遍历即可
图论 相关知识 连通性 连通图:无向图当中,任何两个节点都是可以到达的
连通分量:无向图当中的极大连通子图
强连通图:有向图中,任何两个节点是可以相互到达的
强连通分量:有向图当中的极大强连通子图
表示方法 1.邻接矩阵: 二维数组
grid[2] [5] = 6,表示节点2 指向 节点5,边的权值为6
2.邻接表:数组 + 链表
描述的图是:
节点1 指向 节点3、节点5;节点2 指向 节点4、节点3、节点5;节点3 指向 节点4;节点4 指向 节点1
深搜 & 宽搜 深搜先按照一个方向搜索下去,如果碰壁就回溯 ,其框架和回溯基本一致:
1 2 3 4 5 6 7 8 9 10 11 12 void dfs (参数) { if (终止条件) { 存放结果; return ; } for (选择:本节点所连接的其他节点) { 处理节点; dfs(图,选择的节点); 回溯,撤销处理结果 } }
回顾一下回溯的框架:
1 2 3 4 5 6 7 8 9 10 11 void backtracking (参数) { if (终止条件) { 存放结果; return ; } for (选择:本层集合中元素(树中节点孩子的数量就是集合的大小)) { 处理节点; backtracking(路径,选择列表); 回溯,撤销处理结果 } }
宽搜:
98. 所有可达路径 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 import java.util.*;public class Main { static List<List<Integer>> result = new LinkedList <>(); static List<Integer> path = new LinkedList <>(); public static void main (String[] args) { Scanner scan = new Scanner (System.in); int n = scan.nextInt(), m = scan.nextInt(); List<Integer>[] graph = new List [n]; for (int i = 0 ; i < n; i++) { graph[i] = new LinkedList <>(); } while (scan.hasNext()) { int x = scan.nextInt(); int y = scan.nextInt(); graph[x - 1 ].add(y - 1 ); } int [] used = new int [n]; path.add(0 ); dfs(used, 0 , n, graph); if (result.size() == 0 ) { System.out.println(-1 ); } for (List<Integer> p : result) { StringBuilder sb = new StringBuilder (); sb.append(p.get(0 ) + 1 ); for (int i = 1 ; i < p.size(); i++) { sb.append(" " + (p.get(i) + 1 )); } System.out.println(sb); } } public static void dfs (int [] used, int x, int n, List<Integer>[] graph) { if (x == n - 1 ) { result.add(new LinkedList <>(path)); return ; } for (int i = 0 ; i < graph[x].size(); i++) { int y = graph[x].get(i); if (used[y] == 0 ) { used[y] = 1 ; path.add(y); dfs(used, y, n, graph); path.remove(path.size() - 1 ); used[y] = 0 ; } } } }