当前位置: 首页 > news >正文

代码随想录算法训练营第十八天

LeetCode题目:

  • 669. 修剪二叉搜索树
  • 108. 将有序数组转换为二叉搜索树
  • 538. 把二叉搜索树转换为累加树
  • 2179. 统计数组中好三元组数目(每日一题)
  • 307. 区域和检索 - 数组可修改(树状数组)
  • 122. 买卖股票的最佳时机 II
  • 309. 买卖股票的最佳时机含冷冻期
  • 188. 买卖股票的最佳时机 IV

其他:

今日总结
往期打卡


669. 修剪二叉搜索树

跳转: 669. 修剪二叉搜索树

学习: 代码随想录公开讲解

问题:

给你二叉搜索树的根节点 root ,同时给定最小边界low 和最大边界 high。通过修剪二叉搜索树,使得所有节点的值在[low, high]中。修剪树 不应该 改变保留在树中的元素的相对结构 (即,如果没有被移除,原有的父代子代关系都应当保留)。 可以证明,存在 唯一的答案

所以结果应当返回修剪好的二叉搜索树的新的根节点。注意,根节点可能会根据给定的边界发生改变。

在这里插入图片描述

思路:

这题需要利用二叉搜索树的性质
遍历,如果小于边界搜右边代替自己返回,如果大于自己搜左边代替自己返回

复杂度:

  • 时间复杂度: O ( n ) O(n) O(n)
  • 空间复杂度: O ( 1 ) O(1) O(1)

代码(前序遍历):

class Solution {public TreeNode trimBST(TreeNode root, int low, int high) {if(root==null) return null;if(root.val<low) return trimBST(root.right,low,high);if(root.val>high) return trimBST(root.left,low,high);root.left = trimBST(root.left,low,high);root.right = trimBST(root.right,low,high);return root;        }
}

108. 将有序数组转换为二叉搜索树

跳转: 108. 将有序数组转换为二叉搜索树

学习: 代码随想录公开讲解

问题:

给你一个整数数组 nums ,其中元素已经按 升序 排列,请你将其转换为一棵 平衡 二叉搜索树。

在这里插入图片描述

思路:

平衡二叉树是指该树所有节点的左右子树的高度相差不超过1
所以这题需要对数组二分.
每次平分数组向下遍历到 l>=r 即可

复杂度:

  • 时间复杂度: O ( n ) O(n) O(n)
  • 空间复杂度: O ( 1 ) O(1) O(1)

代码(前序遍历):

class Solution {TreeNode buildTree(int[] nums,int left,int right){if(left>=right) return null;int mid = (left+right)/2;TreeNode root = new TreeNode(nums[mid]);if(right-left==1) return root;root.left = buildTree(nums,left,mid);root.right = buildTree(nums,mid+1,right);return root;}public TreeNode sortedArrayToBST(int[] nums) {return buildTree(nums,0,nums.length);}
}

538. 把二叉搜索树转换为累加树

跳转: 538. 把二叉搜索树转换为累加树

学习: 代码随想录公开讲解

问题:

给出二叉 搜索 树的根节点,该树的节点值各不相同,请你将其转换为累加树(Greater Sum Tree),使每个节点 node 的新值等于原树中大于或等于 node.val 的值之和。

提醒一下,二叉搜索树满足下列约束条件:

  • 节点的左子树仅包含键 小于 节点键的节点。
  • 节点的右子树仅包含键 大于 节点键的节点。
  • 左右子树也必须是二叉搜索树。

在这里插入图片描述

思路:

二叉树搜索树中序遍历有序,这题要把节点替换为大于等于当前节点的累加和,所以可以逆反中序遍历,然后额外使用一个遍历记录上一次的值.

复杂度:

  • 时间复杂度: O ( n ) O(n) O(n)
  • 空间复杂度: O ( 1 ) O(1) O(1)

代码(逆反中序遍历):

class Solution {int pre = 0;public TreeNode convertBST(TreeNode root) {if(root==null) return null;convertBST(root.right);root.val+=pre;pre = root.val;convertBST(root.left);return root;        }
}

2179. 统计数组中好三元组数目(每日一题)

跳转: 2179. 统计数组中好三元组数目

灵茶山艾府题解

问题:

给你两个下标从 0 开始且长度为 n 的整数数组 nums1nums2 ,两者都是 [0, 1, ..., n - 1]排列

好三元组 指的是 3互不相同 的值,且它们在数组 nums1nums2 中出现顺序保持一致。换句话说,如果我们将 pos1v 记为值 vnums1 中出现的位置,pos2v 为值 vnums2 中的位置,那么一个好三元组定义为 0 <= x, y, z <= n - 1 ,且 pos1x < pos1y < pos1zpos2x < pos2y < pos2z 都成立的 (x, y, z)

请你返回好三元组的 总数目

思路:

这题直接做是找公共子序列个数,因为都是 0 到 n-1 的排序 ,所以可以通过映射转化为求递增子序列问题(获得一个索引哈希,另一个顺序枚举中间,看看之前有多少比自己小的合法值,就能算出之后有多少比自己大的合法值)
知道前面合法的 l e s s y less_y lessy,后面合法的就是 n − 1 − y − ( i − l e s s y ) n−1−y−(i−less_y) n1y(ilessy)
合记:在这里插入图片描述

但哪怕使用前缀和,如果直接暴力,也会超时
所以需要使用树状数组这种特殊的数据结构来计算合法前缀和

复杂度:

  • 时间复杂度: O ( n l o g n ) O(nlog_n) O(nlogn)
  • 空间复杂度: O ( n ) O(n) O(n)

代码:

class FenwickTree {private final int[] tree;public FenwickTree(int n) {tree = new int[n + 1]; // 使用下标 1 到 n}// a[i] 增加 val// 1 <= i <= npublic void update(int i, long val) {for (; i < tree.length; i += i & -i) {tree[i] += val;}}// 求前缀和 a[1] + ... + a[i]// 1 <= i <= npublic int pre(int i) {int res = 0;for (; i > 0; i &= i - 1) {res += tree[i];}return res;}
}class Solution {public long goodTriplets(int[] nums1, int[] nums2) {int n = nums1.length;int[] p = new int[n];for (int i = 0; i < n; i++) {p[nums1[i]] = i;}long ans = 0;FenwickTree ft = new FenwickTree(n);for(int i=0;i<n;i++) {int y = p[nums2[i]];long less = ft.pre(y);ans+=less*(n-1-y-i+less);ft.update(y+1,1);}return ans;}
}

307. 区域和检索 - 数组可修改(树状数组)

跳转: 307. 区域和检索 - 数组可修改

学习: 动画讲解讲解

问题:

给你一个数组 nums ,请你完成两类查询。

  1. 其中一类查询要求 更新 数组 nums 下标对应的值
  2. 另一类查询要求返回数组 nums 中索引 left 和索引 right 之间( 包含 )的nums元素的 ,其中 left <= right

实现 NumArray 类:

  • NumArray(int[] nums) 用整数数组 nums 初始化对象
  • void update(int index, int val)nums[index] 的值 更新val
  • int sumRange(int left, int right) 返回数组 nums 中索引 left 和索引 right 之间( 包含 )的nums元素的 (即,nums[left] + nums[left + 1], ..., nums[right]

思路:

lowbit
对于更新操作,通过索引可以定位到其对应的最底层,然后直接向上修改即可.上层在后面,每次向上刚好是加上bit最左位 lowbit.
查询操作也是先定位底层,不过是要加前缀,所以每次向上是减去bit最左位 lowbit.

复杂度:

  • 时间复杂度: O ( n ) O(n) O(n)
  • 空间复杂度: O ( n ) O(n) O(n)

代码:

class NumArray {private final int[] tree;private final int[] nums;public NumArray(int[] nums) {int n= nums.length;this.nums = nums;tree = new int[n+1];for(int i=1;i<=n;i++){tree[i]+=nums[i-1];int nxt = i+(i&-i);if(nxt<=n) tree[nxt] += tree[i];}}public void update(int index, int val) {int delta = val - nums[index];nums[index] = val;for(int i=index+1;i<tree.length;i+=i&-i){tree[i] += delta;}}private int prefixSum(int i) {int s = 0;for (; i > 0; i &= i - 1) { // i -= i & -i 的另一种写法s += tree[i];}return s;}public int sumRange(int left, int right) {return prefixSum(right+1)-prefixSum(left);}
}

122. 买卖股票的最佳时机 II

跳转: 122. 买卖股票的最佳时机 II

学习: 灵茶山艾府题解 & 公开讲解

问题:

给你一个整数数组 prices ,其中 prices[i] 表示某支股票第 i 天的价格。

在每一天,你可以决定是否购买和/或出售股票。你在任何时候 最多 只能持有 一股 股票。你也可以先购买,然后在 同一天 出售。

返回 你能获得的 最大 利润

思路:

可以考虑股票持有和未持有两种状态,每种状态都可以基于前一天持有或未持有变化而来
这题如果递归必须要加记忆化搜索

复杂度:

  • 时间复杂度: O ( n ) O(n) O(n)
  • 空间复杂度: O ( 1 ) O(1) O(1)

代码(递推):

class Solution {public int maxProfit(int[] prices) {int n = prices.length;int p1 = 0;int p2 = Integer.MIN_VALUE;for(int i=1;i<n;i++){int tmp = p1;p2 = Math.max(p1-prices[i-1],p2);p1 = Math.max(p2+prices[i-1],tmp);// System.out.println(p1+" "+p2);}return Math.max(p2+prices[n-1],p1);}
}

复杂度:

  • 时间复杂度: O ( n ) O(n) O(n)
  • 空间复杂度: O ( n ) O(n) O(n)

代码(递归):

class Solution {private int[][] memo;public int dfs(int[] prices,int day,int choose){if(day==0){return choose==1?-100000:0;} if(memo[day][choose]!=-1)return memo[day][choose];int a = dfs(prices,day-1,0);int b = dfs(prices,day-1,1);if(choose==1){return memo[day][1] =  Math.max(a-prices[day-1],b);}else{return memo[day][0] = Math.max(b+prices[day-1],a);}}public int maxProfit(int[] prices) {int n = prices.length;memo = new int[n+1][2];for(int[] row:memo){Arrays.fill(row,-1);}return dfs(prices,n,0);}
}

309. 买卖股票的最佳时机含冷冻期

跳转: 309. 买卖股票的最佳时机含冷冻期

学习: 灵茶山艾府题解 & 公开讲解

问题:

给定一个整数数组prices,其中第 prices[i] 表示第 *i* 天的股票价格 。

设计一个算法计算出最大利润。在满足以下约束条件下,你可以尽可能地完成更多的交易(多次买卖一支股票):

  • 卖出股票后,你无法在第二天买入股票 (即冷冻期为 1 天)。

**注意:**你不能同时参与多笔交易(你必须在再次购买前出售掉之前的股票)。

思路:

可以考虑股票持有和未持有两种状态,每种状态都可以基于前一天持有或未持有变化而来
这题如果递归必须要加记忆化搜索
这题就是比上题多个冷冻期,考虑买入时不能是刚卖出即可

如果写递推,因为设计前天,需要三个变量

复杂度:

  • 时间复杂度: O ( n ) O(n) O(n)
  • 空间复杂度: O ( 1 ) O(1) O(1)

代码(递推):

class Solution {public int maxProfit(int[] prices) {int n = prices.length;int p1 = 0;int pre_p1 = 0;int p2 = Integer.MIN_VALUE;for(int i=0;i<n;i++){int tmp = p1;p2 = Math.max(pre_p1-prices[i],p2);p1 = Math.max(p2+prices[i],tmp);pre_p1 = tmp;// System.out.println(p1+" "+p2);}return p1;}
}

复杂度:

  • 时间复杂度: O ( n ) O(n) O(n)
  • 空间复杂度: O ( n ) O(n) O(n)

代码(递归):

class Solution {private int[][] memo;public int dfs(int[] prices, int day, int choose) {if (day < 0) {return choose == 1 ? -100000 : 0;}if (memo[day][choose] != -1)return memo[day][choose];if (choose == 1) {return memo[day][1] = Math.max(dfs(prices, day - 2, 0) - prices[day], dfs(prices, day - 1, 1));}return memo[day][0] = Math.max(dfs(prices, day - 1, 1) + prices[day], dfs(prices, day - 1, 0));}public int maxProfit(int[] prices) {int n = prices.length;memo = new int[n+1][2];for (int[] row : memo) {Arrays.fill(row, -1);}return dfs(prices, n-1, 0);}
}

188. 买卖股票的最佳时机 IV

跳转: 188. 买卖股票的最佳时机 IV

学习: 灵茶山艾府题解 & 公开讲解

问题:

给你一个整数数组 prices 和一个整数 k ,其中 prices[i] 是某支给定的股票在第 i 天的价格。

设计一个算法来计算你所能获取的最大利润。你最多可以完成 k 笔交易。也就是说,你最多可以买 k 次,卖 k 次。

**注意:**你不能同时参与多笔交易(你必须在再次购买前出售掉之前的股票)。

思路:

可以考虑股票持有和未持有两种状态,每种状态都可以基于前一天持有或未持有变化而来
这题如果递归必须要加记忆化搜索
这题限制交易次数,所以需要额外加一维状态标识

复杂度:

  • 时间复杂度: O ( n ) O(n) O(n)
  • 空间复杂度: O ( 1 ) O(1) O(1)

代码(迭代):

class Solution {public int maxProfit(int k, int[] prices) {int[][] f = new int[k + 2][2];for (int j = 0; j <= k + 1; j++) {f[j][1] = Integer.MIN_VALUE / 2;}for (int p : prices) {for (int i=k+1;i>0;i--) {f[i][1] = Math.max(f[i][0] - p, f[i][1]);f[i][0] = Math.max(f[i-1][1] + p, f[i][0]);// System.out.println(p1+" "+p2);}}return f[k+1][0];}
}

复杂度:

  • 时间复杂度: O ( n ) O(n) O(n)
  • 空间复杂度: O ( n ) O(n) O(n)

代码(递归):

class Solution {private int[][][] memo;public int dfs(int[] prices, int day, int choose,int k) {if(k<0) return -100000;if (day < 0) {return choose == 1 ? -100000 : 0;}if (memo[day][k][choose] != -1)return memo[day][k][choose];if (choose == 1) {return memo[day][k][1] = Math.max(dfs(prices, day - 1, 0,k) - prices[day], dfs(prices, day - 1, 1,k));}return memo[day][k][0] = Math.max(dfs(prices, day - 1, 1,k-1) + prices[day], dfs(prices, day - 1, 0,k));}public int maxProfit(int k, int[] prices) {int n = prices.length;memo = new int[n][k+1][2];for (int[][] row : memo) {for(int[] row2 : row)Arrays.fill(row2, -1);}return dfs(prices, n-1, 0,k);}
}

总结

今天继续练习二叉树,复习了平衡二叉树和累加树
了解了树状数组和状态机动规的思路

往期打卡

代码随想录算法训练营第十七天

代码随想录算法训练营周末三

代码随想录算法训练营第十六天

代码随想录算法训练营第十五天

代码随想录算法训练营第十四天

代码随想录算法训练营第十三天

代码随想录算法训练营第十二天

代码随想录算法训练营第十一天

代码随想录算法训练营周末二

代码随想录算法训练营第十天

代码随想录算法训练营第九天

代码随想录算法训练营第八天

代码随想录算法训练营第七天

代码随想录算法训练营第六天

代码随想录算法训练营第五天

代码随想录算法训练营周末一

代码随想录算法训练营第四天

代码随想录算法训练营第三天

代码随想录算法训练营第二天

代码随想录算法训练营第一天

相关文章:

  • redisson分布式锁--实际应用!!!
  • 决策树简介
  • redis -- redis介绍,性能(与mysql性能对比),使用场景,CAP介绍
  • gravity`(控制 View 内部内容的对齐方式)
  • Hikyuu C++与Python层交互机制
  • Vue 3中的setup【与Vue 2的区别】
  • 深度学习--深度学习概念、框架以及构造
  • GIT工具学习【1】:新安装git预操作
  • candence17.4原理图编号
  • 你了解哪些Java限流算法?
  • 深入解析操作系统的文件系统:从存储介质到数据管理的核心引擎
  • 猿辅导集团推首个教育AI范式小猿AI 聚焦家校应用场景发布3款新品
  • VGA显示
  • 【euclid】10.2 2D变换模块(transform2d.rs)Arbitrary trait
  • 蓝桥杯嵌入式十六届赛前复习总结与准备
  • linux运维篇-Ubuntu(debian)系操作系统创建源仓库
  • 基本元器件—电阻器(2025.4.14)
  • 实现表单验证
  • YOLOv2 快速入门与核心概念:更快、更准的目标检测利器
  • tap交换机配置步骤
  • 伊朗外长: 下一轮伊美核问题谈判将于26日举行
  • 全国首家由司法行政部门赋码登记的商事调解组织落户上海
  • 杭州:调整个人购买家庭住房享受契税优惠住房套数查询规则
  • 霸王茶姬成美股“中国茶饮第一股”:首日涨近16%,市值60亿美元
  • 虎门站一旅客跳下股道,广铁集团发布情况说明
  • 共享单车、公共设施表面等频现“小广告”?上海将出手治理