找出兩個(gè)UIView的最近的公共View,如果不存在,則輸出nil

找出兩個(gè)UIView的最近的公共View,如果不存在,則輸出nil

分析:這其實(shí)就是數(shù)據(jù)結(jié)構(gòu)里面的找最近公共祖先的問(wèn)題。
一個(gè)UIViewController中的所有view之間的關(guān)系其實(shí)可以看成一棵樹(shù),UIViewControllerview變量就是這棵樹(shù)的根節(jié)點(diǎn),其他的view都是根節(jié)點(diǎn)的直接或間接子節(jié)點(diǎn)。
所以我們可以通過(guò)view的superview屬性,一直找到根節(jié)點(diǎn)。需要注意的是,在代碼中,我們還需要考慮到各種非法輸入,如果輸入了nil,則也需要處理,避免異常。以下是找到指定view到根view的路徑代碼:

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

然后對(duì)于兩個(gè)viewAviewB,我們可以得到兩個(gè)路徑,而本題中我們要找的是這里面最近的一個(gè)公共節(jié)點(diǎn)。

一個(gè)簡(jiǎn)單直接的方法:拿第一個(gè)路徑中的所有節(jié)點(diǎn),去第二個(gè)節(jié)點(diǎn)中查找。假設(shè)路徑的平均長(zhǎng)度是N,因?yàn)槊總€(gè)節(jié)點(diǎn)都要找N次,一共有N個(gè)節(jié)點(diǎn),所以這個(gè)辦法的時(shí)間復(fù)雜度是0(N^2)。

+(UIView *)commonView_1:(UIView *)viewA andView:(UIView *)viewB{
    NSArray *array1 = [self superView:viewA];
    NSArray *array2 = [self superView:viewB];
    for(NSInteger i = 0; i < array1.count; i++){
        UIView *targetView = array1[1];
        for(NSInteger j = 0; j < array2.count; j++){
            if(targetView == array2[j]){
                return targetView;
            }
        }
    }
    return nil;
}

一個(gè)改進(jìn)的方法:我們將一個(gè)路徑中的所有節(jié)點(diǎn)先放進(jìn)NSSet[1]中。因?yàn)镹SSet的內(nèi)部實(shí)現(xiàn)是一個(gè)hash表,所以查找元素的時(shí)間復(fù)雜度變成了O(1),我們一共有N個(gè)節(jié)點(diǎn),所以總的時(shí)間復(fù)雜度就優(yōu)化到了O(N)。

+(UIView *)commonView_2:(UIView *)viewA andView:(UIView *)viewB{
    NSArray *array1 = [self superView:viewA];
    NSArray *array2 = [self superView:viewB];
    NSSet *set = [NSSet setWithArray:array2];
    for(NSInteger i = 0; i < array1.count; i++){
        UIView *targetView = array1[1];
        if([set containsObject:targetView ]){
            return targetView;
        }
    }
    return nil;
}

除了使用NSSet外,我們還可以使用類(lèi)似歸并排序[2]的思想,用兩個(gè)‘指針’,分別指向兩個(gè)路徑的根節(jié)點(diǎn),然后從根節(jié)點(diǎn)開(kāi)始,找到第一個(gè)不同的節(jié)點(diǎn),第一個(gè)不同的節(jié)點(diǎn)的上一個(gè)公共節(jié)點(diǎn)就是我們的答案。時(shí)間復(fù)雜度依然是O(N) 代碼如下:

+(UIView *)commonView_3:(UIView *)viewA andView:(UIView *)viewB{
    NSArray *array1 = [self superView:viewA];
    NSArray *array2 = [self superView:viewB];
    NSSet *set = [NSSet setWithArray:array2];
    NSInteger p1 = array1.count - 1;
    NSInteger P2 = array2.count - 1;
    UIView *answerView = nil;
    While(p1 >= 0 && p2 >= 0){
        if(array[p1] == array2[p2])
            answerView  = array[p1];
        }
        p1--;
        p2--;
    }
    return answerView  ;
}

我們還可以使用UIViewisDescendant方法來(lái)簡(jiǎn)化我們的代碼,不過(guò)這樣寫(xiě)的話,時(shí)間復(fù)雜度應(yīng)該也是O(N^2)。判斷view是不是指定視圖的子視圖 -->BOOL isView = [textView isDescendantOfView:self.view];
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
    }
}

特別地,如果我們利用Optional的flatMap方法,可以將上面的代碼簡(jiǎn)化得更短,基本上算是一行代碼搞定。

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

本文摘自唐巧大神的微信訂閱號(hào)
原文鏈接:https://mp.weixin.qq.com/s/u_RkTqxTY8X1fDURNT2C9Q


  1. NSSet:它和NSArray功能性質(zhì)一樣,用于存儲(chǔ)對(duì)象,屬于集合;無(wú)序的集合,在內(nèi)存中存儲(chǔ)方式是不連續(xù)的。比如你要存儲(chǔ)元素A,一個(gè)hash算法直接就能直接找到A應(yīng)該存儲(chǔ)的位置;同樣,當(dāng)你要訪問(wèn)A時(shí),一個(gè)hash過(guò)程就能找到A存儲(chǔ)的位置。而對(duì)于NSArray,若想知道A到底在不在數(shù)組中,則需要便利整個(gè)數(shù)組,顯然效率較低了;
    https://www.cnblogs.com/zxykit/p/5556435.html ?

  2. 歸并排序:假設(shè)數(shù)據(jù)中有8個(gè)元素,先分為四組進(jìn)行比較,之后分為兩組進(jìn)行比較,最后分為一組進(jìn)行比較,這樣就衍生出來(lái)兩種方法,一種是自頂向下的歸并排序,一種是自底向上的歸并排序。https://www.cnblogs.com/xiaofeixiang/p/4589954.html ?

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容