113. 路径总和II
链接:https://leetcode-cn.com/problems/path-sum-ii/
题目描述见链接内容。
解法1:广度优先搜索
在广度优先搜索时,重新构造了一个queue
,原本的queue
只用来存放未遍历的节点,现在queue
的成员是一个对象,对象中包括了三个属性,node
用来存放未遍历的节点,val
存放遍历到当前节点前的历史路径求和,history
保存当前节点前的历史路径
var pathSum = function (root, targetSum) {
let result = [];
if (!root) {
return [];
}
const queue = [{node: root, val: 0, history: []}];
for (let i = 0; i < queue.length; i++) {
const {node, val, history} = queue[i];
if (!node.left && !node.right) {
if (node.val + val === targetSum) {
result.push([...history, node.val]);
}
}
if (node.left) {
queue.push({node: node.left, val: val + node.val, history: [...history, node.val]});
}
if (node.right) {
queue.push({node: node.right, val: val + node.val, history: [...history, node.val]});
}
}
return result;
};
- 时间复杂度:
${O(N)}$
,N
是二叉树节点树 - 空间复杂度:
${O(H)}$
,H
是二叉树深度,最坏情况是${ON)}$
,平均复杂度是${O(log N)}$
- 执行用时:96ms, 在所有JavaScript提交中击败了86%的用户,内存消耗:47.3MB,在所有JavaScript提交中击败了36%的用户
解法2:广度优先搜索2
实际上原理差不多,只是做了如下改动:
- 将历史路径求和单独用了一个队列
sumQueue
表示,与queue
同步更新 - 没有再保存历史路径,而是用一个
Map
保存了每一个节点的父节点,发现最终的节点是符合要求的节点时,通过Map
不断寻找父节点找出历史路径
var pathSum = function (root, targetSum) {
if (!root) {
return [];
}
const result = [];
const queue = [root];
const sumQueue = [0];
const map = new Map();
// 通过向上循环,找出父节点,返回历史路径
function getParentPath(node) {
const parentPath = [];
let current = node;
while (current) {
parentPath.unshift(current.val);
current = map.get(current);
}
return parentPath;
}
while (queue.length) {
const node = queue.shift();
const currentSum = sumQueue.shift();
if (!node.left && !node.right) {
if (node.val + currentSum === targetSum) {
result.push(getParentPath(node));
}
} else {
if (node.left) {
// 保存父节点
if (!map.get(node.left)) {
map.set(node.left, node);
}
queue.push(node.left);
// 更新求和队列
sumQueue.push(currentSum + node.val);
}
if (node.right) {
// 保存父节点
if (!map.get(node.right)) {
map.set(node.right, node);
}
queue.push(node.right);
// 更新求和队列
sumQueue.push(currentSum + node.val);
}
}
}
return result;
};
- 时间复杂度:
${O(N)}$
,N
是二叉树节点树 - 空间复杂度:
${O(N)}$
,N
是二叉树节点树 - 执行用时:112ms, 在所有JavaScript提交中击败了37%的用户,内存消耗:41.7MB,在所有JavaScript提交中击败了49%的用户
深度优先搜索
利用深度优先搜索时,内部的dfs
函数加入传递path
参数,每次在新的分支递归时,都会产生新的path
,在叶子节点处符合要求的话,将对应的path
放到结果中
var pathSum = function (root, targetSum) {
const result = [];
function dfs(node, sum, path = []) {
if (!node) {
return;
}
if (!node.left && !node.right) {
if (node.val === sum) {
result.push([...path, node.val]);
}
}
if (node.left) {
dfs(node.left, sum - node.val, [...path, node.val]);
}
if (node.right) {
dfs(node.right, sum - node.val, [...path, node.val]);
}
}
dfs(root, targetSum);
return result;
};
- 时间复杂度:
${O(N)}$
,N
是二叉树节点树 - 空间复杂度:
${O(N)}$
,N
是二叉树节点树 - 执行用时:96ms, 在所有JavaScript提交中击败了86%的用户,内存消耗:48.6MB,在所有JavaScript提交中击败了12%的用户