给定一个二叉树,找出所有路径中各节点相加总和。
一个有效的路径,指的是从根节点到叶节点的路径。这样就是一条有效的路径,求这个路径上值的和,以及对应路径上的值.
- 树的遍历有 前序,中序,后序 , 层次遍历4种, 我们需要找到一条有效的路径, 根节点是访问的第一个节点 , 所以前序遍历是比较合适的, 根->左 在递归的过程中,会一直找到最左边的节点, 这就是第一条完整的路径.
- 求和需要知道路径上的值,可以用一个sum值来保存这个路径上的值的和, 但是如果还需要对应路径上的值, 那么就需要一个集合来保存了, 使用数组或者栈都是可以的.
- 当路径变化的时候,还需要把旧路径上的值去掉.
当用前序遍历的方式访问到某一结点时,我们把该结点添加到路径(数组或栈中)上,并累加该结点的值。
如果该结点为叶结点,路径结束,我们把它打印出来。
如果当前结点不是叶结点,则继续访问它的子结点。
当前结点访问结束后,递归函数将自动回到它的父结点。因此我们在函数退出之前要在路径上删除当前结点并减去当前结点的值,以确保返回父结点时路径刚好是从根结点到父结点的路径。由此可以看出保存路径的数据结构实际上是一个栈,因为路径要与递归调用状态一致,而递归调用的本质就是一个压栈和出栈的过程。
好,下面到代码部分.
二叉树的节点定义, 根据传入数组,生成对应的完全二叉树
@interface TreeNode : NSObject
/// 根据数组生成一个完全二叉树
+ (TreeNode *)nodeWithDataArray:(NSArray *)dataArray;
/// 生成一个节点,左右节点为nil
+ (TreeNode *)nodeWithData:(int)data;
/// 生成一个节点,左右节点为指定节点
+ (TreeNode *)nodeWithData:(int)data leftNode:(TreeNode *)leftNode rightNode:(TreeNode *)rightNode;
@property (nonatomic, assign) int data;
@property (nonatomic, strong) TreeNode * leftNode;
@property (nonatomic, strong) TreeNode * rightNode;
@end
-------
#import "TreeNode.h"
@implementation TreeNode
// 实现方式1:根据完全二叉树的下标(1~n)来处理, 任意子节点的下标/2 都是父节点
+ (TreeNode *)nodeWithDataArray:(NSArray *)dataArray {
if (dataArray == nil || dataArray.count == 0) {
return nil;
}
//根据数组的数量,生成一个完全二叉树
NSMutableArray<TreeNode *> * treeArray = [NSMutableArray array];
//放一个占位的值,这样根节点的下标就对应成treeArray[1]了,计算父节点会更容易点
[treeArray addObject:[TreeNode nodeWithData:NAN]];
for (int i = 0; i<dataArray.count; i++) {
NSNumber * num = dataArray[i];
TreeNode * temp = [TreeNode nodeWithData:num.intValue];
// 如果是偶数,说明是对应父节点的左子节点
if (treeArray.count%2==0&&treeArray.count>1) {
TreeNode * father = treeArray[treeArray.count/2];
father.leftNode = temp;
}
// 如果是奇数,说明是对应父节点的右子节点
if (treeArray.count%2==1) {
TreeNode * father = treeArray[treeArray.count/2];
father.rightNode = temp;
}
[treeArray addObject:temp];
}
return treeArray[1];
}
/// 实现方式2:模拟层次遍历, 依次生成子节点并赋值
+ (TreeNode *)nodeWithDataArray2:(NSArray<NSNumber *> *)dataArray {
if (dataArray == nil || dataArray.count == 0) {
return nil;
}
//模拟队列结构,进行层次遍历
NSMutableArray<TreeNode *> * treeArray = [NSMutableArray array];
TreeNode * root = [TreeNode nodeWithData:dataArray.firstObject.intValue];
[treeArray addObject:root];
for (int i = 1; i<dataArray.count; i++) {
NSNumber * num = dataArray[i];
TreeNode * leftNode = [TreeNode nodeWithData:num.intValue];
TreeNode * father = treeArray.firstObject;
[treeArray removeObject:father];
father.leftNode = leftNode;
[treeArray addObject:leftNode];
i++;
if (i>=dataArray.count) {
break;
}
num = dataArray[i];
TreeNode * rightNode = [TreeNode nodeWithData:num.intValue];
father.rightNode = rightNode;
[treeArray addObject:rightNode];
}
return root;
}
+ (TreeNode *)nodeWithData:(int)data {
TreeNode * root = [[TreeNode alloc] init];
root.data = data;
return root;
}
+ (TreeNode *)nodeWithData:(int)data leftNode:(TreeNode *)leftNode rightNode:(TreeNode *)rightNode {
TreeNode * root = [[TreeNode alloc] init];
root.data = data;
root.leftNode = leftNode;
root.rightNode = rightNode;
return root;
}
- (NSString *)description {
return [NSString stringWithFormat:@"%d %@ %@",self.data,self.leftNode,self.rightNode];
}
@end
- (void)viewDidLoad {
[super viewDidLoad];
[self sumTree];
}
/// 二叉树路径和
- (void)sumTree {
// 测试用例
// TreeNode * root = [TreeNode nodeWithDataArray:nil];
// TreeNode * root = [TreeNode nodeWithDataArray:@[]];
// TreeNode * root = [TreeNode nodeWithDataArray:@[@(10)]];
// TreeNode * root = [TreeNode nodeWithDataArray:@[@(12),@(4),@(7)]];
TreeNode * root = [TreeNode nodeWithDataArray:@[@(10),@(5),@(12),@(4),@(7)]];
// TreeNode * root = [TreeNode nodeWithDataArray:@[@(1),@(2),@(3),@(4),@(5),@(6),@(7)]];
// TreeNode * root = [TreeNode nodeWithDataArray:@[@(1),@(2),@(3),@(4),@(5),@(6),@(7),@(8),@(9),@(10),@(11),@(12),@(13),]];
[self __sumAction:root];
}
- (void)__sumAction:(TreeNode *)node {
if (node == nil) {
return;
}
// 用数组模拟一个栈栈结构
static NSMutableArray * array = nil;
if (array == nil) {
array = [NSMutableArray array];
}
[array addObject:@(node.data)];
// 打印当前节点,和当前路径的和
NSLog(@"添加新node:%d sum:%@",node.data,[array valueForKeyPath:@"@sum.self"]);
// 前序遍历,根->左->右
if (node.leftNode) {
[self __sumAction:node.leftNode];
}
if (node.rightNode) {
[self __sumAction:node.rightNode];
}
// 如果是叶子节点,打印
if (node.leftNode == nil && node.rightNode == nil) {
NSLog(@"此路径完成,%@, 总和为: %@",[array componentsJoinedByString:@"->"],[array valueForKeyPath:@"@sum.self"]);
}
NSLog(@"移除此元素 %@",array.lastObject);
[array removeLastObject];
}