问题
剑指offer上有这么一道题:输入一颗二叉树的根节点和一个整数,打印出二叉树中结点值的和为输入整数的所有路径。路径定义为从树的根结点开始往下一直到叶结点所经过的结点形成一条路径。(注意: 在返回值的list中,数组长度大的数组靠前)
求解思路:
我的解法是将这个题目分成两个步骤,第一个步骤是先得到二叉树满足路径和为输入整数的所有路径;第二步将结果中的数组按照数组长度降序进行排序。
代码:
import java.util.ArrayList;
/**
public class TreeNode {
int val = 0;
TreeNode left = null;
TreeNode right = null;
public TreeNode(int val) {
this.val = val;
}
}
*/
import java.util.*;
public class Solution {
public ArrayList<ArrayList<Integer>> FindPath(TreeNode root,int target) {
//创建一个集合存储二叉树的路径
//这个题目的路径要求的是用整型列表存储,换成其他类型的也可以
ArrayList<ArrayList<Integer>> result = new ArrayList<>();
//如果二叉树为空将结果直接返回
if (root == null)
return result;
//定义一个方法求解二叉树满足要求的路径
result = binaryTreePaths(root, target);
//如果返回的结果中不存在路径之和等于target 或者只有一条路径直接返回不需要进行排序
if (result.size() == 0 || result.size() == 1)
return result;
else{
//对多条路径按照路径长度进行排序,这里用的是快排
result = quickSort(result, 0, result.size() - 1);
// 返回排序后的结果
return result;
}
}
/*
这个方法主要用到了两个队列:一个存储路径的队列,一个是存储结点的队列。
每次循环都将上一次循环得到的路径进行更新,即把从结点队列得到的队首元素添加到路径中。
在这里由于题目对二叉树的路径是有要求所有在每条路径的首位元素作为该路径上所有结点值之和,每次循环更新该值。
遇到叶子节点时判断这个值与目标值是否相等,相等则将该值从路径中删除然后将这条路径添加到路径结果集中
*/
public ArrayList<ArrayList<Integer>> binaryTreePaths(TreeNode root, int targetVal) {
ArrayList<ArrayList<Integer>> list=new ArrayList<>();
//使用队列存储二叉树中的所有结点
Queue<TreeNode> qNode=new LinkedList<TreeNode>();
//存储路径
Queue<ArrayList<Integer>> qStr=new LinkedList<>();
if (root==null) return list;
//将root结点放入队首
qNode.add(root);
ArrayList<Integer> items = new ArrayList<>();
//把一条空路径放入路径队列中
qStr.add(items);
//记录每条路径上结点值之和将其放入路径数组的第一位
int sumLeft = 0;
int sumRight = 0;
//循环结点队列
while(!qNode.isEmpty()) {
//获取队首结点 首次为root结点
TreeNode curNode = qNode.remove();
//获取队首路径 首次为空 每一次获取的都是上一次循环记录的路径
ArrayList<Integer> curStr = qStr.remove();
//复制一条路径
ArrayList<Integer> rightList = (ArrayList<Integer>) curStr.clone();
//如果当前结点是叶子节点 则是这条路径的尽头 即可将路径添加至路径结点中
if (curNode.left==null && curNode.right==null){
//将当前叶子结点的值添加至路径中
curStr.add(curNode.val);
int res = 0;
//如果当前路径只有一个结点 说明整个二叉树只有一个结点
if(curStr.size() == 1){
res = curStr.get(0);
//用这个结点的值与目标值比较相等时将这个路径添加到路径集合中
if (res == targetVal){
list.add(curStr);
}
//当前路径不止一个结点
}else{
//当前路径结点值的和 = 上次循环路径几点值之和 + 路径末尾结点的值
res = curStr.get(0) + curNode.val;
//符合要求则将路径添加到路径集合中
if (res == targetVal){
//将路径的首个元素删除
curStr.remove(0);
list.add(curStr);
}
}
}
//当前结点不是叶子结点
if (curNode.left!=null) {
//把当前结点的左子结点添加到结点队列中
qNode.add(curNode.left);
//如果为0说明当前路径没有任何结点
if (sumLeft == 0){
sumLeft += curNode.val;
//先把记录路径和的值添加到路径第一位
curStr.add(sumLeft);
}else{
//不为0说明当前路径有结点
sumLeft += curNode.val;
curStr.set(0, sumLeft);
}
//当前结点添加至路径中
curStr.add(curNode.val);
//当前路径添加到路径集合中
qStr.add(curStr);
}
if (curNode.right!=null) {
if (sumRight == 0){
sumRight += curNode.val;
rightList.add(sumRight);
}else{
sumRight += curNode.val;
rightList.set(0, sumRight);
}
qNode.add(curNode.right);
rightList.add(curNode.val);
qStr.add(rightList);
}
}
return list;
}
/*
对结果集按照数组的长度进行降序快速排序
*/
public ArrayList<ArrayList<Integer>> quickSort(ArrayList<ArrayList<Integer>> arr, int start, int end){
int pivot = arr.get(start).size();
int i = start;
int j = end;
while (i<j) {
//这里比较都是每个数组元素的长度
while ((i<j)&&(arr.get(j).size() < pivot)) {
j--;
}
while ((i<j)&&(arr.get(i).size() > pivot)) {
i++;
}
if ((arr.get(i).size() == arr.get(j).size())&&(i<j)) {
i++;
} else {
ArrayList<Integer> temp = arr.get(i);
arr.set(i, arr.get(j));
arr.set(j, temp);
}
}
if (i-1>start) arr = quickSort(arr,start,i-1);
if (j+1<end) arr= quickSort(arr,j+1,end);
return arr;
}
}
总结:
这一题不仅考察了对二叉树寻找路径的操作,而且还考察了排序算法,我认为是一个不错的综合性的题目。当然了,这种解题思路以及代码不一定是最优的,还有很大的改进空间。