这是一道非常经典的题目,在《剑指offer》中也有收录,而且在各大博客中也有提及。
路径和的使用还是非常广泛的,因为操作简单,灵活性强。
首先我们要给出这种树的定义:
package com.mengzhidu.teach.algorithm.tree.demo;
/**
* Created by xinxing on 2019/4/11
*/
public class NumTreeNode {
private int value;
private NumTreeNode left;
private NumTreeNode right;
public NumTreeNode(int value, NumTreeNode left, NumTreeNode right) {
this.value = value;
this.left = left;
this.right = right;
}
public int getValue() {
return value;
}
public void setValue(int value) {
this.value = value;
}
public NumTreeNode getLeft() {
return left;
}
public void setLeft(NumTreeNode left) {
this.left = left;
}
public NumTreeNode getRight() {
return right;
}
public void setRight(NumTreeNode right) {
this.right = right;
}
}
然后我们为了方便后面的操作,我们给一个助手方法:
package com.mengzhidu.teach.algorithm.tree.demo;
/**
* Created by xinxing on 2019/4/12
*/
public class NumTreeNodeHelper {
/**
* 22
* / \
* 0 3
* / \
* -3 4
* / \
* -7 8
* @return
*/
public static NumTreeNode getNumberTreeNode() {
NumTreeNode aNode = new NumTreeNode(-7, null, null);
NumTreeNode bNode = new NumTreeNode(8, null, null);
NumTreeNode cNode = new NumTreeNode(4, aNode, bNode);
NumTreeNode dNode = new NumTreeNode(-3, null, null);
NumTreeNode eNode = new NumTreeNode(3, dNode, cNode);
NumTreeNode fNode = new NumTreeNode(0, null, null);
NumTreeNode root = new NumTreeNode(22, fNode, eNode);
return root;
}
}
我们这里给出的思路依然是通过递归的方式给出,其实大多数人见到这个题的第一个思路,就是它应该类似于前序遍历的方式来解决。
一般来说常规的思路是这样的:
package com.mengzhidu.teach.algorithm.tree.demo.path;
import com.mengzhidu.teach.algorithm.tree.demo.NumTreeNode;
import com.mengzhidu.teach.algorithm.tree.demo.NumTreeNodeHelper;
import java.util.ArrayList;
import java.util.List;
/**
* 获取所有的路径节点和列表
* 它在第一种方式上优化了一下空间开销
*/
public class PathSumDemo2 {
private static final List<NumTreeNode> currentList = new ArrayList<>();
private static final List<List<NumTreeNode>> allList = new ArrayList<>();
public static void main(String[] args) {
NumTreeNode node = NumTreeNodeHelper.getNumberTreeNode();
getPathSumList(node, 22);
System.out.println("所有的路径和为22的链路为:");
for (List<NumTreeNode> list : allList) {
for (NumTreeNode item : list) {
System.out.print(item.getValue() + " ");
}
System.out.println();
}
}
private static void getPathSumList(NumTreeNode node, int sum) {
if (node == null) {
return;
}
currentList.add(node);
if (node.getValue() == sum) {
allList.add(new ArrayList<>(currentList));
}
if (node.getLeft() != null) {
getPathSumList(node.getLeft(), sum - node.getValue());
}
if (node.getRight() != null) {
getPathSumList(node.getRight(), sum - node.getValue());
}
currentList.remove(currentList.size() - 1);
}
}
这里需要它在递归的时候,在它向上返回的时候,它是需要清理掉当前节点的数据的,这样会节省空间。
还有一种稍显笨拙的递归方法,它占用的空间更高,但是它的方法还是比较原始的,这里也给出来吧:
package com.mengzhidu.teach.algorithm.tree.demo.path;
import com.mengzhidu.teach.algorithm.tree.demo.NumTreeNode;
import com.mengzhidu.teach.algorithm.tree.demo.NumTreeNodeHelper;
import java.util.ArrayList;
import java.util.List;
/**
* 获取所有的路径和的节点列表
* 这里最大的问题就是每次访问一个新的节点的时候就必须得创建一个新的链表
* 占用空间巨大,不推荐,但是它反映了一个原始的思路
*/
public class PathSumDemo1 {
public static void main(String[] args) {
NumTreeNode root = NumTreeNodeHelper.getNumberTreeNode();
List<List<NumTreeNode>> list = new ArrayList<>();
getPathSumList(root, 22, new ArrayList<NumTreeNode>(), list);
System.out.println("所有的路径和为22的链路为:");
for (List<NumTreeNode> item : list) {
for (NumTreeNode node : item) {
System.out.print(node.getValue() + " ");
}
System.out.println();
}
}
private static void getPathSumList(NumTreeNode node, int sum, List<NumTreeNode> currentResult,
List<List<NumTreeNode>> allResult) {
if (node == null) {
return;
}
currentResult.add(node);
if (node.getValue() == sum) {
allResult.add(currentResult);
}
if (node.getLeft() != null) {
List<NumTreeNode> leftList = new ArrayList<>();
leftList.addAll(currentResult);
getPathSumList(node.getLeft(), sum - node.getValue(), leftList, allResult);
}
if (node.getRight() != null) {
ArrayList<NumTreeNode> rightList = new ArrayList<>();
rightList.addAll(currentResult);
getPathSumList(node.getRight(), sum - node.getValue(), rightList, allResult);
}
}
}