当前位置:网站首页>剑指offer专项突击版第17天
剑指offer专项突击版第17天
2022-08-02 09:14:00 【hys__handsome】
容易想到 O ( N 2 ) O(N^2) O(N2)的暴力遍历算法,本质上跟for双重循环暴力遍历很类似,就是实现代码需要的技巧较多也比较新颖。
class Solution {
public:
//保证root下每种路径中的每个结点都会选取,不会有路径中间存在没选取的结点
int dfs(TreeNode *root, int targetSum) {
if(!root) return 0;
int cnt = 0;
if(root->val == targetSum) cnt++;
cnt += dfs(root->left, targetSum - root->val);
cnt += dfs(root->right, targetSum - root->val);
return cnt;
}
int pathSum(TreeNode* root, int targetSum) {
if(!root) return 0;
//选取root点
int ans = dfs(root,targetSum);
//不选root点
ans += pathSum(root->left, targetSum);
ans += pathSum(root->right, targetSum);
return ans;
}
};
仔细思考会发现,这里面有很多重复计算的,可以使用前缀和优化成 O ( N ) O(N) O(N) 时间复杂度,其中前缀和值放在map的键上,值则为个数,这样就不用重新遍历一遍之前的前缀和,严谨的来讲因为用到map就导致算法复杂度为 O ( N ∗ l o g N ) O(N*logN) O(N∗logN)。
class Solution {
private:
unordered_map<long long, int> prefix;
// int ans = 0;
public:
int dfs(TreeNode *root, long long sum, int targetSum) {
if(!root) return 0;
sum += root->val;
int ans = 0;
if(prefix.count(sum-targetSum)) {
//sum - x = targetSum,其中x就是前面某个前缀和
ans += prefix[sum-targetSum];
}
prefix[sum]++;
ans += dfs(root->left, sum, targetSum);
ans += dfs(root->right, sum, targetSum);
prefix[sum]--; //回溯,不然会用到其他分支的结点之和
return ans;
}
int pathSum(TreeNode* root, int targetSum) {
prefix[0] = 1; //什么都没选,当某条从root开始的路径结点之和等于targetSum时会用到
return dfs(root, 0, targetSum);
}
};
使用到后序遍历+DP思想
- 后序遍历是先求左右子路径的最大值
- DP思想是当前栈帧会计算当前结点+左子路径+右子路径(这个不能回溯给父节点的,因为会使得路径不是线性的),不管你要回溯给父节点当前结点、当前+左子路径、当前+右子路径,最终得到的是全局最大的路径和。
class Solution {
private:
int res = INT_MIN;
public:
int maxPathSum(TreeNode* root) {
maxGain(root);
return res;
}
int maxGain(TreeNode *root) {
if(root == nullptr) return 0;
int left = maxGain(root->left);
int right = maxGain(root->right);
res = max(res, root->val); //考虑左右路径都为负值
res = max(res, root->val + left);
res = max(res, root->val + right);
res = max(res, root->val + left + right);
//回溯只能返回当结点值 或 当前+左 或 当前+右
return max(max(root->val,root->val+left), root->val+right);
}
};
代码简化版,左右为负值时不如不选,就为0
class Solution {
private:
int res = INT_MIN;
public:
int maxPathSum(TreeNode* root) {
maxGain(root);
return res;
}
int maxGain(TreeNode *root) {
if(root == nullptr) return 0;
//左右为负值时不如不选,就为0
int left = max(0,maxGain(root->left));
int right = max(0,maxGain(root->right));
res = max(res, root->val + left + right);
//回溯只能返回当结点值 或 当前+左 或 当前+右
return max(left,right) + root->val;
}
};
最容易想到的方法,先存下中序遍历序列,然后按照序列顺序指定每个结点的右结点,这样就等同于创建一个链表
class Solution {
public:
void inorder(vector<TreeNode*> &ls, TreeNode *root) {
if(!root) return;
inorder(ls,root->left);
ls.emplace_back(root);
inorder(ls,root->right);
}
TreeNode* increasingBST(TreeNode* root) {
vector<TreeNode*> ls;
inorder(ls,root);
auto head = new TreeNode(-1);
auto tmp = head;
for(auto cur: ls) {
cur->left = cur->right = nullptr;
tmp->right = cur;
tmp = cur;
}
return head->right;
}
};
来一个难一些的方法,一边遍历一边改变结点指针方向。抓住中序遍历算法->遍历顺序不变的特性。然后用一个移动指针跟着遍历顺序移动。(有个坑,移动指针不能放函数参数里,必须全局变量,因为递归回溯过程中,移动指针还是原来的并没有改变)
class Solution {
private:
TreeNode *cur = nullptr;
public:
void inorder(TreeNode *root) {
if(!root) return;
inorder(root->left);
root->left = nullptr;
cur->right = root;
cur = cur->right;
inorder(root->right);
}
TreeNode* increasingBST(TreeNode* root) {
auto head = new TreeNode(-1);
cur = head;
inorder(root);
return head->right;
}
};
边栏推荐
- (Note) AXIS ACASIS DT-3608 Dual-bay Hard Disk Array Box RAID Setting
- 高效时代,电商运营如何靠RPA快速提效?
- Jetpack Compose 中的状态管理
- 自定义卡包效果实现
- LeetCode第三题(Longest Substring Without Repeating Characters)三部曲之一:解题思路
- Overview of Edge Computing Open Source Projects
- UVM信息服务机制
- Scala类型转换
- 单机部署flink,创建oracle19c rac的连接表时报错 ORA-12505 ,怎么回事?
- AutoJs学习-AES加解密
猜你喜欢

1对1视频源码——快速实现短视频功能提升竞争力

Jenkins--基础--6.2--Pipeline--语法--声明式

稳定币:对冲基金做空 Tether 的结局会是什么?

曲折的tensorflow安装过程(Tensorflow 安装问题的解决)

百战RHCE(第四十七战:运维工程师必会技-Ansible学习2-Ansible安装配置练习环境)

HCIP笔记第十三天

postman下载安装汉化及使用

postman使用方法

pycharm的基本使用教程(1)

Have you ever learned about these architecture designs and architecture knowledge systems?(Architecture book recommendation)
随机推荐
十、 网络管理
高效时代,电商运营如何靠RPA快速提效?
Daily practice of dynamic programming (3)
不用Swagger,那我用啥?
练习40,小蓝的旅行【最短路】
nacos项目搭建
PyCharm使用教程(较详细,图+文)
Spend 2 hours a day to make up for Tencent T8, play 688 pages of SSM framework and Redis, and successfully land on Meituan
day_05模块
数据库mysql
记某社区问答
二分类和多分类
Jenkins--基础--6.3--Pipeline--语法--脚本式
PyCharm usage tutorial (detailed version - graphic and text combination)
【并发编程】- 线程池使用DiscardOldestPolicy策略、DiscardPolicy策略
堪称神级的阿里巴巴“高并发”教程《基础+实战+源码+面试+架构》
The god-level Alibaba "high concurrency" tutorial "basic + actual combat + source code + interview + architecture"
了解下C# 多线程
HCIP笔记十六天
RetinaFace: Single-stage Dense Face Localisation in the Wild