iOS 二叉树相关算法实现

什么是二叉树?

在计算机科学中,二叉树是每个节点最多有两个子树的树结构。通常子树被称作“左子树”和“右子树”,左子树和右子树同时也是二叉树。二叉树的子树有左右之分,并且次序不能任意颠倒。二叉树是递归定义的,所以一般二叉树的相关题目也都可以使用递归的思想来解决,当然也有一些可以使用非递归的思想解决,我下面列出的一些算法有些采用了递归,有些是非递归的。

什么是二叉排序树?

二叉排序树又叫二叉查找树或者二叉搜索树,它首先是一个二叉树,而且必须满足下面的条件:

1)若左子树不空,则左子树上所有结点的值均小于它的根节点的值;

2)若右子树不空,则右子树上所有结点的值均大于它的根结点的值

3)左、右子树也分别为二叉排序树

4)没有键值相等的节点(?可能是因为不好处理键值相等的节点到底是左节点还是右节点吧)

概念就介绍这么多,都是来自网上,下面主要看算法和具体实现代码。

二叉树节点定义

采用单项链表的形式,只从根节点指向孩子节点,不保存父节点。

复制代码
/***  二叉树节点*/
@interface BinaryTreeNode : NSObject/***  值*/
@property (nonatomic, assign) NSInteger value;
/***  左节点*/
@property (nonatomic, strong) BinaryTreeNode *leftNode;
/***  右节点*/
@property (nonatomic, strong) BinaryTreeNode *rightNode;@end
复制代码

创建二叉排序树

二叉树中左右节点值本身没有大小之分,所以如果要创建二叉树,就需要考虑如何处理某个节点是左节点还是右节点,如何终止某个子树而切换到另一个子树。 因此我选择了二叉排序树,二叉排序树中对于左右节点有明确的要求,程序可以自动根据键值大小自动选择是左节点还是右节点。

复制代码
/***  创建二叉排序树*  二叉排序树:左节点值全部小于根节点值,右节点值全部大于根节点值**  @param values 数组**  @return 二叉树根节点*/
+ (BinaryTreeNode *)createTreeWithValues:(NSArray *)values {BinaryTreeNode *root = nil;for (NSInteger i=0; i<values.count; i++) {NSInteger value = [(NSNumber *)[values objectAtIndex:i] integerValue];root = [BinaryTree addTreeNode:root value:value];}return root;
}/***  向二叉排序树节点添加一个节点**  @param treeNode 根节点*  @param value    值**  @return 根节点*/
+ (BinaryTreeNode *)addTreeNode:(BinaryTreeNode *)treeNode value:(NSInteger)value {//根节点不存在,创建节点if (!treeNode) {treeNode = [BinaryTreeNode new];treeNode.value = value;NSLog(@"node:%@", @(value));}else if (value <= treeNode.value) {NSLog(@"to left");//值小于根节点,则插入到左子树treeNode.leftNode = [BinaryTree addTreeNode:treeNode.leftNode value:value];}else {NSLog(@"to right");//值大于根节点,则插入到右子树treeNode.rightNode = [BinaryTree addTreeNode:treeNode.rightNode value:value];}return treeNode;
}
复制代码

二叉树中某个位置的节点

类似索引操作,按层次遍历,位置从0开始算。

复制代码
/***  二叉树中某个位置的节点(按层次遍历)**  @param index    按层次遍历树时的位置(从0开始算)*  @param rootNode 树根节点**  @return 节点*/
+ (BinaryTreeNode *)treeNodeAtIndex:(NSInteger)index inTree:(BinaryTreeNode *)rootNode {//按层次遍历if (!rootNode || index < 0) {return nil;}NSMutableArray *queueArray = [NSMutableArray array]; //数组当成队列[queueArray addObject:rootNode]; //压入根节点while (queueArray.count > 0) {BinaryTreeNode *node = [queueArray firstObject];if (index == 0) {return node;}[queueArray removeObjectAtIndex:0]; //弹出最前面的节点,仿照队列先进先出原则index--; //移除节点,index减少if (node.leftNode) {[queueArray addObject:node.leftNode]; //压入左节点
        }if (node.rightNode) {[queueArray addObject:node.rightNode]; //压入右节点
        }}//层次遍历完,仍然没有找到位置,返回nilreturn nil;
}
复制代码

先序遍历

先访问根,再遍历左子树,再遍历右子树。典型的递归思想。

复制代码
/***  先序遍历*  先访问根,再遍历左子树,再遍历右子树**  @param rootNode 根节点*  @param handler  访问节点处理函数*/
+ (void)preOrderTraverseTree:(BinaryTreeNode *)rootNode handler:(void(^)(BinaryTreeNode *treeNode))handler {if (rootNode) {if (handler) {handler(rootNode);}[self preOrderTraverseTree:rootNode.leftNode handler:handler];[self preOrderTraverseTree:rootNode.rightNode handler:handler];}
}
复制代码

调用方法如下:(用到了block)

NSMutableArray *orderArray = [NSMutableArray array];
[BinaryTree preOrderTraverseTree:root handler:^(BinaryTreeNode *treeNode) {[orderArray addObject:@(treeNode.value)];
}];
NSLog(@"先序遍历结果:%@", [orderArray componentsJoinedByString:@","]);

 

中序遍历

先遍历左子树,再访问根,再遍历右子树。

对于二叉排序树来说,中序遍历得到的序列是一个从小到大排序好的序列。

复制代码
/***  中序遍历*  先遍历左子树,再访问根,再遍历右子树**  @param rootNode 根节点*  @param handler  访问节点处理函数*/
+ (void)inOrderTraverseTree:(BinaryTreeNode *)rootNode handler:(void(^)(BinaryTreeNode *treeNode))handler {if (rootNode) {[self inOrderTraverseTree:rootNode.leftNode handler:handler];if (handler) {handler(rootNode);}[self inOrderTraverseTree:rootNode.rightNode handler:handler];}
}
复制代码

 

后序遍历

先遍历左子树,再遍历右子树,再访问根

复制代码
/***  后序遍历*  先遍历左子树,再遍历右子树,再访问根**  @param rootNode 根节点*  @param handler  访问节点处理函数*/
+ (void)postOrderTraverseTree:(BinaryTreeNode *)rootNode handler:(void(^)(BinaryTreeNode *treeNode))handler {if (rootNode) {[self postOrderTraverseTree:rootNode.leftNode handler:handler];[self postOrderTraverseTree:rootNode.rightNode handler:handler];if (handler) {handler(rootNode);}}
}
复制代码

 

层次遍历

按照从上到下、从左到右的次序进行遍历。先遍历完一层,再遍历下一层,因此又叫广度优先遍历。需要用到队列,在OC里可以用可变数组来实现。

复制代码
/***  层次遍历(广度优先)**  @param rootNode 二叉树根节点*  @param handler  访问节点处理函数*/
+ (void)levelTraverseTree:(BinaryTreeNode *)rootNode handler:(void(^)(BinaryTreeNode *treeNode))handler {if (!rootNode) {return;}NSMutableArray *queueArray = [NSMutableArray array]; //数组当成队列[queueArray addObject:rootNode]; //压入根节点while (queueArray.count > 0) {BinaryTreeNode *node = [queueArray firstObject];if (handler) {handler(node);}[queueArray removeObjectAtIndex:0]; //弹出最前面的节点,仿照队列先进先出原则if (node.leftNode) {[queueArray addObject:node.leftNode]; //压入左节点
        }if (node.rightNode) {[queueArray addObject:node.rightNode]; //压入右节点
        }}
}
复制代码

 

二叉树的深度

二叉树的深度定义为:从根节点到叶子结点依次经过的结点形成树的一条路径,最长路径的长度为树的深度。

1)如果根节点为空,则深度为0;

2)如果左右节点都是空,则深度为1;

3)递归思想:二叉树的深度=max(左子树的深度,右子树的深度)+ 1

复制代码
/***  二叉树的深度**  @param rootNode 二叉树根节点**  @return 二叉树的深度*/
+ (NSInteger)depthOfTree:(BinaryTreeNode *)rootNode {if (!rootNode) {return 0;}if (!rootNode.leftNode && !rootNode.rightNode) {return 1;}//左子树深度NSInteger leftDepth = [self depthOfTree:rootNode.leftNode];//右子树深度NSInteger rightDepth = [self depthOfTree:rootNode.rightNode];return MAX(leftDepth, rightDepth) + 1;
}
复制代码

 

二叉树的宽度

二叉树的宽度定义为各层节点数的最大值。

复制代码
/***  二叉树的宽度**  @param rootNode 二叉树根节点**  @return 二叉树宽度*/
+ (NSInteger)widthOfTree:(BinaryTreeNode *)rootNode {if (!rootNode) {return 0;}NSMutableArray *queueArray = [NSMutableArray array]; //数组当成队列[queueArray addObject:rootNode]; //压入根节点NSInteger maxWidth = 1; //最大的宽度,初始化为1(因为已经有根节点)NSInteger curWidth = 0; //当前层的宽度while (queueArray.count > 0) {curWidth = queueArray.count;//依次弹出当前层的节点for (NSInteger i=0; i<curWidth; i++) {BinaryTreeNode *node = [queueArray firstObject];[queueArray removeObjectAtIndex:0]; //弹出最前面的节点,仿照队列先进先出原则//压入子节点if (node.leftNode) {[queueArray addObject:node.leftNode];}if (node.rightNode) {[queueArray addObject:node.rightNode];}}//宽度 = 当前层节点数maxWidth = MAX(maxWidth, queueArray.count);}return maxWidth;
}
复制代码

 二叉树的所有节点数

递归思想:二叉树所有节点数=左子树节点数+右子树节点数+1

复制代码
/***  二叉树的所有节点数**  @param rootNode 根节点**  @return 所有节点数*/
+ (NSInteger)numberOfNodesInTree:(BinaryTreeNode *)rootNode {if (!rootNode) {return 0;}//节点数=左子树节点数+右子树节点数+1(根节点)return [self numberOfNodesInTree:rootNode.leftNode] + [self numberOfNodesInTree:rootNode.rightNode] + 1;
}
复制代码

二叉树某层中的节点数

1)根节点为空,则节点数为0;

2)层为1,则节点数为1(即根节点)

3)递归思想:二叉树第k层节点数=左子树第k-1层节点数+右子树第k-1层节点数

复制代码
/***  二叉树某层中的节点数**  @param level    层*  @param rootNode 根节点**  @return 层中的节点数*/
+ (NSInteger)numberOfNodesOnLevel:(NSInteger)level inTree:(BinaryTreeNode *)rootNode {if (!rootNode || level < 1) { //根节点不存在或者level<0return 0;}if (level == 1) { //level=1,返回1(根节点)return 1;}//递归:level层节点数 = 左子树level-1层节点数+右子树level-1层节点数return [self numberOfNodesOnLevel:level-1 inTree:rootNode.leftNode] + [self numberOfNodesOnLevel:level-1 inTree:rootNode.rightNode];
} 
复制代码

二叉树叶子节点数

叶子节点,又叫终端节点,是左右子树都是空的节点。

复制代码
/***  二叉树叶子节点数**  @param rootNode 根节点**  @return 叶子节点数*/
+ (NSInteger)numberOfLeafsInTree:(BinaryTreeNode *)rootNode {if (!rootNode) {return 0;}//左子树和右子树都是空,说明是叶子节点if (!rootNode.leftNode && !rootNode.rightNode) {return 1;}//递归:叶子数 = 左子树叶子数 + 右子树叶子数return [self numberOfLeafsInTree:rootNode.leftNode] + [self numberOfLeafsInTree:rootNode.rightNode];
}
复制代码

 

二叉树最大距离(二叉树的直径)

二叉树中任意两个节点都有且仅有一条路径,这个路径的长度叫这两个节点的距离。二叉树中所有节点之间的距离的最大值就是二叉树的直径。

有一种解法,把这个最大距离划分了3种情况:

1)这2个节点分别在根节点的左子树和右子树上,他们之间的路径肯定经过根节点,而且他们肯定是根节点左右子树上最远的叶子节点(他们到根节点的距离=左右子树的深度)。

2)这2个节点都在左子树上

3)这2个节点都在右子树上

综上,只要取这3种情况中的最大值,就是二叉树的直径。

复制代码
/***  二叉树最大距离(直径)**  @param rootNode 根节点**  @return 最大距离*/
+ (NSInteger)maxDistanceOfTree:(BinaryTreeNode *)rootNode {if (!rootNode) {return 0;}
//    方案一:(递归次数较多,效率较低)//分3种情况://1、最远距离经过根节点:距离 = 左子树深度 + 右子树深度NSInteger distance = [self depthOfTree:rootNode.leftNode] + [self depthOfTree:rootNode.rightNode];//2、最远距离在根节点左子树上,即计算左子树最远距离NSInteger disLeft = [self maxDistanceOfTree:rootNode.leftNode];//3、最远距离在根节点右子树上,即计算右子树最远距离NSInteger disRight = [self maxDistanceOfTree:rootNode.rightNode];return MAX(MAX(disLeft, disRight), distance);
}
复制代码

这个方案效率较低,因为计算子树的深度和最远距离是分开递归的,存在重复递归遍历的情况。其实一次递归,就可以分别计算出深度和最远距离,于是有了第二种方案:

复制代码
/***  二叉树最大距离(直径)**  @param rootNode 根节点**  @return 最大距离*/
+ (NSInteger)maxDistanceOfTree:(BinaryTreeNode *)rootNode {if (!rootNode) {return 0;}
//    方案2:将计算节点深度和最大距离放到一次递归中计算,方案一是分别单独递归计算深度和最远距离TreeNodeProperty *p = [self propertyOfTreeNode:rootNode];return p.distance;
}/***  计算树节点的最大深度和最大距离**  @param rootNode 根节点**  @return TreeNodeProperty*/
+ (TreeNodeProperty *)propertyOfTreeNode:(BinaryTreeNode *)rootNode {if (!rootNode) {return nil;}TreeNodeProperty *left = [self propertyOfTreeNode:rootNode.leftNode];TreeNodeProperty *right = [self propertyOfTreeNode:rootNode.rightNode];TreeNodeProperty *p = [TreeNodeProperty new];//节点的深度depth = 左子树深度、右子树深度中最大值+1(+1是因为根节点占了1个depth)p.depth = MAX(left.depth, right.depth) + 1;//最远距离 = 左子树最远距离、右子树最远距离和横跨左右子树最远距离中最大值p.distance = MAX(MAX(left.distance, right.distance), left.depth+right.depth);return p;
}
复制代码

二叉树中某个节点到根节点的路径

既是寻路问题,又是查找节点问题。

定义一个存放路径的栈(不是队列了,但是还是用可变数组来实现的)

1)压入根节点,再从左子树中查找(递归进行的),如果未找到,再从右子树中查找,如果也未找到,则弹出根节点,再遍历栈中上一个节点。

2)如果找到,则栈中存放的节点就是路径所经过的节点。

复制代码
/***  二叉树中某个节点到根节点的路径**  @param treeNode 节点*  @param rootNode 根节点**  @return 存放路径节点的数组*/
+ (NSArray *)pathOfTreeNode:(BinaryTreeNode *)treeNode inTree:(BinaryTreeNode *)rootNode {NSMutableArray *pathArray = [NSMutableArray array];[self isFoundTreeNode:treeNode inTree:rootNode routePath:pathArray];return pathArray;
}/***  查找某个节点是否在树中**  @param treeNode 待查找的节点*  @param rootNode 根节点*  @param path  根节点到待查找节点的路径**  @return YES:找到,NO:未找到*/
+ (BOOL)isFoundTreeNode:(BinaryTreeNode *)treeNode inTree:(BinaryTreeNode *)rootNode routePath:(NSMutableArray *)path {if (!rootNode || !treeNode) {return NO;}//找到节点if (rootNode == treeNode) {[path addObject:rootNode];return YES;}//压入根节点,进行递归
    [path addObject:rootNode];//先从左子树中查找BOOL find = [self isFoundTreeNode:treeNode inTree:rootNode.leftNode routePath:path];//未找到,再从右子树查找if (!find) {find = [self isFoundTreeNode:treeNode inTree:rootNode.rightNode routePath:path];}//如果2边都没查找到,则弹出此根节点if (!find) {[path removeLastObject];}return find;
}
复制代码

二叉树中两个节点最近的公共父节点

首先需要明白,根节点肯定是二叉树中任意两个节点的公共父节点(不一定是最近的),因此二叉树中2个节点的最近公共父节点一定在从根节点到这个节点的路径上。因此我们可以先分别找到从根节点到这2个节点的路径,再从这两个路径中找到最近的公共父节点。

复制代码
/***  二叉树中两个节点最近的公共父节点**  @param nodeA    第一个节点*  @param nodeB    第二个节点*  @param rootNode 二叉树根节点**  @return 最近的公共父节点*/
+ (BinaryTreeNode *)parentOfNode:(BinaryTreeNode *)nodeA andNode:(BinaryTreeNode *)nodeB inTree:(BinaryTreeNode *)rootNode {if (!rootNode || !nodeA || !nodeB) {return nil;}if (nodeA == nodeB) {return nodeA;}//从根节点到节点A的路径NSArray *pathA = [self pathOfTreeNode:nodeA inTree:rootNode];//从根节点到节点B的路径NSArray *pathB = [self pathOfTreeNode:nodeB inTree:rootNode];//其中一个节点不在树中,则没有公共父节点if (pathA.count == 0 || pathB == 0) {return nil;}//从后往前推,查找第一个出现的公共节点for (NSInteger i = pathA.count-1; i>=0; i--) {for (NSInteger j = pathB.count - 1; j>=0; j--) {if ([pathA objectAtIndex:i] == [pathB objectAtIndex:j]) {//找到return [pathA objectAtIndex:i];}}}return nil;
}
复制代码

二叉树中两个节点之间的路径

从查找最近公共父节点衍生出来的。

复制代码
/***  二叉树中两个节点之间的路径**  @param nodeA    第一个节点*  @param nodeB    第二个节点*  @param rootNode 二叉树根节点**  @return 两个节点间的路径*/
+ (NSArray *)pathFromNode:(BinaryTreeNode *)nodeA toNode:(BinaryTreeNode *)nodeB inTree:(BinaryTreeNode *)rootNode {if (!rootNode || !nodeA || !nodeB) {return nil;}NSMutableArray *path = [NSMutableArray array];if (nodeA == nodeB) {[path addObject:nodeA];[path addObject:nodeB];return path;}//从根节点到节点A的路径NSArray *pathA = [self pathOfTreeNode:nodeA inTree:rootNode];//从根节点到节点B的路径NSArray *pathB = [self pathOfTreeNode:nodeB inTree:rootNode];//其中一个节点不在树中,则没有路径if (pathA.count == 0 || pathB == 0) {return nil;}//从后往前推,查找第一个出现的公共节点for (NSInteger i = pathA.count-1; i>=0; i--) {[path addObject:[pathA objectAtIndex:i]];for (NSInteger j = pathB.count - 1; j>=0; j--) {//找到公共父节点,则将pathB中后面的节点压入pathif ([pathA objectAtIndex:i] == [pathB objectAtIndex:j]) {j++; //j++是为了避开公共父节点while (j<pathB.count) {[path addObject:[pathB objectAtIndex:j]];j++;}return path;}}}return nil;
}
复制代码

二叉树两个节点之间的距离

可以从两个节点之间的路径衍生出来。

复制代码
/***  二叉树两个节点之间的距离**  @param nodeA    第一个节点*  @param nodeB    第二个节点*  @param rootNode 二叉树根节点**  @return 两个节点间的距离(-1:表示没有找到路径)*/
+ (NSInteger)distanceFromNode:(BinaryTreeNode *)nodeA toNode:(BinaryTreeNode *)nodeB inTree:(BinaryTreeNode *)rootNode {if (!rootNode || !nodeA || !nodeB) {return -1;}if (nodeA == nodeB) {return 0;}//从根节点到节点A的路径NSArray *pathA = [self pathOfTreeNode:nodeA inTree:rootNode];//从根节点到节点B的路径NSArray *pathB = [self pathOfTreeNode:nodeB inTree:rootNode];//其中一个节点不在树中,则没有路径if (pathA.count == 0 || pathB == 0) {return -1;}//从后往前推,查找第一个出现的公共节点for (NSInteger i = pathA.count-1; i>=0; i--) {for (NSInteger j = pathB.count - 1; j>=0; j--) {//找到公共父节点if ([pathA objectAtIndex:i] == [pathB objectAtIndex:j]) {//距离=路径节点数-1 (这里要-2,因为公共父节点重复了一次)return (pathA.count - i) + (pathB.count - j) - 2;}}}return -1;
}
复制代码

 

翻转二叉树

你会翻转二叉树吗?如果不会,那对不起,我们不会录用你!

翻转二叉树,又叫求二叉树的镜像,就是把二叉树的左右子树对调(当然是递归的)

复制代码
/***  翻转二叉树(又叫:二叉树的镜像)**  @param rootNode 根节点**  @return 翻转后的树根节点(其实就是原二叉树的根节点)*/
+ (BinaryTreeNode *)invertBinaryTree:(BinaryTreeNode *)rootNode {if (!rootNode) {return nil;}if (!rootNode.leftNode && !rootNode.rightNode) {return rootNode;}[self invertBinaryTree:rootNode.leftNode];[self invertBinaryTree:rootNode.rightNode];BinaryTreeNode *tempNode = rootNode.leftNode;rootNode.leftNode = rootNode.rightNode;rootNode.rightNode = tempNode;return rootNode;
}
复制代码

判断二叉树是否完全二叉树

完全二叉树定义为:若设二叉树的高度为h,除第h层外,其它各层的结点数都达到最大个数,第h层有叶子结点,并且叶子结点都是从左到右依次排布。

完全二叉树必须满足2个条件:

1)如果某个节点的右子树不为空,则它的左子树必须不为空

2)如果某个节点的右子树为空,则排在它后面的节点必须没有孩子节点

这里还需要理解“排在它后面的节点”,回头看看层次遍历算法,我们就能知道在层次遍历时,是从上到下从左到右遍历的,先将根节点弹出队列,再压入孩子节点,因此“排在它后面的节点”有2种情况:

1)同层次的后面的节点

2)同层次的前面的节点的孩子节点(因为遍历前面的节点时,会弹出节点,同时将孩子节点压入队列)

通过上面的分析,我们可以设置一个标志位flag,当子树满足完全二叉树时,设置flag=YES。当flag=YES而节点又破坏了完全二叉树的条件,那么它就不是完全二叉树。

复制代码
/***  是否完全二叉树*  完全二叉树:若设二叉树的高度为h,除第h层外,其它各层的结点数都达到最大个数,第h层有叶子结点,并且叶子结点都是从左到右依次排布**  @param rootNode 根节点**  @return YES:是完全二叉树,NO:不是完全二叉树*/
+ (BOOL)isCompleteBinaryTree:(BinaryTreeNode *)rootNode {if (!rootNode) {return NO;}//左子树和右子树都是空,则是完全二叉树if (!rootNode.leftNode && !rootNode.rightNode) {return YES;}//左子树是空,右子树不是空,则不是完全二叉树if (!rootNode.leftNode && rootNode.rightNode) {return NO;}//按层次遍历节点,找到满足完全二叉树的条件://条件1:如果某个节点的右子树不为空,则它的左子树必须不为空//条件2:如果某个节点的右子树为空,则排在它后面的节点必须没有孩子节点//排在该节点后面的节点有2种:1)同层次的后面的节点 2)同层次的前面的节点的孩子节点(因为遍历前面的节点的时候,会将节点从队列里pop,同时把它的孩子节点push到队列里)NSMutableArray *queue = [NSMutableArray array];[queue addObject:rootNode];BOOL isComplete = NO; //是否已经满足完全二叉树while (queue.count > 0) {BinaryTreeNode *node = [queue firstObject];[queue removeObjectAtIndex:0];//左子树为空且右子树不为空,则不是完全二叉树if (!node.leftNode && node.rightNode) {return NO;}if (isComplete && (node.leftNode || node.rightNode)) {//前面的节点已满足完全二叉树,如果还有孩子节点,则不是完全二叉树return NO;}//右子树为空,则已经满足完全二叉树if (!node.rightNode) {isComplete = YES;}//压入if (node.leftNode) {[queue addObject:node.leftNode];}if (node.rightNode) {[queue addObject:node.rightNode];}}return isComplete;
}
复制代码

 判断二叉树是否满二叉树

 满二叉树定义为:除了叶结点外每一个结点都有左右子叶且叶子结点都处在最底层的二叉树

 满二叉树的一个特性是:叶子数=2^(深度-1),因此我们可以根据这个特性来判断二叉树是否是满二叉树。

复制代码
/***  是否满二叉树*  满二叉树:除了叶结点外每一个结点都有左右子叶且叶子结点都处在最底层的二叉树**  @param rootNode 根节点**  @return YES:满二叉树,NO:非满二叉树*/
+ (BOOL)isFullBinaryTree:(BinaryTreeNode *)rootNode {if (!rootNode) {return NO;}//二叉树深度NSInteger depth = [self depthOfTree:rootNode];//二叉树叶子节点数NSInteger leafNum = [self numberOfLeafsInTree:rootNode];//满二叉树特性:叶子数=2^(深度-1)if (leafNum == pow(2, (depth - 1))) {return YES;}return NO;
}
复制代码

判断二叉树是否平衡二叉树

平衡二叉树定义为:它是一棵空树或它的左右两个子树的高度差的绝对值不超过1,并且左右两个子树都是一棵平衡二叉树。平衡二叉树又叫AVL树。

复制代码
/***  是否平衡二叉树*  平衡二叉树:即AVL树,它是一棵空树或它的左右两个子树的高度差的绝对值不超过1,并且左右两个子树都是一棵平衡二叉树**  @param rootNode 根节点**  @return YES:平衡二叉树,NO:非平衡二叉树*/
+ (BOOL)isAVLBinaryTree:(BinaryTreeNode *)rootNode {static NSInteger height;if (!rootNode) {height = 0;return YES;}if (!rootNode.leftNode && !rootNode.rightNode) {height = 1;return YES;}BOOL isAVLLeft = [self isAVLBinaryTree:rootNode.leftNode];NSInteger heightLeft = height;BOOL isAVLRight = [self isAVLBinaryTree:rootNode.rightNode];NSInteger heightRight = height;height = MAX(heightLeft, heightRight)+1;if (isAVLLeft && isAVLRight && ABS(heightLeft-heightRight) <= 1) {return YES;}return NO;
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/247888.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

vux 组件库首次使用安装

1、首先通过脚手架新建一个项目&#xff0c;过程略。 创建完项目后&#xff0c;在项目里安装vux&#xff0c; 通过命令 npm install vux --save 安装 2、安装vux-loader&#xff0c; 通过命令 npm install vux-loader --save-dev 安装&#xff08;vux文档没说明&#xff09; 3、…

爬取豆瓣top250

#xpath #第一种方法 可在开发者工具中找到标签&#xff0c;右键copy xpath&#xff0c;有时需去掉tbody标签 #第二种方法 简单学习xpath&#xff0c;自己书写&#xff0c;掌握基本语法即可&#xff0c;简单的层级关系#先将csv文件以记事本打开&#xff0c;更改编码为ASNI&…

LED音乐频谱之点阵

转载请注明出处&#xff1a;http://blog.csdn.net/ruoyunliufeng/article/details/37967455 一.硬件 这里的LED选择直插的雾面LED&#xff0c;亮度可以还不失美观。注意每行要加上限流电阻。74HC138&#xff08;三八译码器&#xff09;作为列选&#xff0c;每行都连着74HC595&a…

上架相关——App Store 上架流程

说实话&#xff0c;公司要上架一个自己做的一个小项目。为了完成这个任务&#xff0c;菜鸟的我一遍找资料一遍跟着做&#xff0c;一遍修改错误一遍查找解决方案。网上的资料大部分都是2015年以前的资料&#xff0c;资料有点不够过时&#xff0c;而且步骤配图也不是很详细&#…

上架相关——appstore 更新app版本

注&#xff1a;此片文章是基于app已经上架&#xff0c;也就是证书都已经配置好的前提下。 首先是还是app打包 修改版本号 修改project处的pp文件 检查无误后打包打包完成后upload to app store 漫长的等待。。 上传到appstore进入iTunesConnect 选择我的app 选择对应app点…

iOS开发 蓝牙技术4.0详解

前言 前端时间,同学在做项目过程中遇到关于蓝牙方面的问题,今天我就给大家进行详细的进行讲解下蓝牙在iOS开发中的具体实现.在介绍蓝牙前,大家要搞清楚什么是蓝牙? 什么是蓝牙? 随着蓝牙低功耗技术BLE&#xff08;Bluetooth Low Energy&#xff09;的发展&#xff0c;蓝牙技术…

前端面试题(五)

position的属性有哪些&#xff1f; 1、absolute生成绝对定位的元素&#xff0c;相对于值不为 static的第一个父元素进行定位。 2、fixed &#xff08;老IE不支持&#xff09;生成绝对定位的元素&#xff0c;相对于浏览器窗口进行定位。 3、relative生成相对定位的元素&#xff…

qrcode.js 二维码生成器

二维码生成 并显示&#xff1a; <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <html xmlns"http://www.w3.org/1999/xhtml" xml:lang"ko" …

AVPlayer设置从哪儿开始播放

avplayer 播放视频 首先介绍几个方法吧和属性吧。 - (id)addPeriodicTimeObserverForInterval:(CMTime)interval queue:(dispatch_queue_t)queue usingBlock:(void (^)(CMTime time))block 这个方法可以用于跟新进度条。 - (void)seekToTime:(CMTime)time completionHandler:(v…

老男孩爬虫实战密训课第一季,2018.6,初识爬虫训练-实战1-爬取汽车之家新闻数据...

1.爬虫介绍 编写程序&#xff0c;根据URL获取网站信息 2.用到的库 requests库 bs4库 3.内容及步骤 4.代码 import requests import os from bs4 import BeautifulSoup # 1.下载页面 ret requests.get(urlhttps://www.autohome.com.cn/news/) ret.encoding ret.apparent_encod…

Table 表格导出功能

<Card class"clearfix"><p slot"title"><Icon type"ios-list"></Icon>收入信息</p><!-- 导出1 --><div class"daochu1"><!-- 导出按钮 --><div class"search"><B…

动态添加后的数据转换 — 后台接收数据

let data this.projectPersonnel.map(item > {let obj {}obj.member item.people.map(info > {return info.id})obj.member JSON.stringify(obj.member)obj.projectId idobj.teamId item.name.idreturn obj})

iOS开发--地图与定位

iOS开发--地图与定位 概览 现在很多社交、电商、团购应用都引入了地图和定位功能&#xff0c;似乎地图功能不再是地图应用和导航应用所特有的。的确&#xff0c;有了地图和定位功能确实让我们的生活更加丰富多彩&#xff0c;极大的改变了我们的生活方式。例如你到了一个陌生的地…

iview组件库 - 穿梭栏设置

<Modalv-model"modal1"title"项目药品上下架维护"width"1020":mask-closable"false"on-ok"addOk()"><Col span"36"><Selectfilterableon-change"onChangeProject"placeholder"请先…

如何优雅地使用Sublime Text3

Sublime Text&#xff1a;一款具有代码高亮、语法提示、自动完成且反应快速的编辑器软件&#xff0c;不仅具有华丽的界面&#xff0c;还支持插件扩展机制&#xff0c;用她来写代码&#xff0c;绝对是一种享受。相比于难于上手的Vim&#xff0c;浮肿沉重的Eclipse&#xff0c;VS…

Windows 聚焦的锁屏壁纸设置为桌面壁纸

需求&#xff1a; Windows的锁屏壁纸偶尔遇到非常喜欢的壁纸&#xff0c;想设置为桌面壁纸。 步骤如下&#xff1a; 1. “Windows 聚焦”的锁屏壁纸都保存在隐藏文件夹 --- Assets里。 a. 打开“资源管理器 b. 在地址栏复制粘贴下方路径后按回车键&#xff0c;即可快速跳转至这…

Chrome 控制台的console用法收集

Chrome 控制台console的用法 大家都有用过各种类型的浏览器&#xff0c;每种浏览器都有自己的特色&#xff0c;本人拙见&#xff0c;在我用过的浏览器当中&#xff0c;我是最喜欢Chrome的&#xff0c;因为它对于调试脚本及前端设计调试都有它比其它浏览器有过之而无不及的地方。…

pycharm安装lxml

今天下午刚学爬虫&#xff0c;要安好多库的感觉&#xff0c;崩溃 requests 首先我们用pip安装完成后&#xff0c;在pycharm里面还要导入进去&#xff0c;没有的话是会报错的 文件--设置--Project Interpreter 然后点击pip进去&#xff0c;搜索requests&#xff0c;再安装进去就…

6.1团队第二阶段冲刺(七)

燃尽图&#xff1a; 任务板: 会议照片&#xff1a; 昨天完成了管理员对发布人的查询功能&#xff0c;条件查询功能以及一系列常用小功能 今天完成了功能说明部分及其那部分界面美化&#xff0c;所有界面的退出以及回到首页的功能及首页美化等 明天打算做信息分页显示&#xff0…

在vue项目中使用树形结构的穿梭框

先看一下最后的效果&#xff1a; 一个基于elementui的穿梭框组件&#xff1a;el-tree-transfer 第一步&#xff1a;安装组件 npm install el-tree-transfer --save 第二步&#xff1a;写代码 // 使用树形穿梭框组件<tree-transfer :title"title" :from_datafromDa…