Objective-c各種排序算法

作者:Fly晴天里Fly

Objective-C排序算法

根據(jù)將排序記錄是否全部放置在內(nèi)存中尊沸,將排序分為內(nèi)排序和外排序谤祖,之前講的都是內(nèi)排序兄世,這里總結(jié)一下敬拓,內(nèi)排序分為四類:插入排序邻薯、交換排序、選擇排序和歸并排序乘凸。

image

快排

快速排序是面試中經(jīng)常會被問的一個排序算法厕诡。一般要求手寫。
快排是對冒泡排序的一種改進营勤。
平均時間復(fù)雜度O(nlogn)灵嫌,最壞時間復(fù)雜度O(n*n)

因為二分查找的時間復(fù)雜度是o(logn), 每次分成兩段壹罚,那么分的次數(shù)就是logn了,每一次處理需要n次計算寿羞,那么時間復(fù)雜度就是nlogn了猖凛!

最壞是O(n^2). 這種情況就是數(shù)組剛好的倒序,然后每次去中間元的時候都是取最大或者最小绪穆。
穩(wěn)定性:不穩(wěn)定辨泳。

快速排序時間復(fù)雜度為O(n×log(n))的證明


+ (void)quickSort:(NSMutableArray *)arr low:(NSInteger)low high:(NSInteger)high {
    if (arr.count == 0 || low >= high) {
        return;
    }

    NSInteger i = low, j = high;
    NSInteger key = [arr[low] integerValue];

    while (i < j && [arr[j] integerValue] >= key) {
        j--;
    }

    arr[i] = arr[j];

    while (i < j && [arr[i] integerValue] <= key) {
        i++;
    }

    arr[j] = arr[i];

    NSLog(@"一次排序結(jié)果:%@", arr);

    arr[i] = @(key);

    [[self class] quickSort:arr low:low high:i-1];
    [[self class] quickSort:arr low:i+1 high:high];
}

堆排序

堆排序是一種選擇排序,其時間復(fù)雜度為O(nlogn)玖院。
定義:

image

堆的存儲

image

其基本思想為(大頂堆):

  1. 將初始待排序關(guān)鍵字序列(R1,R2....Rn)構(gòu)建成大頂堆漠吻,此堆為初始的無序區(qū);
  2. 將堆頂元素R[1]與最后一個元素R[n]交換司恳,此時得到新的無序區(qū)(R1,R2,......Rn-1)和新的有序區(qū)(Rn),且滿足R[1,2...n-1]<=R[n];
  3. 由于交換后新的堆頂R[1]可能違反堆的性質(zhì)途乃,因此需要對當(dāng)前無序區(qū)(R1,R2,......Rn-1)調(diào)整為新堆,然后再次將R[1]與無序區(qū)最后一個元素交換扔傅,得到新的無序區(qū)(R1,R2....Rn-2)和新的有序區(qū)(Rn-1,Rn)耍共。不斷重復(fù)此過程直到有序區(qū)的元素個數(shù)為n-1,則整個排序過程完成猎塞。

因此對于堆排序试读,最重要的兩個操作就是構(gòu)造初始堆和調(diào)整堆,其實構(gòu)造初始堆事實上也是調(diào)整堆的過程荠耽,只不過構(gòu)造初始堆是對所有的非葉節(jié)點都進行調(diào)整钩骇。
由于堆也是用數(shù)組模擬的,故堆化數(shù)組后铝量,第一次將A[0]與A[n - 1]交換倘屹,再對A[0…n-2]重新恢復(fù)堆。第二次將A[0]與A[n – 2]交換慢叨,再對A[0…n - 3]重新恢復(fù)堆纽匙,重復(fù)這樣的操作直到A[0]與A[1]交換。由于每次都是將最大的數(shù)據(jù)并入到后面的有序區(qū)間拍谐,故操作完成后整個數(shù)組就有序了烛缔。

作為一個開發(fā)者,有一個學(xué)習(xí)的氛圍跟一個交流圈子特別重要轩拨,這是一個我的iOS交流群:761407670 進群密碼123践瓷,不管你是小白還是大牛歡迎入駐 ,分享BAT,阿里面試題亡蓉、面試經(jīng)驗晕翠,討論技術(shù), 大家一起交流學(xué)習(xí)成長寸宵!

另附上一份各好友收集的大廠面試題崖面,進群可自行下載!


已知一棵完全二叉樹各節(jié)點的編號為0到n梯影,如何得出其第一個非葉子節(jié)點的編號
最后一個葉子節(jié)點的索引值是n-1巫员,它的父節(jié)點索引值是[(n-1)-1]/2 = n/2 -1

/* (最大)堆的向下調(diào)整算法
  *
  * 注:數(shù)組實現(xiàn)的堆中,第N個節(jié)點的左孩子的索引值是(2N+1)甲棍,右孩子的索引是(2N+2)简识。
  *     其中,N為數(shù)組下標(biāo)索引值感猛,如數(shù)組中第1個數(shù)對應(yīng)的N為0七扰。
  *
  * 參數(shù)說明:
  *     a -- 待排序的數(shù)組
  *     start -- 被下調(diào)節(jié)點的起始位置(一般為0,表示從第1個開始)
  *     end   -- 截至范圍(一般為數(shù)組中最后一個元素的索引)
*/
- (void)heapAdjust:(NSMutableArray *)marr start:(int)start end:(int)end {
    int lchild = 2*start; //i的左孩子節(jié)點序號
    int rchild = 2*start+1; //i的右孩子節(jié)點序號
    int max = start;  //臨時變量

    if (start <= end/2) {  //如果i是葉節(jié)點就不用進行調(diào)整

        if (lchild <= end && [marr[lchild] integerValue] > [marr[max] integerValue]) {
            max = lchild;
        }

        if (rchild <= end && [marr[rchild] integerValue] > [marr[max] integerValue] ) {
            max = rchild;
        }

        if (max != start) {
            [marr exchangeObjectAtIndex:start withObjectAtIndex:max];
            // 避免調(diào)整之后以max為父節(jié)點的子樹不是堆
            [self heapAdjust:marr start:max end:end];
        }
    }
}

// 建大根堆
- (void)buildHeap:(NSMutableArray *)marr  {
    int size = (int)marr.count;
    // 最后一個葉子節(jié)點的索引值是n-1陪白,它的父節(jié)點索引值是[(n-1)-1]/2 = n/2 -1
    for (int i = size/2-1; i >= 0; --i) {
        [self heapAdjust:marr start:i end:size-1];
    }
}

- (void)heapSort:(NSMutableArray *)marr {
    [self buildHeap:marr];

    NSLog(@"build after: %@", marr);

    int len = (int)marr.count-1;
    for (int i = len; i >= 0; i--) {
        // 交換堆頂和最后一個元素颈走,即每次將剩余元素中的最大者放到最后面
        [marr exchangeObjectAtIndex:0 withObjectAtIndex:i];
        // 重新調(diào)整堆頂節(jié)點成為大頂堆
        [self heapAdjust:marr start:0 end:i-1];
    }
}

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        NSArray *arr = @[@(12), @(14), @(100),  @(29), @(8), @(21), @(15), @(59), @(50), @(99)];
        NSMutableArray *marr = [NSMutableArray arrayWithArray:arr];
        SGSort *sort = [[SGSort alloc] init];
        [sort heapSort:marr];
        NSLog(@"heapsort result =%@", marr);
    }
    return 0;
}

// 輸出結(jié)果
2017-08-27 19:55:04.657 SuanFaXueXi[3487:106882] build after: (
    100,
    99,
    50,
    59,
    14,
    21,
    15,
    29,
    12,
    8
)
2017-08-27 19:55:07.151 SuanFaXueXi[3487:106882] heapsort result =(
    8,
    12,
    14,
    15,
    21,
    29,
    50,
    59,
    99,
    100
)

由于每次重新恢復(fù)堆的時間復(fù)雜度為O(logN),共N - 1次重新恢復(fù)堆操作咱士,再加上前面建立堆時N / 2次向下調(diào)整立由,每次調(diào)整時間復(fù)雜度也為O(logN)。二次操作時間相加還是O(N * logN)序厉。故堆排序的時間復(fù)雜度為O(N * logN)

堆排序適合于數(shù)據(jù)量非常大的場合(百萬數(shù)據(jù))锐膜。
堆排序不需要大量的遞歸或者多維的暫存數(shù)組。這對于數(shù)據(jù)量非常巨大的序列是合適的弛房。比如超過數(shù)百萬條記錄道盏,因為快速排序,歸并排序都使用遞歸來設(shè)計算法文捶,在數(shù)據(jù)量非常大的時候荷逞,可能會發(fā)生堆棧溢出錯誤。

歸并排序

希爾排序

基本思想是:先將整個待排記錄序列分割成為若干子序列分別進行直接插入排序粹排,待整個序列中的記錄基本有序時再對全體記錄進行一次直接插入排序颅围。

計數(shù)排序

如果在面試中有面試官要求你寫一個O(n)時間復(fù)雜度的排序算法,你千萬不要立刻說:這不可能恨搓!雖然前面基于比較的排序的下限是O(nlogn)院促。但是確實也有線性時間復(fù)雜度的排序,只不過有前提條件斧抱,就是待排序的數(shù)要滿足一定的范圍的整數(shù)常拓,而且計數(shù)排序需要比較多的輔助空間。其基本思想是辉浦,用待排序的數(shù)作為計數(shù)數(shù)組的下標(biāo)弄抬,統(tǒng)計每個數(shù)字的個數(shù)咧欣。然后依次輸出即可得到有序序列娶桦。

今天學(xué)習(xí)了計數(shù)排序昼激,貌似計數(shù)排序的復(fù)雜度為o(n)障本。很強大。他的基本思路為:

  1. 我們希望能線性的時間復(fù)雜度排序懊亡,如果一個一個比較依啰,顯然是不實際的,書上也在決策樹模型中論證了店枣,比較排序的情況為nlogn的復(fù)雜度速警。

  2. 既然不能一個一個比較,我們想到一個辦法鸯两,就是如果我在排序的時候就知道他的位置闷旧,那不就是掃描一遍,把他放入他應(yīng)該的位置不就可以了嘛钧唐。

3.要知道他的位置忙灼,我們只需要知道有多少不大于他不就可以了嗎?

  1. 以此為出發(fā)點钝侠,我們怎么確定不大于他的個數(shù)呢缀棍?我們先來個約定,如果數(shù)組中的元素都比較集中机错,都在[0, max]范圍內(nèi)爬范。我們開一個max的空間b數(shù)組,把b數(shù)組下標(biāo)對應(yīng)的元素和要排序的A數(shù)組下標(biāo)對應(yīng)起來弱匪。這樣不就可以知道不比他大的有多少個了嗎青瀑?我們只要把比他小的位置元素個數(shù)求和,就是不比他大的萧诫。例如:A={3,5,7};我們開一個大小為8的數(shù)組b斥难,把a[0] = 3 放入b[3]中,使b[3] = 0; 同理 b[5] = 1; b[7] = 2;其他我們都設(shè)置為-1帘饶,哈哈我們只需要遍歷一下b數(shù)組哑诊,如果他有數(shù)據(jù),就來出來及刻,鐵定是當(dāng)前最小的镀裤。如果要知道比a[2]小的數(shù)字有多少個,值只需要求出b[0] – b[6]的有數(shù)據(jù)的和就可以了缴饭。這個0(n)的速度不是蓋得暑劝。

  2. 思路就是這樣咯。但是要注意兩個數(shù)相同的情況A = {1,2,3,3,4}颗搂,這種情況就不可以咯担猛,所以還是有點小技巧的。

6.處理小技巧:我們不把A的元素大小與B的下標(biāo)一一對應(yīng),而是在B數(shù)組對應(yīng)處記錄該元素大小的個數(shù)傅联。這不久解決了嗎先改。哈哈。例如A = {1,2,3,3,4}我們開大小為5的數(shù)組b;記錄數(shù)組A中元素值為0的個數(shù)為b[0] = 0, 記錄數(shù)組A中元素個數(shù)為1的b[1] = 1,同理b[2] = 1, b[3] = 2, b[4] = 1;好了蒸走,這樣我們就知道比A4小的元素個數(shù)是多少了:count = b[0] + b[1] + b[2] + b[3] = 4;他就把A[4]的元素放在第4個位置仇奶。

算法系列:計數(shù)排序

桶排序

基數(shù)排序

總結(jié)

在前面的介紹和分析中我們提到了冒泡排序、選擇排序载碌、插入排序三種簡單的排序及其變種快速排序猜嘱、堆排序衅枫、希爾排序三種比較高效的排序嫁艇。后面我們又分析了基于分治遞歸思想的歸并排序還有計數(shù)排序、桶排序弦撩、基數(shù)排序三種線性排序步咪。我們可以知道排序算法要么簡單有效,要么是利用簡單排序的特點加以改進益楼,要么是以空間換取時間在特定情況下的高效排序猾漫。

image
image

1. 從平均時間來看,快速排序是效率最高的感凤,但快速排序在最壞情況下的時間性能不如堆排序和歸并排序悯周。而后者相比較的結(jié)果是,在n較大時歸并排序使用時間較少陪竿,但使用輔助空間較多禽翼。

2. 上面說的簡單排序包括除希爾排序之外的所有冒泡排序、插入排序族跛、簡單選擇排序闰挡。其中直接插入排序最簡單,但序列基本有序或者n較小時礁哄,直接插入排序是好的方法长酗,因此常將它和其他的排序方法,如快速排序桐绒、歸并排序等結(jié)合在一起使用夺脾。

3. 基數(shù)排序的時間復(fù)雜度也可以寫成O(d*n)。因此它最使用于n值很大而關(guān)鍵字較小的的序列茉继。若關(guān)鍵字也很大劳翰,而序列中大多數(shù)記錄的最高關(guān)鍵字均不同,則亦可先按最高關(guān)鍵字不同馒疹,將序列分成若干小的子序列佳簸,而后進行直接插入排序。

4. 從方法的穩(wěn)定性來比較,基數(shù)排序是穩(wěn)定的內(nèi)排方法生均,所有時間復(fù)雜度為O(n^2)的簡單排序也是穩(wěn)定的听想。但是快速排序、堆排序马胧、希爾排序等時間性能較好的排序方法都是不穩(wěn)定的汉买。穩(wěn)定性需要根據(jù)具體需求選擇。

5. 上面的算法實現(xiàn)大多數(shù)是使用線性存儲結(jié)構(gòu)佩脊,像插入排序這種算法用鏈表實現(xiàn)更好蛙粘,省去了移動元素的時間。具體的存儲結(jié)構(gòu)在具體的實現(xiàn)版本中也是不同的威彰。

參考

排序 Heap Sort
堆排序算法解析
堆排序
面試中的排序算法總結(jié)
計數(shù)排序出牧,傳說時間復(fù)雜度為0(n)的排序

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市歇盼,隨后出現(xiàn)的幾起案子舔痕,更是在濱河造成了極大的恐慌,老刑警劉巖豹缀,帶你破解...
    沈念sama閱讀 217,509評論 6 504
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件伯复,死亡現(xiàn)場離奇詭異,居然都是意外死亡邢笙,警方通過查閱死者的電腦和手機啸如,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,806評論 3 394
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來氮惯,“玉大人叮雳,你說我怎么就攤上這事】鸷В” “怎么了债鸡?”我有些...
    開封第一講書人閱讀 163,875評論 0 354
  • 文/不壞的土叔 我叫張陵,是天一觀的道長铛纬。 經(jīng)常有香客問我厌均,道長,這世上最難降的妖魔是什么告唆? 我笑而不...
    開封第一講書人閱讀 58,441評論 1 293
  • 正文 為了忘掉前任棺弊,我火速辦了婚禮,結(jié)果婚禮上擒悬,老公的妹妹穿的比我還像新娘模她。我一直安慰自己,他們只是感情好懂牧,可當(dāng)我...
    茶點故事閱讀 67,488評論 6 392
  • 文/花漫 我一把揭開白布侈净。 她就那樣靜靜地躺著尊勿,像睡著了一般。 火紅的嫁衣襯著肌膚如雪畜侦。 梳的紋絲不亂的頭發(fā)上元扔,一...
    開封第一講書人閱讀 51,365評論 1 302
  • 那天,我揣著相機與錄音旋膳,去河邊找鬼澎语。 笑死,一個胖子當(dāng)著我的面吹牛验懊,可吹牛的內(nèi)容都是我干的擅羞。 我是一名探鬼主播,決...
    沈念sama閱讀 40,190評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼义图,長吁一口氣:“原來是場噩夢啊……” “哼减俏!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起歌溉,我...
    開封第一講書人閱讀 39,062評論 0 276
  • 序言:老撾萬榮一對情侶失蹤垄懂,失蹤者是張志新(化名)和其女友劉穎骑晶,沒想到半個月后痛垛,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,500評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡桶蛔,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,706評論 3 335
  • 正文 我和宋清朗相戀三年匙头,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片仔雷。...
    茶點故事閱讀 39,834評論 1 347
  • 序言:一個原本活蹦亂跳的男人離奇死亡蹂析,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出碟婆,到底是詐尸還是另有隱情电抚,我是刑警寧澤,帶...
    沈念sama閱讀 35,559評論 5 345
  • 正文 年R本政府宣布竖共,位于F島的核電站蝙叛,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏公给。R本人自食惡果不足惜借帘,卻給世界環(huán)境...
    茶點故事閱讀 41,167評論 3 328
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望淌铐。 院中可真熱鬧肺然,春花似錦、人聲如沸腿准。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,779評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至街望,卻和暖如春倦沧,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背它匕。 一陣腳步聲響...
    開封第一講書人閱讀 32,912評論 1 269
  • 我被黑心中介騙來泰國打工展融, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人豫柬。 一個月前我還...
    沈念sama閱讀 47,958評論 2 370
  • 正文 我出身青樓告希,卻偏偏與公主長得像,于是被迫代替她去往敵國和親烧给。 傳聞我的和親對象是個殘疾皇子燕偶,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 44,779評論 2 354

推薦閱讀更多精彩內(nèi)容

  • Objective-C排序算法 根據(jù)將排序記錄是否全部放置在內(nèi)存中,將排序分為內(nèi)排序和外排序础嫡,之前講的都是內(nèi)排序指么,...
    Fly晴天里Fly閱讀 372評論 0 1
  • 概述 排序有內(nèi)部排序和外部排序,內(nèi)部排序是數(shù)據(jù)記錄在內(nèi)存中進行排序榴鼎,而外部排序是因排序的數(shù)據(jù)很大伯诬,一次不能容納全部...
    蟻前閱讀 5,183評論 0 52
  • 1.插入排序—直接插入排序(Straight Insertion Sort) 基本思想: 將一個記錄插入到已排序好...
    依依玖玥閱讀 1,253評論 0 2
  • 總結(jié)一下常見的排序算法。 排序分內(nèi)排序和外排序巫财。內(nèi)排序:指在排序期間數(shù)據(jù)對象全部存放在內(nèi)存的排序盗似。外排序:指在排序...
    jiangliang閱讀 1,342評論 0 1
  • 概述 排序有內(nèi)部排序和外部排序,內(nèi)部排序是數(shù)據(jù)記錄在內(nèi)存中進行排序平项,而外部排序是因排序的數(shù)據(jù)很大赫舒,一次不能容納全部...
    zwb_jianshu閱讀 1,136評論 0 0