关于树的算法的总结

之前总结过,主要分为两大类:

一种是基于遍历的,算法框架就是前序遍历,只是在访问结点时候的操作不同。一般操作的都是全局变量,靠side effect,没有返回值。

一种是基于传统递归思想的,先求左右子树的解(子问题的解),然后基于左右子树的解计算当前树的解。这种函数有返回值。


其实还有第三种,就是以上二者的结合,既有返回值,又操作全局数据。基于第二种算法,不同之处在于左右子树的解除了被用于计算当前树的解之外,还可以额外利用,进行一些判断,更新一些全局变量。


比如判断一棵树是否平衡,需要求树的高度,而树的高度是一个递归函数,h(root) = max(h(root->left), h(root->hight))+1, 自底向上的过程中求过每一棵子树的左右子树的高度,就可以判断子树是否平衡,如果任意一处子树不平衡,就可以设置全局变量,得到结果。这个函数的框架就是一个求高度的递归函数,多了2个操作:

1)判断两棵子树的高度差是否大于1,如果大于,设置全局变量。

2)提前终止递归,如果求左子树的过程中已经设置了全局变量为false, 那么就没必要继续求右子树了。


当然,有时候可以把这个全局变量和函数返回值融合到一起,比如求高度,正常总是非负的,就可以用一个负数表征全局变量代表的意思。这时候这个求高度的递归函数定义是:如果树是平衡的,返回树的高度,否则返回-1。


还有一个例子,求树中一般路径(任意两点之间)最大和,普通路径(从一点往下的路径,不会跨越根结点到另一边)最大和的递推式是 f(root)= max(f(root->left),f(root-right)) >0 ? max(f(root->left),f(root->right)): 0 + root->val。算法的框架就是这个,只是加进去一般路径和的计算,然后和全局变量maxPathSum 比较,并保存。


返回值是复合含义的函数。

一般来讲,函数的返回值的含义是单一的,不会有多种解读。但有的函数返回值可以有多重含义。经典的LCA就是个例子。定义一个函数,f(root, a, b),如果 root是 a, b之一,返回root,如果a, b均不在根为root的树里,返回NULL,否则返回a, b的最近公共祖先。递推关系是:

1)如果 f(root->left, a, b) 和 f(root->right, a, b) 都为NULL,返回NULL

2)  如果 f(root->left, a, b) 和 f(root->right, a, b) 都不为NULL, 那么这两个返回值均为第一个含义(root 为 a, b之一),LCA必然为root, 返回root

3)  如果 f(root->left, a, b) 和 f(root->right, a, b) 其中一个为NULL, 那么另一个返回值的含义就是最近公共祖先,返回这一个值。


判断树是否平衡也是,可以定义一个复合含义返回值的递归函数,balancedHight(root), 如果这棵树平衡,返回其高度。否则返回-1。


九章算法后的更新:

第三种模式(即依赖返回值,又操作全局变量)可以归并到第二种模式。通过定义一个复合的返回类型Result,把需要的子问题的信息打包返回。比如isBST问题,需要左右子树上的信息包括,1)是否是BST 2) 最小值、最大值。可以把这三项数据定义一个类型来返回数据。再比如isBalanced问题,定义返回数据包括,1)子树是否平衡,2)子树高度,当前树根据左右子树上的这两个信息就可以做判断。


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值