こんにちは
今回の学びは
・再帰でもO(n)ならいい感じだよ。つまり実装してOK。
問題は。。。
まずは入出力をしっかりおさえましょう。
入力は空っぽではないTreeNodeです、ということはnullチェックもしなくていいし、そのノードが一個だけのノードだったらそのままそののノードを値を答えとして返せるわけです
出力は数字で最大値だけなので、intergerですね 得にノードをかえす必要はないようです
他にきになる問題文はありますか?
もちろん勘違いしてはいけないのはrootを通る必要はない、ということです。
それではってみよう!
実装前後でこれは毎回書きましょう。timespace anaylysis (時間とスペースの分析)は
bfsで各ノードをスタートとしてすべての点をカバーしていくのはO(n**2)です
多分最適は操作パス一発のO(n)でいけるんじゃないかって想像します
言うは易し、実際のコードがおもいつきませんね。
マイナスにぶち当たるまで、bfsで。ぶち当たっtら、まだ検索していないノードまでdfsでとんで繰り返して終了はどうだろう。
マイナス飛ばすだけでいけるのかなー。サンプルケースでもし左ノードが9ではなく11以上だれば、マイナスをたどってもくっつけてしまったほうが、おおきいので、否。
では、これはどうだろう。再帰的にかんがえる。
まず、左サブ木の最大を求める。同様に右サブ木の最大も求める。最後にそのどちらかと、接続部分の値との合計を比較する。
このアルゴはサンプルケースではいけそうだ。
適当に他のサンプルを作ってみよう
pseudocode(擬似コード)をかいてみますか。
def maxPathSum(self, node: TreeNode) -> int: maxl = self.maxPathSum(node.left) maxr = self.maxPathSum(node.right) return max(maxl, maxr) + node.val
上の構想は素晴らしいものの
細部に注意しないとハマってしまう。今回のように。
実際の実装はこちら
class Solution:
def printAll(self, root: TreeNode):
root.p()
def __init__(self):
self.maxi = -sys.maxsize - 1
def rec(self, node: TreeNode) -> int:
if node == None: return -sys.maxsize - 1
max_left = self.rec(node.left)
max_right = self.rec(node.right)
x = max(
node.val,
max_left, max_right,
max_left + node.val, max_right + node.val,
max_left + max_right + node.val)
# print("current max is %s" % x)
# print("max_left %s, max_right %s, node.val %s" % (max_left, max_right, node.val))
self.maxi = max(self.maxi, x)
if node.val < 0:
return max(max_left + node.val, max_right + node.val)
else:
return x
def maxPathSum(self, node: TreeNode) -> int:
self.maxi = -sys.maxsize - 1
self.rec(node)
return self.maxi
既に、1、2回、提出エラーがあったので改良をくわえている。
リターンの場合分けと、最終的にself.maxiを返すということと、nodeがからの時は0ではなくminimum値(マイナスで最小値)を返すという点だ。
そこまでしても通らない。上のコードでは拾はleft + right + val という部分がじつはおかしいのだ。
最後はそこを修正して...
一筋なわでは行かなくなって、スパゲッティになってきました。
そういう時は、深呼吸して
図を書いたりしたり、テストケース増やしたりして行くしかないと思います
私の場合も通らなかった、以下2つを通すためには
冷静に分岐条件を考える必要がありました。
root = stringToTreeNode("[1,-2,-3,1,3,-2,null,-1]")
Solution().printAll(root)
print("maxDepth:%s" % Solution().maxPathSum(root))
root = stringToTreeNode("[5,4,8,11,null,13,4,7,2,null,null,null,1]")
Solution().printAll(root)
print("maxDepth:%s" % Solution().maxPathSum(root))
両方ともリートコードで提出して始めてエラーになりわかった問題ですので
実装しただけでは到底きづけません。
最終的にには
self.maxi = max(self.maxi, x, max_left + max_right + node.val)
のように、左と右とノードをmaxiとしては候補にあげるが、後ろに伝えわしない
if node.val < 0:
return max(max_left + node.val, max_right + node.val)
else:
return x
というのが味噌である。
class Solution:
def printAll(self, root: TreeNode):
root.p()
def __init__(self):
self.maxi = -sys.maxsize - 1
def rec(self, node: TreeNode) -> int:
if node == None: return -sys.maxsize - 1
max_left = self.rec(node.left)
max_right = self.rec(node.right)
x = max(
node.val,
max_left, max_right,
max_left + node.val, max_right + node.val)
self.maxi = max(self.maxi, x, max_left + max_right + node.val)
if node.val < 0:
return max(max_left + node.val, max_right + node.val)
else:
return x
def maxPathSum(self, node: TreeNode) -> int:
self.maxi = -sys.maxsize - 1
self.rec(node)
return self.maxi
実装後です。timespace anaylysis (時間とスペースの分析)は n回再帰をかけるわけですが毎回単純な比較しかしてない
のでO(n)です。スペースは再帰文のo(n)です
はい!なんとか自力でとけました。
初めてのhard問題で解けたのでこれまた嬉しかったです

まとめ
再帰でもO(n)ならいい感じだよ。つまり実装してOK。
以上です