iOS 面试题(一):寻找最近公共 View --转自唐巧
题目:找出两个 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 --转自唐巧的更多相关文章
- iOS开发寻找最近公共view
新技能 #pragma mark --寻找最近公共view + (NSArray *)superViews:(UIView *)view{ if (view==nil) { return @[]; } ...
- 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 ...
- 大厂常问iOS面试题--性能优化篇
1.造成tableView卡顿的原因有哪些? 1.最常用的就是cell的重用, 注册重用标识符 如果不重用cell时,每当一个cell显示到屏幕上时,就会重新创建一个新的cell 如果有很多数据的时候 ...
- 最全的iOS面试题及答案-转载
1. Object-c的类可以多重继承么?可以实现多个接口么?Category是什么?重写一个类的方式用继承好还是分类好?为什么? 答: Object-c的类不可以多重继承:可以实现多个接口,通过实现 ...
- iOS面试题及答案2015.6.7
iOS面试题及答案 1. Object-c的类可以多重继承么?可以实现多个接口么?Category是什么?重写一个类的方式用继承好还是分类好?为什么? 答: Object-c的类不可以多重继承 ...
- [转载]iOS面试题总
转载自:http://blog.sina.com.cn/s/blog_67eb608b0101r6xb.html (2014-06-13 20:23:33) 转载▼ 标签: 转载 crash 原文 ...
- 试答卓同学的 iOS 面试题
卓同学昨天写了一篇文章<4道过滤菜鸟的iOS面试题>.我手痒决定默写一个参考答案.后来发现不认真回答被大家喷成狗,所以决定积极改造,重新做人.下面就是修编之后的答案. 1. struct和 ...
- iOS面试题大全-点亮你iOS技能树
所有的内容大部分来自于网络的搜集,所以我不是一个创造者,而是一个搬运工.我尽量把题目,尤其是参考答案的出处列明.若有任何疑问,建议,意见,请联系我. 第一部分面试题来源于iOS-Developer-I ...
- 原 iOS面试题收集
原 iOS面试题收集 发表于2年前(2013-07-22 13:47) 阅读(369) | 评论(0) 4人收藏此文章, 我要收藏 赞0 听云性能监测产品App.Server.CDN免费试用,绑定 ...
随机推荐
- Knockout.js随手记(5)
以列表方式呈现数据 处理以数组形式储存的多条数据,要先认识foreach.在ViewModel定义一个JavaScript Array或是ko.observableArray() (observab ...
- MVC Controller Dependency Injection for Beginners【翻译】
在codeproject看到一篇文章,群里的一个朋友要帮忙我翻译一下顺便贴出来,这篇文章适合新手,也算是对MEF的一个简单用法的介绍. Introduction In a simple stateme ...
- Html 移动web开发细节处理
1.-webkit-tap-highlight-color:rgba(255,255,255,0)可以同时屏蔽ios和android下点击元素时出现的阴影.备注:transparent的属性值在and ...
- mysql数据库链接与创建
有童鞋问到说,环境搭建好了,mysql也安装了,但是就是进不去数据库,也启动不了,一直报错,那么下面这边就说下如何用Navicat链接上创建的数据库 首先 1)在xshell里进入mysql,命令是: ...
- python安装库
首先确保安装了pip,并且pip也加入了系统path路径: pip下载:https://pypi.python.org/pypi/pip#downloads 下载Python对应的包:(http:// ...
- lua52 C API测试代码
//这是一篇lua与C++交互的情景测试 #include <lua.hpp> #include <lauxlib.h> #include <lualib.h> # ...
- 分布式缓存技术redis学习系列(三)——redis高级应用(主从、事务与锁、持久化)
上文<详细讲解redis数据结构(内存模型)以及常用命令>介绍了redis的数据类型以及常用命令,本文我们来学习下redis的一些高级特性. 安全性设置 设置客户端操作秘密 redis安装 ...
- XML序列化及反序列化
//对象序列化xml OutModel outmodel = new OutModel(); XmlSerializer serializer = new XmlSerializer(typeof(O ...
- android--handler
app在启动时会产生一个进程和一个线程,线程是主线程,又叫UI线程,更新UI元素必须要在UI线程中更新,否则会报错. 在UI线程中有消息队列,子线程sendMessage到MQ中,looper类取出队 ...
- hibernate 的SessionFactory的getCurrentSession 与 openSession() 的区别
1 getCurrentSession创建的session会和绑定到当前线程,而openSession不会. 2 getCurrentSession创建的线程会在事务回滚或事物提交后自动关闭,而ope ...