题目:找出两个 UIView 的最近的公共 View,如果不存在,则输出 nil 。

分析:这其实是数据结构里面的找最近公共祖先的问题。

一个 UIViewController 中的所有 view 之间的关系其实可以看成一颗树,UIViewController 的 view 变量是这颗树的根节点,其它的 view 都是根节点的直接或间接子节点。

所以我们可以通过 view 的 superview 属性,一直找到根节点。需要注意的是,在代码中,我们还需要考虑各种非法输入,如果输入了 nil,则也需要处理,避免异常。以下是找到指定 view 到根 view 的路径代码:

+ (NSArray *)superViews:(UIView *)view {
if (view == nil) {
return @[];
}
NSMutableArray *result = [NSMutableArray array];
while (view != nil) {
[result addObject:view];
view = view.superview;
}
return [result copy];
}

然后对于两个 view A 和 view B,我们可以得到两个路径,而本题中我们要找的是这里面最近的一个公共节点。

一个简单直接的办法:拿第一个路径中的所有节点,去第二个节点中查找。假设路径的平均长度是 N,因为每个节点都要找 N 次,一共有 N 个节点,所以这个办法的时间复杂度是 O(N^2)。

+ (UIView *)commonView_1:(UIView *)viewA andView:(UIView *)viewB {
NSArray *arr1 = [self superViews:viewA];
NSArray *arr2 = [self superViews:viewB];
for (NSUInteger i = ; i < arr1.count; ++i) {
UIView *targetView = arr1[i];
for (NSUInteger j = ; j < arr2.count; ++j) {
if (targetView == arr2[j]) {
return targetView;
}
}
}
return nil;
}

一个改进的办法:我们将一个路径中的所有点先放进 NSSet 中。因为 NSSet 的内部实现是一个 hash 表,所以查找元素的时间复杂度变成了 O(1),我们一共有 N 个节点,所以总时间复杂度优化到了 O(N)。

+ (UIView *)commonView_2:(UIView *)viewA andView:(UIView *)viewB {
NSArray *arr1 = [self superViews:viewA];
NSArray *arr2 = [self superViews:viewB];
NSSet *set = [NSSet setWithArray:arr2];
for (NSUInteger i = ; i < arr1.count; ++i) {
UIView *targetView = arr1[i];
if ([set containsObject:targetView]) {
return targetView;
}
}
return nil;
}

除了使用 NSSet 外,我们还可以使用类似归并排序的思想,用两个「指针」,分别指向两个路径的根节点,然后从根节点开始,找第一个不同的节点,第一个不同节点的上一个公共节点,就是我们的答案。代码如下:

/* O(N) Solution */
+ (UIView *)commonView_3:(UIView *)viewA andView:(UIView *)viewB {
NSArray *arr1 = [self superViews:viewA];
NSArray *arr2 = [self superViews:viewB];
NSInteger p1 = arr1.count - ;
NSInteger p2 = arr2.count - ;
UIView *answer = nil;
while (p1 >= && p2 >= ) {
if (arr1[p1] == arr2[p2]) {
answer = arr1[p1];
}
p1--;
p2--;
}
return answer;
}

我们还可以使用 UIView 的 isDescendant 方法来简化我们的代码,不过这样写的话,时间复杂度应该也是 O(N^2) 的。lexrus 提供了如下的 Swift 版本的代码:

/// without flatMap
extension UIView {
func commonSuperview(of view: UIView) -> UIView? {
if let s = superview {
if view.isDescendant(of: s) {
return s
} else {
return s.commonSuperview(of: view)
}
}
return nil
}
}

特别地,如果我们利用 Optinal 的 flatMap 方法,可以将上面的代码简化得更短,基本上算是一行代码搞定。怎么样,你学会了吗?

extension UIView {
func commonSuperview(of view: UIView) -> UIView? {
return superview.flatMap {
view.isDescendant(of: $) ?
$ : $.commonSuperview(of: view)
}
}
}

iOS 面试题(一):寻找最近公共 View --转自唐巧的更多相关文章

  1. iOS开发寻找最近公共view

    新技能 #pragma mark --寻找最近公共view + (NSArray *)superViews:(UIView *)view{ if (view==nil) { return @[]; } ...

  2. iOS之面试题:腾讯三次面试以及参考思路

    使用了第三方库, 有看他们是怎么实现的吗? 例:SD.YY.AFN.MJ等! <1>.SD为例: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 ...

  3. 大厂常问iOS面试题--性能优化篇

    1.造成tableView卡顿的原因有哪些? 1.最常用的就是cell的重用, 注册重用标识符 如果不重用cell时,每当一个cell显示到屏幕上时,就会重新创建一个新的cell 如果有很多数据的时候 ...

  4. 最全的iOS面试题及答案-转载

    1. Object-c的类可以多重继承么?可以实现多个接口么?Category是什么?重写一个类的方式用继承好还是分类好?为什么? 答: Object-c的类不可以多重继承:可以实现多个接口,通过实现 ...

  5. iOS面试题及答案2015.6.7

    iOS面试题及答案     1. Object-c的类可以多重继承么?可以实现多个接口么?Category是什么?重写一个类的方式用继承好还是分类好?为什么? 答: Object-c的类不可以多重继承 ...

  6. [转载]iOS面试题总

    转载自:http://blog.sina.com.cn/s/blog_67eb608b0101r6xb.html (2014-06-13 20:23:33) 转载▼ 标签: 转载   crash 原文 ...

  7. 试答卓同学的 iOS 面试题

    卓同学昨天写了一篇文章<4道过滤菜鸟的iOS面试题>.我手痒决定默写一个参考答案.后来发现不认真回答被大家喷成狗,所以决定积极改造,重新做人.下面就是修编之后的答案. 1. struct和 ...

  8. iOS面试题大全-点亮你iOS技能树

    所有的内容大部分来自于网络的搜集,所以我不是一个创造者,而是一个搬运工.我尽量把题目,尤其是参考答案的出处列明.若有任何疑问,建议,意见,请联系我. 第一部分面试题来源于iOS-Developer-I ...

  9. 原 iOS面试题收集

    原 iOS面试题收集 发表于2年前(2013-07-22 13:47)   阅读(369) | 评论(0) 4人收藏此文章, 我要收藏 赞0 听云性能监测产品App.Server.CDN免费试用,绑定 ...

随机推荐

  1. 【MongoDB】 基于C#官方驱动2.2版的封装类

    一.前言 最近项目中要用到MongoDB,因此实现做了不少的调研.发现网上很多现有关于MongoDB C#官方驱动的调用方法都是基于1.8版本的,已经不是用了最新的2.2版本.因此我在基于C#官方驱动 ...

  2. Jquery Mobile开发以及Js对象动态绑定

    动态创建对象并绑定属性: var instantiate = function (Type, args) { var Constructor = function () { }; Constructo ...

  3. AutoMapper搬运工之配置

    回顾 前几篇搬运了AutoMapper的基本用法,自定义映射,相信有看的同学已经会使用AutoMapper这个强大的Mapping工具了.不过细心的你是否还记得前几篇中有提到Map的创建并非是每次都需 ...

  4. form-line 样式 让 两个控件在同一个水平位置

    <div class="row"> <div> <label class="form-inline">参加单位:<in ...

  5. 什么是shell

    Shell本身是一个用C语言编写的程序,它是用户使用Linux的桥梁.Shell既是一种命令语言,又是一种程序设计语言.作为命令语言,它交互式地解释和执行用户输入的命令:作为程序设计语言,它定义了各种 ...

  6. T-SQL Recipes之 Table Variables and Temporary Tables

    Problem 许多时候, 我们想要Table Variables在动态SQL中执行,但现实是很骨感的.比如这个示例: DECLARE @sql_command NVARCHAR(MAX); DECL ...

  7. iptables 开启端口

    1.开启iptables端口 开启1521端口: iptables -A INPUT -p tcp --dport  -j ACCEPT iptables -A OUTPUT -p tcp --dpo ...

  8. Javascript并发模型和事件循环

    Javascript并发模型和事件循环 JavaScript的"并发模型"是基于事件循环的,这个并发模型有别于Java的多线程, javascript的并发是单线程的. Javas ...

  9. BZOJ1493 [NOI2007]项链工厂

    未完待续... 终于改对了 热泪盈眶.jpg 错误原因:pushdown的时候没有判断是否有左右儿子,也没当x=0 return,于是出现一些奇怪的错误 #include<bits/stdc++ ...

  10. js 闭包之一

    既然说闭包的化,我们就先来说说函数.慢慢的进入进入正题 (1)函数申明 f1(); function f1(){ alert(") }//结果 1 (2)函数定义 f1();var f1=f ...