LeetCode
1. 递归
递归要素:
- 参数中要有操作的数组或集合,以及目标。
- 结束条件,结束的判断需要由其它参数参与确定。
- 根据实际情况方法是否有返回。
2. 动态规划
动态规划要素:
- 创建一个(n+1)的数组来存放规划值。
- 数组第一个需要虚拟赋值,一般为0或false等。
- 由数组的最后一个或最后几个确定结果。
2.1 LeetCode198. 打家劫舍
创建一个长于数组的数组,用来存放每一次计算的结果,而每一次的计算又是从第一个到当前的**结果,一直重复到结束。
public int rob(int[] nums) {
int result = 0; int length = nums.length; if (length == 1) {
result = nums[0]; } else if (length == 2) {
result = Math.max(nums[0], nums[1]); } else if (length > 2) {
int[] dp = new int[length + 1]; dp[0] = 0; dp[1] = nums[0]; dp[2] = nums[1]; for (int i = 3; i <= length; i++) {
// 动态规划(dynamic programming), 妙 啊! dp[i] = Math.max(dp[i - 2], dp[i - 3]) + nums[i - 1]; } result = Math.max(dp[length], dp[length - 1]); } return result; }
讯享网
2.2 LeetCode139. 单词拆分
讯享网 / * 动态规划 (dynamic programming) * 以示例1为例,s为"leetcode",wordDict为{"leet", "code"} * dp数组为[true, false, false, false, true, false, false, false, true] */ public static boolean wordBreak(String s, List<String> wordDict) {
HashSet<String> wordSet = new HashSet<>(wordDict); // 去除词典重复项 int length = s.length(); boolean[] dp = new boolean[length + 1]; dp[0] = true; for (int i = 1; i <= length; i++) {
for (int j = 0; j < i; j++) {
if (dp[j] && wordSet.contains(s.substring(j, i))) {
dp[i] = true; break; } } } return dp[length]; }
3. 队列
3.1 优先队列 (PriorityQueue) 重排序
实例化示例:
// 需要指定队列中存储的对象类型,如下为Integer。 // 可以指定初始容量,initialCapacity。 // 可以指定排序规则,如下为从小到大排序。 PriorityQueue<Integer> queueMin = new PriorityQueue<Integer>(new Comparator<Integer>() {
@Override public int compare(Integer o1, Integer o2) {
return (o1 < o2) ? 1 : ((o1 == o2) ? 0 : -1); } });
| 常用方法 | 描述 | 返回 | 参数 |
|---|---|---|---|
| size() | 获取队列大小 | int:队列中元素的个数 | |
| isEmpty() | 队列是否为空 | boolean:元素的个数是否为0,size()==0 | |
| offer(E e) | 向队列中添加元素,自动排在最末尾 | boolean:是否添加成功 | 要添加的元素 |
| peek() | 获取最后位置的元素,但实际是类中一个数组的第一个 | E:最后位置的元素 | |
| poll() | 弹出最后位置的元素,并且能得到此元素 | E:最后位置的元素 |
3.1 阻塞队列 (BlockingQueue) 重阻塞
实例化示例:
讯享网// 可以指定初始容量,initialCapacity。 // fair为阻塞时的策略,为true按队列先进先出的顺序处理元素,否则为未指定处理顺序。 BlockingQueue<CachedLocation> pendingLocations = new ArrayBlockingQueue<>(10000, true);
| 常用方法 | 描述 | 返回 | 参数 |
|---|---|---|---|
| size() | 获取队列大小,获取时会加锁,所以尽量少获取 | int:队列中元素的个数 | |
| isEmpty() | 队列是否为空,会使用size(),所以尽量少获取 | boolean:元素的个数是否为0,size()==0 | |
| offer(E e) | 向队列中添加元素,自动排在最末尾 | boolean:是否添加成功 | 要添加的元素 |
| drainTo(Collection<? super E> c) | 从此队列中删除所有可用元素并将它们添加到给定集合 | int:传输的元素数 | 存放所有元素的集合 |
4. 前缀和
4.1 LeetCode437. 路径总和 III
前缀和结合二叉树TreeNode来使用。还使用Map来记录一些特殊值的次数。
public static void main(String[] arg0) {
Object[] nums = {
10, 5, -3, 3, 2, null, 11, 3, -2, null, 1}; System.out.println("路径总和 = " + pathSum(new TreeNode(nums), 8)); } public static int pathSum(TreeNode root, int targetSum) {
Map<Integer, Integer> counts = new HashMap<>(); // 这里设置0对应的值为1,这样如果第一个根节点就等于目标和,在递归中给res赋的值就是1,不需要再判断 counts.put(0, 1); return childMethod(root, counts, 0, targetSum); } / * 递归 + 前缀和 */ public static int childMethod(TreeNode node, Map<Integer, Integer> counts, int currSum, int targetSum) {
if (node == null || !node.isValidVal()) {
// 递归到了最末端终止,并且返回0条符合条件路径 return 0; } int res = 0; currSum += node.val; res = counts.getOrDefault(currSum - targetSum, 0); // 这里赋值不明白 counts.put(currSum, counts.getOrDefault(currSum, 0) + 1); // 记录前缀和的次数 res += childMethod(node.left, counts, currSum, targetSum); res += childMethod(node.right, counts, currSum, targetSum); counts.put(currSum, counts.getOrDefault(currSum, 0) - 1); // 检查完当前分支就要减少当前前缀和的次数 return res; }
附:时间复杂度 和 空间复杂度
- 时间复杂度:假设单次操作的时间为单位时间,求出结果总共需要的大概次数。
讯享网 - 递归的时间复杂度一般为n。 - 动态规划的时间复杂度一般为n的平方。
- 空间复杂度:整个操作需要开辟的内存空间。
- 递归需要在栈上开辟空间。首次执行开辟一个空间,每次递归多开辟一个空间,最多递归多少次就是空间复杂度。 - 动态规划需要创建一个(总长度+1)的数组来存放规划值,空间复杂度即为数组长度。
牛客
1. 对一个数组或者集合中的所有元素做全部组合
– 采用递归,而且递归中含有for循环
– 数组或集合数据要全局,判断标准要全局
– 新建两个全局的数组,大小要和源数据相同,一个记录选中下标,一个记录对应的值
– dfs递归两个参数,一个p记录当前赋值位置并且下次递归也可使用,一个count记录使用前几个数据
讯享网public static int[] data; // 源数据 public static int number; // 判断标准 public static int[] tempIndex; // 记录选中下标 public static int[] tempData; // 记录选中下标对应的数据 {
List<List<Integer>> ans = new ArrayList<>(); dfs(0, 0, ans); // p 和 count 必须从0开始 } // 递归方法,第三个参数是结果,其实也可以做成全局变量 public static void dfs(int p, int count, List<List<Integer>> ans) {
int total = 0; List<Integer> temp = new ArrayList<>(); for (int i = 0; i < count; i++) {
temp.add(tempData[i]); total += temp.get(i); if (total == number) {
ans.add(temp); return; // 要根据判断标准来写,是否需要再增加count } if (total > number) {
return; // 要根据判断标准来写,是否需要再增加count } } int i = 0; if (p - 1 >= 0 && tempIndex[p - 1] != -1) {
i = tempIndex[p - 1] + 1; } for (; i < data.length; i++) {
tempData[p] = data[i]; tempIndex[p] = i; dfs(p + 1, count + 1, ans); } }
- 举个例子并列下递归的执行顺序,例如:
4, 3, 2, 1。- dfs for循环第一个
4,临时数据数组第一个位置为4, count也变成1,之后第二次递归又for循环, 临时数据数组第一个位置为3,count也变成2,得到4, 3,类推走完4, 3, 2,4, 3, 2, 1,4, 3, 1。- 之后第二次递归的for循环走下一个,也就是临时数据第二个位置是源数组第三个位置,得到
4, 2,再4, 2, 1结束。- 之后第二次递归的for循环走最后一个,得到
4, 1,整个第一个循环以4开头的就全部完成。- 接下来第一个循环以3开头,顺序
3,3, 2,3, 2, 1,3, 1。- 继续第一个循环以2开头,顺序为
2,2, 1。- 最后第一次循环只剩下
1。
2. 数组只能向上下左右感染的问题
– 先将上下左右的偏移数组列出来,循环用
– 需要有一个队列记录已感染位置,先进先处理的原则,并且处理后做标记已处理
– 用感染数增加到最后的值做判断得结论,或者有dp[]记录感染步骤得结论
static int[][] offsets = new int[][]{
{
1, 0}, {
-1, 0}, {
0, 1}, {
0, -1}}; Queue<int[]> queue = new LinkedList<>(); queue.offer(p1); queue.offer(p2); int count = m * n - 2; // 已经有两个作为扩散点了 int[][] dp = new int[m][n]; dp[p1[0]][p1[1]] = 1; dp[p2[0]][p2[1]] = 1; int day = 1; while (!queue.isEmpty() && count > 0) {
int[] p = queue.poll(); // 弹出并获取到队列首部对象 int x = p[0]; int y = p[1]; day = dp[x][y] + 1; for (int[] offset : offsets) {
int newX = x + offset[0]; int newY = y + offset[1]; if (newX >= 0 && newX < m && newY >= 0 && newY < n && dp[newX][newY] == 0) {
dp[newX][newY] = day; queue.offer(new int[]{
newX, newY}); count --; } } } System.out.println(day - 1);

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容,请联系我们,一经查实,本站将立刻删除。
如需转载请保留出处:https://51itzy.com/kjqy/41460.html