当前位置:网站首页>剑指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;
}
};
边栏推荐
- Analysis of software testing technology How far is Turing test from us
- Spend 2 hours a day to make up for Tencent T8, play 688 pages of SSM framework and Redis, and successfully land on Meituan
- 瑞吉外卖项目剩余功能补充
- 稳定币:对冲基金做空 Tether 的结局会是什么?
- [Must read] Mylander valuation analysis, electrical stimulation products for pelvic and postpartum rehabilitation
- 四字节的float比八字结的long范围大???
- 每天花2小时恶补腾讯T8纯手打688页SSM框架和Redis,成功上岸美团
- node制作一个视频帧长图生成器
- 大厂外包,值得拥有吗?
- 【打新必读】麦澜德估值分析,骨盆及产后康复电刺激产品
猜你喜欢
膜拜,Alibaba分布式系统开发与核心原理解析手册
谈谈对Volatile的理解
Spend 2 hours a day to make up for Tencent T8, play 688 pages of SSM framework and Redis, and successfully land on Meituan
大厂外包,值得拥有吗?
Redis数据结构
OneinStack多版本PHP共存
AutoJs学习-实现谢尔宾斯基三角
在 QT Creator 上配置 opencv 环境的一些认识和注意点
Detailed explanation of calculation commands in shell (expr, (()), $[], let, bc )
pnpm:简介
随机推荐
谈谈对Volatile的理解
动态规划每日一练(2)
自定义卡包效果实现
Openwrt_树莓派B+_Wifi中继
shell脚本
pnpm的安装与使用
node制作一个视频帧长图生成器
PyQt5(一) PyQt5安装及配置,从文件夹读取图片并显示,模拟生成素描图像
next permutation
Have you ever learned about these architecture designs and architecture knowledge systems?(Architecture book recommendation)
Jenkins--基础--5.4--系统配置--全局工具配置
主流监控系统工具选型及落地场景参考
Installation and use of pnpm
练习40,小蓝的旅行【最短路】
四字节的float比八字结的long范围大???
day1-机器学习-回归问题
了解下C# 不安全代码
【微信小程序】本地服务页面案例实现
百战RHCE(第四十六战:运维工程师必会技-Ansible学习1-基础知识讲解)
pnpm:简介