歡迎光臨
每天分享高質量文章

用if else,switch,while,for顛覆你的程式設計認知

作者:ZhengYaWei

連結:https://www.jianshu.com/p/ceed2daebc47

前言

該篇文章主要會涉及如下幾個問題:

1、if else 和 switch case 在日常開發中該如何抉擇?兩者相比誰的效率會高些?
2、如何基於赫夫曼樹結構減少 if else 分支判斷次數?
3、如何巧妙的應用 do…while(0) 改善程式碼結構?
4、哨兵是什麼東西?如何利用哨兵提高有序陣列查詢效率?
5、如何降低 for 迴圈巢狀的時間複雜度?
6、如何利用策略樣式替換繁瑣的 if else 分支?

雖然該篇文章說來說去都是 if else、switch case、for、while幾個很簡單的關鍵字,但是確實都是一些比較實用的小技巧,希望對你實際工作有所幫助。

PS: 做了回標題黨,見諒?????

一、if else 和 switch case 效率問題

switch case 與 if else 的根本區別在於: switch case 會生成一個跳轉表來指示實際的 case 分支的地址,而這個跳轉表的索引號與switch變數的值是相等的。從而,switch case 不用像 if else 那樣遍歷條件分支直到命中條件,而只需訪問對應索引號的表項從而到達定位分支的目的。switch case 會生成一份大小(表項數)為最大 case 常量 +1 的跳錶,程式首先判斷 switch 變數是否大於最大 case 常量,若大於,則跳到 default 分支處理;否則取得索引號為switch變數大小的跳錶項的地址(即跳錶的起始地址+表項大小*索引號),程式接著跳到此地址執行,到此完成了分支的跳轉。

switch 的缺點主要有兩點: 1、 switch 有點以空間換時間的意思,因為它要生成跳錶,特別是當case常量分佈範圍很大但實際有效值又比較少的情況,switch case 的空間利用率將變得很低。2、if else 能應用於更多的場合,switch case可能就做不來,如:if (a > 1) 。

除此, if else 的效率問題同簡單檔案壓縮原理還有一定的關聯,主要是涉及赫夫曼樹結構知識,具體可以參照筆者之前寫的這篇文章

二、用do while(0) 改善程式碼結構

先看一段程式碼,要重點註意程式碼中的註釋。

- (NSString *)handleString:(NSString *)str {
    if (![str isKindOfClass:[NSString class]]) {
        return nil;
    }
    if(str.length <= 0) {
        return nil;
    }
    // 第一部分邏輯依賴於前面的判斷,只有判斷透過的時候才執行
    我是第一部分邏輯偽程式碼
    // 第二部分邏輯不依賴於前面的判斷(第二部分中的邏輯可能會依賴第一部分邏輯處理結果),無論判斷是否透過都要執行
    我是第二部分邏輯偽程式碼
}

試問,怎樣做才能巧妙的滿足上述註釋程式碼的需求,因為上述程式碼中存在 return nil; 一旦執行到此處,邏輯一和邏輯二處的偽程式碼都不會再執行。為了滿足上述要求,我們可以巧妙的利用 break 退出臨時構造的程式碼塊,但不退出整個函式。

- (NSString *)handleString:(NSString *)str {
    do {
        if (![str isKindOfClass:[NSString class]]) {
            break;
        }
        if(str.length <= 0) {
            break;
        }
        // 第一部分邏輯依賴於前面的判斷,只有判斷透過的時候才執行
        我是第一部分邏輯偽程式碼
    }while (0);    
    // 第二部分邏輯不依賴於前面的判斷(第二部分中的邏輯可能會依賴第一部分邏輯處理結果),無論判斷是否透過都要執行
    我是第二部分邏輯偽程式碼
}

三、有序陣列查詢操作中的哨兵

正常的查詢處理。

    NSArray *arr = @[@1,@2,@3,@4,@5];
    for (NSInteger i = 0; i         if ([arr[i] integerValue] == 2) {
            NSLog(@"for 找到了");
        }
    }

利用哨兵進行查詢處理。

- (BOOL)search:(NSNumber *)key array:(NSMutableArray *)arr{
    if (arr.count <= 0) {
        return NO;
    }
    NSInteger i = arr.count - 1;
    NSNumber *firstObj = (NSNumber *)arr[0];
    if ([firstObj integerValue] == [key integerValue]) {
        return 0;
    }
    NSLock * lock = [[NSLock alloc]init];
    [lock lock];
    arr[0] = key;
    //同上面for迴圈相比,i 
    while ([arr[i] integerValue] != [key integerValue]) {//該句程式碼和上面for迴圈中的if判斷等價======
        i--;//該句程式碼和上面的i+等價======
    }
    arr[0] = firstObj;
    [lock unlock];
    if (i == 0) {
        return NO;
    }else{
        return YES;
    }
}

仔細觀察上述兩段程式碼,同樣是在有序陣列中查詢標的為 2 的元素,第一段程式碼是常規迭代處理,第二段程式碼是將要查詢的元素設定為哨兵。同第一段程式碼相比第二種方式少了 i < arr.count 的判斷,在小批次有序陣列查詢中對效率的提升並無明顯影響,但是在處理大批次資料時候,對效能提升還是比較明顯的。

四、多層 for 巢狀處理

實際開發中應儘量避免使用雙層 for 迴圈,客戶端資料量比較小可能實際開發中並不是很註意這些。但是後端開發過程中,資料量比較大, 為了提升效能,有些公司後端開發中可能會直接規定避免使用多層 for 迴圈巢狀的形式。一般第二層或更深層的 for 迴圈可以使用字典替換。雙層 for 迴圈巢狀的時間複雜度是 n 的二次方。但如果內部 for 迴圈用字典代替時間複雜度為 O(2n)( 實際是 O(n))。如: 兩個陣列中有且只有一個相同元素,尋找該元素。其中一個陣列就可以先用字典做儲存,遍歷第一個陣列的時候, 同字典中的資料做比較即可。

    NSArray *arr1 = @[@1,@2,@3,@4,@5];
    NSArray *arr2 =@[@5,@6,@7,@8];
    NSMutableDictionary *dict = [NSMutableDictionary dictionary];
    for (NSInteger i = 0; i         [dict setObject:arr2[i] forKey:[NSString stringWithFormat:@"%ld",i]];
    }

    for (NSInteger i= 0 ; i         NSNumber *number = [dict objectForKey:[NSString stringWithFormat:@"%ld",i]];
        if ([arr1[i] integerValue] == [number integerValue]) {
            NSLog(@"相同的資料為:%@",number);
            break;
        }   
    }

五、用策略樣式替換 if else

筆者之前這篇文章的第四部分有詳細介紹到,這裡不再做過多描述。

https://www.jianshu.com/p/98fa80eebc52

小結

文章很簡短,但是筆者自認為都是一些很實用的技巧。可能因為if else、switch、while、for 這幾個關鍵字過於簡單,許多開發者並不太註意,也不知道這些技巧。

贊(0)

分享創造快樂