Approach (brief): Do a single post-order DFS that at each node computes the maximum downward path sum starting at that node (can be just the node, node+left_down, or node+right_down) plus the node-to-node best passing through this node (node alone, node+left_down, node+right_down, node+left_down+right_down). Track global best sum and the node sequence for that best. This is O(n) because each node is visited once and constant work is done per node. The naive O(n^2) approach recomputes path sums for overlapping subtrees repeatedly; memoizing via a single DFS removes that.Python implementation:python
class TreeNode:
def __init__(self, val, left=None, right=None):
self.val = val
self.left = left
self.right = right
def max_path_sum_with_nodes(root):
# global best: (sum, path_nodes_list)
best = {"sum": float("-inf"), "nodes": []}
def dfs(node):
if not node:
return (float("-inf"), []) # no downward path from null
# compute for children
l_sum, l_path = dfs(node.left)
r_sum, r_path = dfs(node.right)
# convert negative-downs into "ignore child" by treating as 0-length path
# but when all values are negative we must allow single node; handle with comparisons
l_down = l_sum if l_sum > 0 else float("-inf")
r_down = r_sum if r_sum > 0 else float("-inf")
# possible downward paths starting at node:
# 1) node alone
down_options = [(node.val, [node.val])]
if l_down != float("-inf"):
down_options.append((node.val + l_down, l_path + [node.val]))
if r_down != float("-inf"):
down_options.append((node.val + r_down, [node.val] + r_path))
# select best downward path (largest sum). If all children negative, node alone chosen.
down_sum, down_nodes = max(down_options, key=lambda x: x[0])
# possible best passing through node uses combinations:
candidates = []
candidates.append((node.val, [node.val]))
if l_down != float("-inf"):
candidates.append((node.val + l_down, l_path + [node.val]))
if r_down != float("-inf"):
candidates.append((node.val + r_down, [node.val] + r_path))
if l_down != float("-inf") and r_down != float("-inf"):
# join left -> node -> right: left path ends at node, right path starts at node
candidates.append((node.val + l_down + r_down, l_path + [node.val] + r_path))
# update global best
for s, nodes in candidates:
if s > best["sum"]:
best["sum"] = s
best["nodes"] = nodes
return down_sum, down_nodes
dfs(root)
return best["sum"], best["nodes"]
Key points and reasoning:- The DFS returns the best downward-sum and its node sequence so reconstruction is immediate; when combining left+node+right we concatenate left path + [node] + right path.- To handle all-negative trees we allow "node alone" as a candidate; using >0 gating for child contributions ensures we don't force negative children to reduce sums but still permit single-node negatives to be chosen.- Time complexity: O(n) — each node processed once, constant-time merges.- Space complexity: O(h) recursion stack + O(k) for returned node lists where k is path length (<= n); overall O(n) worst-case for skewed tree.- Edge cases: null root (return -inf or define as 0 per requirement), single-node tree, duplicate values, long skewed trees.