Objective-C排序演算法實現,objective-c排序演算法
演算法作為程式猿的一種基本技能,對我來說卻一直是一個硬傷。畢竟平時的工作接觸演算法的地方很少,大部分時間都是在搭建UI,寫商務邏輯,寫網路請求。然而在面試當中,演算法往往又是考察的重點。
也許你會說這些平時工作用得到嗎?確實,用到的很少,但是你不會,人家就是不要你,你能怎樣?!
關於這個事情,唐boy有篇文章,我覺得說的挺好,看完後也許你會所感觸。http://blog.devtang.com/blog/2015/06/16/talk-about-tech-interview/
好了,廢話不多說。關於排序演算法,網上有很多資料,我從中整理了一下,並且寫了詳細的注釋,為的是以後預習的時候能一目瞭然的看到每個演算法的思想。如有不足,歡迎指正!
冒泡排序
/** * 冒泡排序 * 對數組中每個位置的資料,從後往前推,依次比較相鄰的兩個數,如果後面的數較小,則交換兩者位置 * 如果一次遍曆沒有發生任何資料交換,則排序直接完成 * @param array 數組 */+(void)bubbleSort:(NSMutableArray *)list{ if (list.count <= 1) { return; } int i, y; BOOL bFinish = YES; //是否發生資料交換 for (i = 1; i<= [list count] && bFinish; i++) { bFinish = NO; //每次遍曆時,重設標誌 //從最後一位開始,依次跟前一位相比,如果較小,則交換位置 //當一次遍曆沒有任何資料交換時,則說明已經排序完成(bFinish=YES),則不再進行迴圈 for (y = (int)[list count]-1; y>=i; y--) { if ([[list objectAtIndex:y] intValue] < [[list objectAtIndex:y-1] intValue]) { //交換位置// NSLog(@"%d<->%d",[[array objectAtIndex:y-1] intValue],[[array objectAtIndex:y] intValue]); [list exchangeObjectAtIndex:y-1 withObjectAtIndex:y]; bFinish = YES; //發生資料交換,則繼續進行下一次遍曆,直到未發生資料交換或者迴圈完成為止// NSLog(@"%@",[array componentsJoinedByString:@" "]); } } }}
/** * 冒泡排序(倒敘) * * @param list 待排序的數組 */+(void)bubbleSortDesc:(NSMutableArray *)list{ if (list.count <= 1) { return; } int i, y; BOOL bFinish = YES; //是否發生資料交換 for (i = 1; i<= [list count] && bFinish; i++) { bFinish = NO; //每次遍曆時,重設標誌 //從最後一位開始,依次跟前一位相比,如果較小,則交換位置 //當一次遍曆沒有任何資料交換時,則說明已經排序完成(bFinish=YES),則不再進行迴圈 for (y = (int)[list count]-1; y>=i; y--) { if ([[list objectAtIndex:y] intValue] > [[list objectAtIndex:y-1] intValue]) { //交換位置 // NSLog(@"%d<->%d",[[array objectAtIndex:y-1] intValue],[[array objectAtIndex:y] intValue]); [list exchangeObjectAtIndex:y-1 withObjectAtIndex:y]; bFinish = YES; //發生資料交換,則繼續進行下一次遍曆,直到未發生資料交換或者迴圈完成為止 // NSLog(@"%@",[array componentsJoinedByString:@" "]); } } }}
快速排序
/** * 快速排序 * * @param list array */+(void)quickSort:(NSMutableArray *)list{ if (list.count <= 1) { return; } [self quickSort:list startIndex:0 endIndex:list.count-1];}/** * 快速排序 * 任意選取一個資料(通常選用數組的第一個數)作為關鍵資料,然後將所有比它小的數都放到它前面,所有比它大的數都放到它後面 * 再分別對兩邊的資料進行快速排序 * @param list 數組 * @param start 低位索引 * @param end 高位索引 */+(void)quickSort:(NSMutableArray *)list startIndex:(NSInteger)start endIndex:(NSInteger)end{ if (start >= end) { //低位大於高位,排序結束 return; } NSInteger low = start; NSInteger high = end; NSInteger key = [[list objectAtIndex:start] integerValue]; //取第一個數作為關鍵資料 while (low < high) { //從後往前推,直到找到第一個比關鍵資料小的值 while ([[list objectAtIndex:high] integerValue] >= key && low < high) { high--; } //將這個值與關鍵資料對調(關鍵資料處於low位置),對調完關鍵資料處於high位置 [list exchangeObjectAtIndex:low withObjectAtIndex:high]; //從前往後推,直到找到第一個比關鍵資料大的值 while ([[list objectAtIndex:low] integerValue] <= key && low < high) { low++; } //將這個值與關鍵資料(關鍵資料已經處於high位置)對調,對調完關鍵資料處於low位置 [list exchangeObjectAtIndex:high withObjectAtIndex:low]; } //對關鍵資料前面的資料進行快速排序 [self quickSort:list startIndex:start endIndex:low-1]; //對關鍵資料後面的資料進行快速排序 [self quickSort:list startIndex:low+1 endIndex:end];}
直接插入排序
/** * 直接插入排序 * 把待排序的紀錄按其關鍵碼值的大小逐個插入到一個已經排好序的有序序列中,直到所有的紀錄插入完為止 * * @param list 待排序的數組 */+(void)insertionSort:(NSMutableArray *)list{ if (list.count <= 1) { return; } //從第1位開始,依次將數組分成2部分:前一部分可以視為已經排好序的,後一部分是未排序的 //對後一部分的資料依次遍曆,插入到前一部分中的合適位置 for (int i=1; i<list.count; i++) { int j=i; //待排序的數(是未排序部分的第1個數,它的上一位元就是已經排序的部分的最後一位元) NSInteger temp = [[list objectAtIndex:i] integerValue]; //從已排序部分的最後一位開始依次往前推,如果比待排序的數大,則將其位置往後移一位 while (j>0 && [[list objectAtIndex:(j-1)]integerValue] > temp) { [list replaceObjectAtIndex:j withObject:[list objectAtIndex:(j-1)]]; j--; } //迴圈結束後的j即為待排序的數需要插入的位置 [list replaceObjectAtIndex:j withObject:@(temp)]; }}
二分排序(插入排序)
/** * 二分排序(插入排序) * * @param list 待排序的數組 */+(void)binaryInsertionSort:(NSMutableArray *)list{ if (list.count <= 1) { return; } //從第1位開始,依次將數組分成2部分:前一部分可以視為已經排好序的,後一部分是未排序的 //對後一部分的資料依次遍曆,插入到前一部分中的合適位置 for (int i=1; i<list.count; i++) { int left = 0; int right = i-1; int middle; //待排序的數(是未排序部分的第1個數,它的上一位元就是已經排序的部分的最後一位元) NSInteger temp = [[list objectAtIndex:i] integerValue]; while (left <= right) { //每次從已經排序的部分中取中間位置的數進行比較 middle = (left+right)/2; //如果跟待排序數相等,則直接插入到此位置即可 if ([[list objectAtIndex:middle]integerValue] == temp){ left = middle; break; } //如果比待排序數大,則從中間位置左邊範圍內再次取中間數尋找(下一迴圈) else if ([[list objectAtIndex:middle]integerValue] > temp) { right = middle-1; } //如果比待排序數小,則從中間位置右邊範圍再次取中間數尋找(下一迴圈) else{ left = middle+1; } } //迴圈結束,找到待插入位置left //依次將left右邊(比left位置資料都大)的資料向右移動一位 for (int j=i; j>left; j--) { [list replaceObjectAtIndex:j withObject:[list objectAtIndex:j-1]]; } //在left位置插入待排序數 [list replaceObjectAtIndex:left withObject:@(temp)]; }}
希爾排序
/** * 希爾排序(插入排序) * 先取一個正整數d1<n,把所有序號相隔d1的數組元素放一組,組內進行直接插入排序;然後取d2<d1,重複上述分組和排序操作;直至di=1,即所有記錄放進一個組中排序為止 * * @param list 待排序的數組 */+(void)shellInsertionSort:(NSMutableArray *)list{ if (list.count <= 1) { return; } NSInteger delt = list.count / 2; //增量:取數組長度一半,以後每次減半,直到增量為1 NSInteger i; while (delt >= 1) { //對位置距離=增量的組分別進行直接插入排序 for (i=delt; i<list.count; i++) { NSInteger k=i; //待排序的數 NSInteger temp = [[list objectAtIndex:i] integerValue]; //從已排序部分的最後一位開始依次往前推(間隔=增量),如果比待排序的數大,則將其位置往後移“增量”位 while (k>=delt && [[list objectAtIndex:k-delt]integerValue] > temp) { [list replaceObjectAtIndex:k withObject:[list objectAtIndex:k-delt]]; k -= delt; //按增量遞推 } //迴圈結束後的j即為待排序的數需要插入的位置 [list replaceObjectAtIndex:k withObject:@(temp)]; } delt = delt/2; //增量:每次減半 }}
堆排序
/** * 堆排序 * 利用堆的特性(堆頂肯定是最大或者最小值),將堆頂資料與堆尾資料交換(相當於刪除堆頂資料並儲存到堆尾),再重新構造堆,直到堆中只剩下1個資料 * * @param list 待排序的數組 */+(void)heapSort:(NSMutableArray *)list{ if (list.count <= 1) { return; } NSInteger i; //初始化堆,取父節點和左右子節點中最大(或者最小)值作為堆頂 //因為堆節點i的葉子節點是2*i+1,因此初始化是從length/2-1處開始整理堆即可 for(i=list.count/2-1;i>=0;i--){ [self heapAdjust:list withIndex:i andLength:list.count]; }// NSLog(@"構造堆:%@", [list componentsJoinedByString:@" "]); //整理完堆後,第0位元肯定是最大(或者最小)值,將它跟第n-1位交換,相當於從堆中刪除第0位,再對剩下的資料重新整理堆 //依次執行上述交換操作,直到只剩下1個數 for(i=list.count-1;i>0;i--){ [list exchangeObjectAtIndex:0 withObjectAtIndex:i]; [self heapAdjust:list withIndex:0 andLength:i];// NSLog(@"構造堆:%@", [list componentsJoinedByString:@" "]); }}/** * 構造堆(最大堆) * * * @param list 待整理的數組 * @param index 待整理的位置 * @param length 待整理的數組的長度(這裡因為待排序的資料和已排序的資料共用了一個數組) */+(void)heapAdjust:(NSMutableArray *)list withIndex:(NSInteger)index andLength:(NSInteger)length{ //左子節點,右子節點為lchild+1 NSInteger lchild = index*2+1; while (lchild < length) { //判斷是否有右子節點,如果有則判斷右子節點是否比左節點大,如果比左節點大則用右子節點於父節點進行比較 if (lchild+1 < length && [[list objectAtIndex:lchild+1]integerValue] > [[list objectAtIndex:lchild] integerValue]) { lchild++; //++表示將左節點位置移到右節點位置,為的是下面跟父節點進行比較 } //如果父節點比子節點小,則說明本身就是一個堆,無需再整理 if ([[list objectAtIndex:lchild]integerValue] < [[list objectAtIndex:index]integerValue]) { break; } //交換父節點和子節點的值(將父節點下沉) [list exchangeObjectAtIndex:index withObjectAtIndex:lchild]; //父節點位置下沉到子節點的位置,再繼續整理下面的堆 index = lchild; lchild = index*2+1; }}
選擇排序
/** * 選擇排序 * 每一次從待排序的資料元素中選出最小(或最大)的一個元素,存放在序列的起始位置,直到全部待排序的資料元素排完 * * @param list 待排序的數組 */+(void)selectSort:(NSMutableArray *)list{ if (list.count <= 1) { return; } NSInteger i,j; //從第0位開始排序,直到最後一位(最後一位無需再排序) for (i=0; i<list.count-1; i++) { j=i;//記錄當前位置,作為對比的關鍵元素 //依次遍曆後面的元素 for (NSInteger k=i+1; k<list.count; k++) { //如果元素比關鍵元素小,則將關鍵元素位置指向此位置,再繼續遍曆 if ([[list objectAtIndex:k]integerValue] < [[list objectAtIndex:j]integerValue]) { j=k; } } //遍曆結束後,得到最小元素位置j,如果不是初始位置,則交換2個位置的值 if (i!=j) { [list exchangeObjectAtIndex:i withObjectAtIndex:j]; } }}
歸併排序
/** * 歸併排序 * 將數組分割成2部分,使左右2部分都有序,再合并成一個 * 為了使左右2部分都有序,可通過遞迴的方式,再對其進行分割,直至分割後只剩1個元素,可以視其為已經有序的,再將左右2部分進行合并 * 合并時,分別從2部分中取第0個數進行比較,將較小(或者較大)的數儲存到臨時數組裡,再用下一個數和上次比較中較大者進行比較,直到某一邊沒有資料,再將另外一邊中剩餘的數複製到臨時數組裡 * 最後產生的臨時數組就是已經排好序的結果 * * @param list 待排序的數組 */+(void)mergeSort:(NSMutableArray *)list{ if (list.count <= 1) { return; } [self mergeSort:list startIndex:0 endIndex:list.count-1];}+(void)mergeSort:(NSMutableArray *)list startIndex:(NSInteger)startIndex endIndex:(NSInteger)endIndex{ if (startIndex < endIndex) { //取中間位置,將左右兩邊分別遞迴進行歸併,直至左右兩邊只剩1個元素 NSInteger middle = (startIndex + endIndex) / 2; [self mergeSort:list startIndex:startIndex endIndex:middle]; [self mergeSort:list startIndex:middle+1 endIndex:endIndex]; //對左右2邊的資料進行合并 NSInteger i=startIndex; //左邊資料的起始位置 NSInteger j= middle+1; //右邊資料的起始位置 NSMutableArray *temp = [NSMutableArray array]; //臨時數組 while (i<=middle && j <= endIndex) { //如果左邊資料較小,則將其放到臨時數組裡,並將左邊位置向後移一位 if ([[list objectAtIndex:i]integerValue] < [[list objectAtIndex:j]integerValue]) { [temp addObject:[list objectAtIndex:i]]; i++; } //否則將右邊資料放到臨時數組裡,並將右邊位置向後移一位 else{ [temp addObject:[list objectAtIndex:j]]; j++; } } //如果左邊還有資料,則將剩餘的部分全都複製到臨時數組裡 while (i<=middle) { [temp addObject:[list objectAtIndex:i]]; i++; } //如果右邊還有資料(左右兩邊只可能存在一邊有剩餘資料的情況),則將剩餘的部分全都複製到臨時數組裡 while (j<=endIndex) { [temp addObject:[list objectAtIndex:j]]; j++; } //再將臨時數組裡的資料(已經排好序了)儲存到未經處理資料中,以達到對未經處理資料的排序 for (i=0; i<temp.count; i++) { //注意:需要從startIndex位置開始,因為這裡是遞迴調用的 [list replaceObjectAtIndex:startIndex withObject:[temp objectAtIndex:i]]; startIndex++; } }}