iOS 開發中常用的排序演算法
我們有許多的排序演算法可以選擇,冒泡、選擇、快速、插入、希爾、歸併、基數等,我今天來簡單介紹一下不同演算法的優缺點。
先普及一個概念:
演算法穩定性:相同元素的前後順序在任何情況都不會發生改變,這種排序成為穩定排序演算法 。反之成為不穩定排序演算法 。
氣泡排序
原理:重複地走訪過要排序的元素列,依次比較兩個相鄰的元素,如果他們的順序(如從大到小、首字母從A到Z)錯誤就把他們交換過來。走訪元素的工作是重複地進行直到沒有相鄰元素需要交換,也就是說該元素已經排序完成。
for (int i = 0; i<result.count-1; i++) { for (int j = 0; j<result.count-1-i; j++) { NSInteger left = [result[j] integerValue]; NSInteger right = [result[j+1] integerValue]; if (left<right) { [result exchangeObjectAtIndex:j withObjectAtIndex:j+1]; } } } NSLog(@"%@",result);
時間複雜度:O(n^2)
演算法穩定性:相同元素的前後順序不會發生改變,所以氣泡排序是一種穩定排序演算法。
選擇排序
原理:它的工作原理是每一次從待排序的資料元素中選出最小(或最大)的一個元素,存放在序列的起始位置,然後,再從剩餘未排序元素中繼續尋找最小(大)元素,然後放到已排序序列的末尾。以此類推,直到全部待排序的資料元素排完。
C語言實現選擇排序
void select_sort(int*a,int n) { register int i,j,min,t; for(i=0;i<n-1;i++) { min=i;//查詢最小值 for(j=i+1;j<n;j++) if(a[min]>a[j]) min=j;//交換 if(min!=i) { t=a[min]; a[min]=a[i]; a[i]=t; } } }
時間複雜度:選擇排序的交換操作介於 0 和 (n - 1) 次之間。選擇排序的比較操作為 n (n - 1) / 2 次之間。選擇排序的賦值操作介於 0 和 3 (n - 1) 次之間
但是它是一種不穩定演算法,相同元素的前後順序有可能發生改變。
快速排序
快速排序(Quicksort)是對氣泡排序的一種改進。
通過一趟排序將要排序的資料分割成獨立的兩部分,其中一部分的所有資料都比另外一部分的所有資料都要小,然後再按此方法對這兩部分資料分別進行快速排序,整個排序過程可以遞迴進行,以此達到整個資料變成有序序列。
一趟快速排序的演算法是:
- 設定兩個變數i、j,排序開始的時候:i=0,j=N-1;
- 以第一個陣列元素作為關鍵資料,賦值給key ,即key =A[0];
- 從j開始向前搜尋,即由後開始向前搜尋(j--),找到第一個小於key 的值A[j],將A[j]和A[i]互換;
- 從i開始向後搜尋,即由前開始向後搜尋(i++),找到第一個大於key 的A[i],將A[i]和A[j]互換;
- 重複第3、4步,直到i=j; (3,4步中,沒找到符合條件的值,即3中A[j]不小於key ,4中A[i]不大於key 的時候改變j、i的值,使得j=j-1,i=i+1,直至找到為止。找到符合條件的值,進行交換的時候i, j指標位置不變。另外,i==j這一過程一定正好是i+或j-完成的時候,此時令迴圈結束)。
此時,一個數組分成兩個小陣列,一組所有的值都大於key ,另一組都小於key 。
然後進行遞迴操作,直到陣列不能再分解為止(只有一個數據),才能得到正確結果。
程式碼實現:(遞迴的思想)
- (void)quickAscendingOrderSort:(NSMutableArray *)arr leftIndex:(NSInteger)left rightIndex:(NSInteger)right { if (left < right) { NSInteger temp = [self getMiddleIndex:arr leftIndex:left rightIndex:right]; [self quickAscendingOrderSort:arr leftIndex:left rightIndex:temp - 1]; [self quickAscendingOrderSort:arr leftIndex:temp + 1 rightIndex:right]; } } - (NSInteger)getMiddleIndex:(NSMutableArray *)arr leftIndex:(NSInteger)left rightIndex:(NSInteger)right { NSInteger tempValue = [arr[left] integerValue]; while (left < right) { while (left < right && tempValue <= [arr[right] integerValue]) { right --; } if (left < right) { arr[left] = arr[right]; } while (left < right && [arr[left] integerValue] <= tempValue) { left ++; } if (left < right) { arr[right] = arr[left]; } } arr[left] = [NSNumber numberWithInteger:tempValue]; return left; }
快速排序也是一種不穩定排序演算法。
插入排序
實現思路:
- 從第一個元素開始,認為該元素已經是排好序的。
- 取下一個元素,在已經排好序的元素序列中從後向前掃描。
- 如果已經排好序的序列中元素大於新元素,則將該元素往右移動一個位置。
- 重複步驟3,直到已排好序的元素小於或等於新元素。
- 在當前位置插入新元素。
- 重複步驟2。
#pragma mark - 插入升序排序 - (void)insertionAscendingOrderSort:(NSMutableArray *)ascendingArr { for (NSInteger i = 1; i < ascendingArr.count; i ++) { NSInteger temp = [ascendingArr[i] integerValue]; for (NSInteger j = i - 1; j >= 0 && temp < [ascendingArr[j] integerValue]; j --) { ascendingArr[j + 1] = ascendingArr[j]; ascendingArr[j] = [NSNumber numberWithInteger:temp]; } } NSLog(@"插入升序排序結果:%@",ascendingArr); }
平均時間複雜度:O(n^2)
上面說了很多,都是我們用不到的,演算法排序,可以拿過來了解一下演算法實現。
下面提供幾種常用的iOS排序方法:
NSComparator排序
升序:(降序只需要改變判斷規則)
[arr sortUsingComparator:^NSComparisonResult(id_Nonnull obj1, id_Nonnull obj2) { NSInteger value1 = [obj1 integerValue]; NSInteger value2 = [obj2 integerValue]; if (value1 < value2) { return NSOrderedAscending; }else{ return NSOrderedDescending; } }]; NSLog(@"%@",arr);
好處:我們可以自己去寫邏輯,控制輸出我們想要的升序規則。
一般用在排序規則比較少見的情況。
NSDescriptor排序
sort descriptor可以很方便的對陣列進行多個key的排序。比如要對陣列的物件先做ID排序,然後在對content進行排序的話,可以寫成:
NSSortDescriptor *firstDescriptor = [[NSSortDescriptor alloc] initWithKey:@"ID" ascending:YES]; NSSortDescriptor *secondDescriptor = [[NSSortDescriptor alloc] initWithKey:@"content" ascending:YES]; NSArray *sortArray = [NSArray arrayWithObjects:firstDescriptor,secondDescriptor,nil]; NSArray*sortedArray = [unSortedArray sortedArrayUsingDescriptors:sortArray];