如何用伪代码实现二叉树路径上的结点最大乘积

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了如何用伪代码实现二叉树路径上的结点最大乘积相关的知识,希望对你有一定的参考价值。

请提供一个思路,思路就可以!这题中的路径很宽泛,比如说一个结点就可以是一个路径,那么乘积就是结点值本身,1到0也可以是一个路径,所以乘积是1*0=0,以此类推,所有可能路径上结点的乘积最大是多少

树形DP

设f[i]表示点i的子树中,一条以i结尾的乘积为正最大链

设g[i]表示点i的子树中,一条以i结尾的乘积为负的最小链

设dp[i]表示点i的子树中的最大链

对于叶节点i,显然有f[i]=dp[i]=1;

g[i]不存在

对于非叶节点i,如果i是正数

点i自成一链,也可以与f[l]或者f[r]连接起来,得到f[i](l,r表示左右孩子)

点i与g[l]或者g[r]连接起来,可以得到g[i]

对于非叶节点i,如果i是负数

点i自成一链,也可以与f[l]或者f[r]连接起来,得到g[i](l,r表示左右孩子)

点i与g[l]或者g[r]连接起来,可以得到f[i]

而dp[i]可以是左子树最大链,也可以是右子树最大链,还可能是两个子树合并的最大链,也可能就是f[i]

综上所述

如果val[i]>0   

f[i]=max(val[i],val[i]*f[l],val[i]*f[r]);
g[i]=max(val[i]*g[l],val[i]*g[r]);
dp[i]=max(dp[l],dp[r],f[l]*f[r]*val[i],g[l]*g[r]*val[i],f[i])

如果val[i]<0  

g[i]=max(val[i],val[i]*f[l],val[i]*f[r]);
f[i]=max(val[i]*g[l],val[i]*g[r]);
dp[i]=max(dp[l],dp[r],f[l]*g[r]*val[i],g[l]*f[r]*val[i],f[i])

还有一些情况需要注意

另开两个数组记录f[i]或g[i]不存在的情况,转移式随之变化

按照转移方程,dfs一下就可以了

参考技术A 为什么g[i] =max(val[i]*g[l],val[i]*g[r]),为什么不是g[i] =min(val[i]*g[l],val[i]*g[r]), 按照你给的定义:设g[i]表示点i的子树中,一条以i结尾的乘积为负的最小链 最小链的话,应该是最小的,求解~ 参考技术B 我是墨大的老师,你这是在玩火!

复习并查集思想_二叉树最大深度问题_几种做法_并查集/递归/递推/最长路径_多种办法实现

A: 二叉树的最大深度

题目描述

二叉树是指每个结点最多有两个子树的树结构,这两个子树通常子树被称作“左子树”(left subtree)和“右子树”(right subtree)。
二叉树的结点层定义为:根结点的层定义为 1 ,根的孩子为第二层结点,依此类推;
二叉树的深度定义为:树中最大的结点层。
给定一颗以1号结点为根的二叉树,求它的最大深度。

输入描述

每组数据包含一颗二叉树,以如下形式给出:
第一行为一个整数 n ( 1 ≤ n ≤ 20 ) ,表示二叉树的结点个数,结点编号为 1~n 。
接下来直到输入数据末尾,每行为两个整数 x 和 y ,表示 x 和 y 之间有一条边,且 y 是 x 的儿子。
输入保证是一颗合法的二叉树。

输出描述

输出一行,表示给定的二叉树的最大深度。

样例输入

3
1 2
2 3

样例输出

3

 思路:

这个题一开始想着用模拟来解决,但是后来发现,这样做非常麻烦,因为你不知道怎么表示,链表不是顺序储存结构,访问是不能通过序列编号直接访问的,

而且给定的编号并不一定是规则的从上到下恰好从小到大(之前想用排序做但是WA了),除了第一个节点一定是1号之外 ,没有其他的限制

但是,这题给的数据量是20,也就是说如果我每个结点都往上找到底不管怎样绝对不会超时或者爆炸,最大也就20*20=400,可以接受深搜,可以最长路径解决

除了求图路径的办法,dp递推式,它的输入方法很神奇,x与y,很像并查集,

 

 

复习一下,一般并查集:

——并查集——是利用数组下标 / 数组值嵌套—形成递归结构,从而实现

比较全的想法整理:https://blog.csdn.net/yjr3426619/article/details/82315133,题集:https://www.cnblogs.com/cyanigence-oi/p/11774190.html

init() find()union()为基础操作;模板如下:

技术图片
 1 int fa[MAXN];
 2 inline void init(int n) {
 3     for (int i = 1; i <= n; ++i)
 4         fa[i] = i;
 5 }
 6 
 7 int find(int x) {
 8     if(fa[x] == x)
 9         return x;
10     else
11         return find(fa[x]);
12 }
13 
14 inline void merge(int i, int j) {
15     fa[find(i)] = find(j);
16 }
View Code

如果有权值,则多设一个权值变量数组,合并操作

 

不过,这题更简单,和一般并查集不一样的地方在于,我们知道这些节点一定是在同1块连通区域内的,而且本题对求路径长度的时间没有太多要求,

因此,并查集中 init() find()union()的操作中,union可以被省略掉,

 

那么简单利用数组下标和实际值嵌套,(更新路径长的并查集)实现递归,就可以解决了(以下为简易版本)

技术图片
 1 #include <iostream>
 2 #include <cstring>
 3 #include <queue>
 4 #include <cstdio>
 5 #include <cmath>
 6 #include <map>
 7 #include <algorithm>
 8 typedef long long ll;
 9 using namespace std;
10 int n;
11 int len=0;
12 #define o NULL
13 //最深不会超过n,
14 
15 
16 int a[25];
17 int m=0;
18 void f(int i) {
19     if(a[i]==i) {
20         len++;
21         if(len>m)m=len;
22         return;
23     }
24     if(a[i]!=i) {
25         len++;
26         f(a[i]);
27     }
28 
29 }
30 
31 int main() {
32     cin>>n;
33     int x,y;
34 
35     for(int i=1; i<=n; i++) {
36         a[i]=i;
37     }
38 
39     while(cin>>x>>y) {
40         a[y]=x;
41         len=0; 
42         f(y);
43         //不用a[x]=y;
44         //是因为父亲可以有多个儿子,但是儿子只有一个父亲,需要利用唯一关系
45     }
46     printf("%d
",m);
47 }
View Code

复杂版本——带权并查集

技术图片
1 int find(int x) {
2     if (x == pre[x])  return x;
3     else {
4         int root = find(pre[x]);     // 找到根节点
5         sum[x] += sum[pre[x]];       // 权值合并,更新
6         return pre[x] = root;        // 压缩路径
7     }
8 }
9 //初始权值都为1,一个节点表示深度=1
View Code

这是升级版的———利用并查集求解路径长/ 深度的问题

https://blog.csdn.net/qq_33204081/article/details/77189674

进一步考虑到深度可以迭代实现递推的关系 len【儿子】=len【父亲】+1,更简单的做法: 

技术图片
 1 #include <iostream>
 2 #include <cstring>
 3 #include <queue>
 4 #include <cstdio>
 5 #include <cmath>
 6 #include <map>
 7 #include <algorithm>
 8 typedef long long ll;
 9 using namespace std;
10 int n;
11 int len=0;
12 #define o NULL
13 //最深不会超过n,
14 
15 int m=0;
16 int l[25];
17 
18 int main() {
19     cin>>n;
20     int x,y;
21     for(int i=1; i<=n; i++) {
22         l[i]=1;
23     }
24     while(cin>>x>>y) {
25         l[y]=l[x]+1;
26         if(m<l[y])m=l[y];
27         //不用a[x]=y;
28         //是因为父亲可以有多个儿子,但是儿子只有一个父亲,需要利用唯一关系
29     }
30     printf("%d
",m);
31 }
View Code

 

 

也可以化成纯图论问题——求最长路径(待续更新)

 

 

 

 

以上是关于如何用伪代码实现二叉树路径上的结点最大乘积的主要内容,如果未能解决你的问题,请参考以下文章

数据结构-如何用二叉树实现hash表(C语言)

复习并查集思想_二叉树最大深度问题_几种做法_并查集/递归/递推/最长路径_多种办法实现

请问如何用随机函数生成二叉树,并遍历?

二叉树的遍历

数据结构与算法-求给定二叉树的最大深度

用层序生成二叉树