#数据结构与算法学习笔记#剑指Offer23:二叉树中和为某一值的路径 + 嵌套容器类排序3种实现 + 测试用例(Java、C/C++)

234 篇文章 1 订阅
80 篇文章 0 订阅

2018.8.27     《剑指Offer》从零单刷个人笔记整理(66题全)目录传送门​​​​​​​

这道题看似很复杂,实际上就是一个二叉树的递归遍历过程,只不过这次是有记录的递归,那么只需要多增加函数的参数用作记录即可。思路是创建一个符合路径列表ArrayList<ArrayList<Integer>> result和一个访问路径容器ArrayList<Integer> route,递归对每条路径用route进行记录,当访问至叶节点时,若结点值的和等于目标整数,则将该访问路径route加入符合路径列表target。

为了增加程序的效率特地进行了剪枝操作,减少递归次数,当结点值的和sum大于目标整数target时,提前结束递归。

为了符合题目要求,这里还涉及到对嵌套容器ArrayList<ArrayList<Integer>> result的排序操作,调用Collections的sort方法并且重写比较器Comparator的compare方法可以对嵌套容器根据size大小进行排序。为了学习,具体实现用了三种方法(外部对象调用,内部类,lambda表达式),参见下面代码。


题目描述

输入一颗二叉树的跟节点和一个整数,打印出二叉树中结点值的和为输入整数的所有路径。路径定义为从树的根结点开始往下一直到叶结点所经过的结点形成一条路径。(注意: 在返回值的list中,数组长度大的数组靠前)


嵌套容器排序3种实现方法:

1.外部对象调用

//排序处
Collections.sort(result, cmp);

//类内部
//将嵌套容器按size大小排序,从大到小(list2.size()-list1.size()返回正数,则从大到小排序)
static Comparator<ArrayList<Integer>> cmp = new Comparator<ArrayList<Integer>>(){
    public int compare(ArrayList<Integer> list1, ArrayList<Integer> list2) {
		return list2.size() - list1.size();
	}
};

2.内部类

Collections.sort(result, new Comparator<ArrayList<Integer>>() {
	@Override
	public int compare(ArrayList<Integer> list1, ArrayList<Integer> list2) {
	    return list2.size() - list1.size();
	}
});

3.lambda表达式

Collections.sort(result, (ArrayList<Integer> list1, ArrayList<Integer> list2)->Integer.compare(list2.size(), list1.size()));

Java实现:

/**
 * 
 * @author ChopinXBP 
 * 输入一颗二叉树的跟节点和一个整数,打印出二叉树中结点值的和为输入整数的所有路径。
 * 路径定义为从树的根结点开始往下一直到叶结点所经过的结点形成一条路径。(注意: 在返回值的list中,数组长度大的数组靠前)
 *
 */

import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;

public class FindPathofBintree {
	public static class TreeNode {
	    int val = 0;
	    TreeNode left = null;
	    TreeNode right = null;
	    public TreeNode(int val) {
	        this.val = val;
	    }
	}
	
	public static void main(String[] args) {
		// TODO Auto-generated method stub
		TreeNode root = new TreeNode(1);
		root = Init(root);
		ArrayList<ArrayList<Integer>> result = FindPath(root, 9);
		for(int i = 0; i < result.size(); i++){
			for(int j = 0; j < result.get(i).size(); j++)
				System.out.print(result.get(i).get(j));
			System.out.print('\n');
		}
	}
	
	public static TreeNode Init(TreeNode root){		
		TreeNode pNode = root;
		pNode.left = new TreeNode(2);
		pNode.right = new TreeNode(3);
		pNode = root.left;
		pNode.left = new TreeNode(5);
		pNode.right = new TreeNode(6);
		pNode = root.right;
		pNode.left = new TreeNode(5);
		pNode.right = new TreeNode(4);
		pNode = root.left.left;
		pNode.left = new TreeNode(1);

		return root;
	}

    public static ArrayList<ArrayList<Integer>> FindPath(TreeNode root, int target) {	
    	ArrayList<ArrayList<Integer>> result =  new ArrayList<ArrayList<Integer>>();	//记录所有符合路径列表
    	if(root == null) return result;
    	
    	ArrayList<Integer> route = new ArrayList<>();//记录每一条访问路径
    	int sum = 0;
    	Solve(result, route, root, target, sum);
    	
    	if(result.size() != 0 || result.size() != 1){
    		//嵌套容器排序——外部对象调用
    		//Collections.sort(result, cmp);
    		//嵌套容器排序——lambda表达式
    		Collections.sort(result, (ArrayList<Integer> list1, ArrayList<Integer> list2)
    				-> Integer.compare(list2.size(), list1.size()));
    	}
    	return result;
    }
    
    //递归对每条路径进行记录,当结点值的和大于目标整数时,提前结束递归;当访问至叶节点时,若结点值的和等于目标整数,则将该访问路径加入符合路径列表
    public static void Solve(ArrayList<ArrayList<Integer>> result, ArrayList<Integer> route, TreeNode root, int target, int sum){
    	route.add(root.val);
    	sum += root.val;
    	if(sum > target) return;
    	
    	if(root.left == null && root.right == null){
    		if(sum == target){
    			result.add(route);
    		}
    	}
    	else if(root.left == null){
    		Solve(result, route, root.right, target, sum);
    	}
    	else if(root.right == null){
    		Solve(result, route, root.left, target, sum);
    	}
    	else{
    		//若需要分岔,则需要重新new一个和route相同的访问路径
    		Solve(result, new ArrayList<>(route), root.right, target, sum);
    		Solve(result, new ArrayList<>(route), root.left, target, sum);
    	}
    }
    
    //将嵌套容器按size大小排序,从大到小(list2.size()-list1.size()返回正数,则从大到小排序)
    static Comparator<ArrayList<Integer>> cmp = new Comparator<ArrayList<Integer>>(){
    	public int compare(ArrayList<Integer> list1, ArrayList<Integer> list2) {
			return list2.size() - list1.size();
		}
	};


	
	//极其简洁的递归方法参考
	public class Solution {
	    private ArrayList<ArrayList<Integer>> result = new ArrayList<>();
	    private ArrayList<Integer> list = new ArrayList<>();
	    public ArrayList<ArrayList<Integer>> FindPath(TreeNode root,int target) {
	        help(root, target);
	        //嵌套容器排序——内部类写法
	        Collections.sort(result, new Comparator<ArrayList<Integer>>() {
	            @Override
	            public int compare(ArrayList<Integer> list1, ArrayList<Integer> list2) {
	                return list2.size() - list1.size();
	            }
	        });
	        return result;
	    }
	    
	    private void help(TreeNode root, int target) {
	        if (root == null)
	            return;
	        list.add(root.val);
	        target -= root.val;		//每次递归减小一次target值
	        if (target == 0 && root.left == null && root.right == null)
	            result.add(new ArrayList<>(list));
	        else if(target > 0){
		        if(root.right!=null)FindPath(root.right, target);
		        if(root.left!=null)FindPath(root.left, target);
	        }
	        list.remove(list.size() - 1);	//访问到叶节点时进行递归回退
	    }
	}
}

C++实现示例:

#include <vector>

void FindPath(BinaryTreeNode* pRoot, int expectedSum, std::vector<int>& path, int& currentSum);

void FindPath(BinaryTreeNode* pRoot, int expectedSum)
{
    if(pRoot == NULL)
        return;

    std::vector<int> path;
    int currentSum = 0;
    FindPath(pRoot, expectedSum, path, currentSum);
}

void FindPath
(
    BinaryTreeNode*   pRoot,        
    int               expectedSum,  
    std::vector<int>& path,         
    int&              currentSum
)
{
    currentSum += pRoot->m_nValue;
    path.push_back(pRoot->m_nValue);

    // 如果是叶结点,并且路径上结点的和等于输入的值
    // 打印出这条路径
    bool isLeaf = pRoot->m_pLeft == NULL && pRoot->m_pRight == NULL;
    if(currentSum == expectedSum && isLeaf)
    {
        printf("A path is found: ");
        std::vector<int>::iterator iter = path.begin();
        for(; iter != path.end(); ++ iter)
            printf("%d\t", *iter);
        
        printf("\n");
    }

    // 如果不是叶结点,则遍历它的子结点
    if(pRoot->m_pLeft != NULL)
        FindPath(pRoot->m_pLeft, expectedSum, path, currentSum);
    if(pRoot->m_pRight != NULL)
        FindPath(pRoot->m_pRight, expectedSum, path, currentSum);

    // 在返回到父结点之前,在路径上删除当前结点,
    // 并在currentSum中减去当前结点的值
    currentSum -= pRoot->m_nValue;
    path.pop_back();
} 

测试代码:

// ====================测试代码====================
void Test(char* testName, BinaryTreeNode* pRoot, int expectedSum)
{
    if(testName != NULL)
        printf("%s begins:\n", testName);

    FindPath(pRoot, expectedSum);

    printf("\n");
}

//            10
//         /      \
//        5        12
//       /\        
//      4  7     
// 有两条路径上的结点和为22
void Test1()
{
    BinaryTreeNode* pNode10 = CreateBinaryTreeNode(10);
    BinaryTreeNode* pNode5 = CreateBinaryTreeNode(5);
    BinaryTreeNode* pNode12 = CreateBinaryTreeNode(12);
    BinaryTreeNode* pNode4 = CreateBinaryTreeNode(4);
    BinaryTreeNode* pNode7 = CreateBinaryTreeNode(7);

    ConnectTreeNodes(pNode10, pNode5, pNode12);
    ConnectTreeNodes(pNode5, pNode4, pNode7);

    printf("Two paths should be found in Test1.\n");
    Test("Test1", pNode10, 22);

    DestroyTree(pNode10);
}

//            10
//         /      \
//        5        12
//       /\        
//      4  7     
// 没有路径上的结点和为15
void Test2()
{
    BinaryTreeNode* pNode10 = CreateBinaryTreeNode(10);
    BinaryTreeNode* pNode5 = CreateBinaryTreeNode(5);
    BinaryTreeNode* pNode12 = CreateBinaryTreeNode(12);
    BinaryTreeNode* pNode4 = CreateBinaryTreeNode(4);
    BinaryTreeNode* pNode7 = CreateBinaryTreeNode(7);

    ConnectTreeNodes(pNode10, pNode5, pNode12);
    ConnectTreeNodes(pNode5, pNode4, pNode7);

    printf("No paths should be found in Test2.\n");
    Test("Test2", pNode10, 15);

    DestroyTree(pNode10);
}

//               5
//              /
//             4
//            /
//           3
//          /
//         2
//        /
//       1
// 有一条路径上面的结点和为15
void Test3()
{
    BinaryTreeNode* pNode5 = CreateBinaryTreeNode(5);
    BinaryTreeNode* pNode4 = CreateBinaryTreeNode(4);
    BinaryTreeNode* pNode3 = CreateBinaryTreeNode(3);
    BinaryTreeNode* pNode2 = CreateBinaryTreeNode(2);
    BinaryTreeNode* pNode1 = CreateBinaryTreeNode(1);

    ConnectTreeNodes(pNode5, pNode4, NULL);
    ConnectTreeNodes(pNode4, pNode3, NULL);
    ConnectTreeNodes(pNode3, pNode2, NULL);
    ConnectTreeNodes(pNode2, pNode1, NULL);

    printf("One path should be found in Test3.\n");
    Test("Test3", pNode5, 15);

    DestroyTree(pNode5);
}

// 1
//  \
//   2
//    \
//     3
//      \
//       4
//        \
//         5
// 没有路径上面的结点和为16
void Test4()
{
    BinaryTreeNode* pNode1 = CreateBinaryTreeNode(1);
    BinaryTreeNode* pNode2 = CreateBinaryTreeNode(2);
    BinaryTreeNode* pNode3 = CreateBinaryTreeNode(3);
    BinaryTreeNode* pNode4 = CreateBinaryTreeNode(4);
    BinaryTreeNode* pNode5 = CreateBinaryTreeNode(5);

    ConnectTreeNodes(pNode1, NULL, pNode2);
    ConnectTreeNodes(pNode2, NULL, pNode3);
    ConnectTreeNodes(pNode3, NULL, pNode4);
    ConnectTreeNodes(pNode4, NULL, pNode5);

    printf("No paths should be found in Test4.\n");
    Test("Test4", pNode1, 16);

    DestroyTree(pNode1);
}

// 树中只有1个结点
void Test5()
{
    BinaryTreeNode* pNode1 = CreateBinaryTreeNode(1);

    printf("One path should be found in Test5.\n");
    Test("Test5", pNode1, 1);

    DestroyTree(pNode1);
}

// 树中没有结点
void Test6()
{
    printf("No paths should be found in Test6.\n");
    Test("Test6", NULL, 0);
}

int _tmain(int argc, _TCHAR* argv[])
{
    Test1();
    Test2();
    Test3();
    Test4();
    Test5();
    Test6();

    return 0;
}

#Coding一小时,Copying一秒钟。留个言点个赞呗,谢谢你#

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值