因为咱们后面讲的一些结构有不少是树结构实现的好比堆,而后基于堆能够实现优先级队列,有界优先级队列等,因此咱们先讲述树结构,咱们可能常见到的是二叉树,可是还有一些其余的树的概念:好比二叉搜索树,AVL树,红黑树,B树,决策树等。以便于在特定场景下使用。node
hit-test 逻辑:此方法经过hitTest:withEvent:从最后到第一个向其每一个子视图发送消息来遍历接收者的子树,直到其中一个返回非nil值。ios
代码:git
- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event {
if (!self.isUserInteractionEnabled || self.isHidden || self.alpha <= 0.01) {
return nil;
}
if ([self pointInside:point withEvent:event]) {
for (UIView *subview in [self.subviews reverseObjectEnumerator]) {
CGPoint convertedPoint = [subview convertPoint:point fromView:self];
UIView *hitTestView = [subview hitTest:convertedPoint withEvent:event];
if (hitTestView) {
return hitTestView;
}
}
return self;
}
return nil;
}
复制代码
采用reverse pre-order depth-first traversal algorithm遍历。首先访问根节点,而后从较高到较低的索引遍历其子树,这样作是为了快速遍历到咱们须要的节点,试想若是从低到高遍历这个View,层级不少的状况下岂不是要遍历不少节点。以下图所示:github
好比“View A.2”和“View B.1”都是重叠的。但因为“View B”的子视图索引高于“View A”,所以“View B”及其子视图呈如今“View A”及其子视图上方。所以,当用户的手指在与“视图A.2”重叠的区域中触摸“视图B.1”时,应经过命中测试返回“视图B.1”。算法
一个小提示打印当前View下全部子View采用了递归遍历并打印,此刻看出来算法的重要性了吧。数据库
- (void)listSubviewsOfView:(UIView *)view {
NSArray *subviews = [view subviews];
if ([subviews count] == 0) return;
for (UIView *subview in subviews)
{
NSLog(@"%@", subview);
[self listSubviewsOfView:subview];
}
}
复制代码
上图后序遍历获得后缀表达式:( ((70 10 - )32 / ) (24 13 + ) X )数组
二叉树的节点最大分支度是2,也说明每一个节点最多拥有2个子节点,范围是[0-2]。bash
下面咱们推导下Max Nodes。上图第三种状况h = 3,Max Nodes = 1 +2 + 22+ 23 = 15,也就是Max Nodes = 1 +2 + 22+ 23 + ....+ 2h= ,也就是等比数列求和,以下图:数据结构
等比数列求和能够参考以下连接: zh.wikipedia.org/wiki/等比数列ide
反过来能够很容易推导出Min Height (h = Log2(n+1)-1),Max Height(h = n-1)。
若是是full binary tree那么节点数和树Height的关系又是什么呢? 推导过程能够参考上面的步骤,Min Nodes(n = 2h+1),Max Nodes(2h+1-1),反过来能够很容易推导出Min Height (h = Log2(n+1)-1),Max Height(h = )。
第i层至多拥有2i-1个节点,最少有1个节点。从下图能够很容易看出来,
度为0的节点数n1和度为2节点数n2的关系。n1 = n2 + 1。看下图
二叉树能够被以广度优先的顺序做为隐式数据结构存储在数组中。注意的是若是这个二叉树是complete binary tree,这些不会浪费空间,可是若是对于A degenerate (or pathological) tree这种高度很大的树就很浪费空间,能够参考后面根据这个存储方式判断这个树是否是complete binary tree的介绍。这种存储方法一般也用在binary heaps。
举例:找E的父节点,E的索引是5,那么Parent = i/2 = 5/2 = 2.5,向下取整就是2,对应的就是B。反之假如找A的左右孩子,A的索引是1,那么左孩子索引就是2对应B,右孩子索引就是3对应C。
注意:Parent的索引若是有存在小数状况是向下取整。
下面咱们看怎么根据这个表示方法判断是否是complete binary tree。
@interface DSTreeNode : NSObject
@property (nonatomic, strong) NSObject *object;
@property (nonatomic, strong) DSTreeNode *leftChild;
@property (nonatomic, strong) DSTreeNode *rightChild;
@property (nonatomic, strong) DSTreeNode *parent;
@property (nonatomic, assign) SEL compareSelector;
- (void)printDescription;
//是不是左仍是结点
- (BOOL)isLeftChildOfParent;
@end
复制代码
这种存储二叉树方法浪费了很多内存,因为那些节点的左右指针(为null或者指向某些节点)。
经过上述四种方式遍历二叉树的每一个节点。
思路:通常咱们习惯 ,根节点-左节点-右节点,这样的模型,咱们就把例如上图A的左子树当作一个块,相似一个大节点用括号圈起来,一样的右子树也这样作。而后每一个块里作前中后遍历。
前序遍历。A,(B,D,E),(C,F,G)。获得结果是 A,B,D,E,C,F,G 。
中序遍历。(D,B,E),A,(F,C,G)。获得的结果是 D,B,E,A,F,C,G 。
后序遍历。(D,E,B),(F,G,C),A。获得的结果是 D,E,B,F,G,C,A 。
层级遍历。 A,B,C,D,E,F,G 。
前序遍历思路:每一个节点从左边画线一直到底部这个线,而后按照从左到右的顺序读取节点。 结果是:A,B,D,E,C,F,G 。
中序遍历思路:每一个节点从中间画线到底部这个线,而后按照从左到右的顺序读取节点。 结果是 D,B,E,A,F,C,G 。
后序遍历思路:每一个节点从右边画线到底部这条线,而后从左到右的顺序读取节点。 结果是 D,E,B,F,G,C,A 。
前序遍历思路:从每一个节点左边画出一个线,而后从根结点开始转一圈,通过每一个节点和树的分支,包裹这个树。通过这些短线的顺序就是结果。A,B,D,E,C,F,G 。
中序遍历思路:从每一个节点底部边画出一个线,而后从根结点开始转一圈,通过每一个节点和树的分支,包裹这个树。通过这些短线的顺序就是结果。D,B,E,A,F,C,G 。
后序遍历思路:从每一个节点右边画出一个线,而后从根结点开始转一圈,通过每一个节点和树的分支,包裹这个树。通过这些短线的顺序就是结果。D,E,B,F,G,C,A 。
这节主要介绍二叉树的代码实现,咱们讲述Linked Representation的实现,主要包含下面几个操做。
从上图能够看出,每一个节点除了自己之外,还得有一个父子以及左右孩子节点信息。所以须要一个节点类。主要代码实现以下:
@interface DSTreeNode : NSObject
@property (nonatomic, strong) NSObject *object;
@property (nonatomic, strong) DSTreeNode *leftChild;
@property (nonatomic, strong) DSTreeNode *rightChild;
@property (nonatomic, strong) DSTreeNode *parent;
@property (nonatomic, assign) SEL compareSelector;
- (void)printDescription;
//是不是左仍是结点
- (BOOL)isLeftChildOfParent;
@end
复制代码
对于二叉树的建立咱们初始化一个根节点的方式建立,以下代码实现:
- (instancetype)initWithObject:(NSObject *)object
{
if (self = [super init]) {
_root = [[DSTreeNode alloc] init];
self.root.object = object;
}
return self;
}
复制代码
以插入节点的方式构建整个二叉树以下代码:
//插入结点
- (BOOL)insertNode:(NSObject *)node parent:(NSObject *)parent isLeftChild:(BOOL)value
{
DSTreeNode *treeNode = [[DSTreeNode alloc] init];
treeNode.object = node;
DSTreeNode *parentNode = [self find:parent];
//1
if (value == true && parentNode.leftChild == nil) {
//2
treeNode.parent = parentNode;
//3
parentNode.leftChild = treeNode;
}
//4
else if (parentNode.rightChild == nil) {
treeNode.parent = parentNode;
parentNode.rightChild = treeNode;
}
//5
else {
NSAssert(parentNode.leftChild != nil || parentNode.rightChild != nil, @"Can't insert into parent node!");
return false;
}
return true;
}
复制代码
代码解释:
查找某个节点
- (DSTreeNode *)find:(NSObject *)object
{
//1
DSQueue*queue = [[DSQueue alloc] init];
[queue enqueue:self.root];
DSTreeNode *node;
//2
while (![queue isEmpty]) {
node = [queue dequeue];
if ([node.object isEqualTo:object]) {
return node;
}
if (node.leftChild) {
[queue enqueue:node.leftChild];
}
if (node.rightChild) {
[queue enqueue:node.rightChild];
}
}
return nil;
}
复制代码
层级遍历的思路和上述查找的思路相似。前中后序遍历的思路利用递归的思路实现,而后按照以前介绍二叉树遍历算法的思路就能够实现了。前序遍历的代码以下:
//若是当前根结点存在则前序遍历这个树
- (void)preOrderTraversal
{
if (self.root) {
[DSBinaryTree preOrderTraversalRecursive:self.root];
}
}
//递归的遍历并打印树 顺序是根 左 右
+ (void)preOrderTraversalRecursive:(DSTreeNode *)node
{
if (node) {
NSLog(@"%@",node.object);
[DSBinaryTree preOrderTraversalRecursive:node.leftChild];
[DSBinaryTree preOrderTraversalRecursive:node.rightChild];
}
}
复制代码
Given a binary tree, return all root-to-leaf paths.
For example, given the following binary tree:
1
/ \
2 3
\
5
All root-to-leaf paths are:
["1->2->5", "1->3"]
复制代码
给咱们一个二叉树,让咱们返回全部根到叶节点的路径。咱们能够采用递归的思路,不停的DFS到叶结点,若是遇到叶结点的时候,那么此时一条完整的路径已经造成,咱们加上当前的叶结点后变成的完整路径放到数组中。
须要注意的是对空节点的判断,以及递归函数回溯时候对一些对象的影响。
- (void)printPathsRecurTreeNode:(DSTreeNode *)treeNode path:(NSString *)path results:(NSMutableArray <NSString *>*)results
{
//1
if (treeNode == nil) {
return;
}
//2
if (treeNode.leftChild == nil && treeNode.rightChild == nil)
{
NSString *resultsStr = [NSString stringWithFormat:@"%@%@",path,treeNode.object];
[results addObject:resultsStr];
}
else
{
//3
if (treeNode.leftChild != nil)
{
NSString *resultsStr = [NSString stringWithFormat:@"%@%@",path,[NSString stringWithFormat:@"%@->",treeNode.object]];
[self printPathsRecurTreeNode:treeNode.leftChild path:resultsStr results:results];
}
//4
if (treeNode.rightChild != nil )
{
NSString *resultsStr = [NSString stringWithFormat:@"%@%@",path,[NSString stringWithFormat:@"%@->",treeNode.object]];
[self printPathsRecurTreeNode:treeNode.rightChild path:resultsStr results:results];
}
}
}
复制代码