排序的分类:
交换类排序:冒泡排序、快速排序
插入类排序:直接插入排序、希尔排序
选择类排序:简单选择排序、堆排序
交换类排序
冒泡排序
时间复杂度:最好(有序情况下)O(n),平均(逆序情况下)O(n^2)
空间复杂度:O(1)
算法思想:(从小到大,从前往后)
第一个记录和第二个记录比较,如果第一个大,则二者交换,否则不交换;然后第二个记录和第三个记录比较,如果第二大,则二者交换,否则不交换……
代码:(这里是创建了NSMutableArray的分类,下同)
#import "NSMutableArray+Sort.h"
@implementation NSMutableArray (Sort)
/** 冒泡排序
* isAsc YES 表示升序(下同)
*/
- (void)sortedArrayWithBubleASC:(BOOL)isAsc{
NSInteger len = self.count;
for (NSInteger i=len-1; i>0; --i) {
BOOL flag = NO;
for (NSInteger j = 0; j < i; ++j) {
if ((isAsc && self[j] > self[j+1]) || (!isAsc && self[j] < self[j+1])) {
[self swapIndex1:j index2:j+1];
flag = YES;
}
}
if (!flag) {
break;
}
}
}
/** 交换2个值 */
- (void)swapIndex1:(NSInteger)index1 index2:(NSInteger)index2{
id temp = self[index1];
self[index1] = self[index2];
self[index2] = temp;
}
@end
快速排序:
时间复杂度:最好(越接近无序,算法越高效)O(n),最坏(越接近有序,算法越低效)O(n^2),平均O(nlogn)
空间复杂度:O(logn)
代码:
/** 快速排序 */
- (void)sortedArrayWithQuickLow:(NSInteger)low high:(NSInteger)high ASC:(BOOL)isAsc{
NSInteger i = low, j = high;
if (i < j) {
id temp = self[i];
while (i < j){
while (i < j && ((self[j] > temp && isAsc) || (self[j] < temp && !isAsc))) --j;
if(i<j) {self[i] = self[j]; ++i;}
while (i < j && ((self[i] < temp && isAsc) || (self[i] > temp && !isAsc))) ++i;
if(i<j) {self[j] = self[i]; --j;}
}
self[i] = temp;
[self sortedArrayWithQuickLow:0 high:i-1 ASC:isAsc];
[self sortedArrayWithQuickLow:i+1 high:high ASC:isAsc];
}
}
选择类排序
堆排序
时间复杂度:O(nlogn)(最好,最坏都是这个)
空间复杂度:O(1)
可以把堆看为一颗完全二叉树,若父亲结点大于孩子结点,则这样的堆叫做大顶堆;若父亲结点小于孩子结点的值,则这样的堆叫做小顶堆。
/** 堆排序 */
- (void)sortedWithHeap:(BOOL)isAsc{
for (NSInteger i = self.count/2 - 1; i>=0; --i) {
[self siftWithLow:i high:self.count asc:isAsc];
}
for (NSInteger i = self.count - 1; i>=1; --i) {
id temp = self[0];
self[0] = self[i];
self[i] = temp;
[self siftWithLow:0 high:i asc:isAsc];
}
}
- (void)siftWithLow:(NSInteger)low high:(NSInteger)high asc:(BOOL)isAsc{
NSInteger left = 2 * low + 1;
NSInteger right = left + 1;
NSInteger lastIndex = low;
//左子节点大的情况
if (left < high && ((self[left] > self[lastIndex] && isAsc) || (self[left] < self[lastIndex] && !isAsc))) lastIndex = left;
//右子节点大的情况
if (right < high && ((self[right] > self[lastIndex] && isAsc) || (self[right] < self[lastIndex] && !isAsc))) lastIndex = right;
//节点改变
if (lastIndex != low) {
//较大的节点值将交换到其所在节点的父节点
id temp = self[low];
self[low] = self[lastIndex];
self[lastIndex] = temp;
//递归遍历
[self siftWithLow:lastIndex high:high asc:isAsc];
}
}
简单选择排序
时间复杂度:O(n^2 )
空间复杂度:O(1)
思想:从头至尾顺序扫描序列,找出最小的一个记录,和第一个记录交换,接着从剩下的记录中继续这种选择和交换,最终使序列有序。
/** 简单选择排序 */
- (void)sortedWithSelect:(BOOL)isAsc{
for (int i = 0; i < self.count; ++i) {
int k = i;
//这个循环是从无序序列中挑出一个最小的元素
for (int j = i+1; j<self.count; ++j) {
if ((self[k] > self[j] && isAsc) || (self[k] < self[j] && !isAsc)) {
k = j;
}
}
[self swapIndex1:i index2:k];
}
}
插入类排序:
直接插入排序
时间复杂度: O(n^2)
空间复杂度:O(1)
稳定性:稳定
代码:
/** 直接插入排序 */
- (void)sortedWithInsert:(BOOL)isAsc{
for (int i = 1; i<self.count; ++i) {
id temp = self[i];
int j = i - 1;
for (; j >= 0 && ((temp < self[j] && isAsc) || ((temp > self[j] && !isAsc))); --j) {
self[j+1] = self[j];
}
self[j+1] = temp;
}
}
希尔排序(缩小增量排序)
时间复杂度:平均O(nlogn)
空间复杂度:O(1)
稳定性:不稳定
/** 希尔排序 */
- (void)sortedWithShellStep:(int)step asc:(BOOL)isAsc{
for (int i = 0; i < self.count; ++i) {
id temp = self[i];
int j = i - step;
for (; j >= 0 && ((temp < self[j] && isAsc) || ((temp > self[j] && !isAsc))); j -= step) {
self[j+step] = self[j];
}
self[j+step] = temp;
}
}
总结:
时间复杂度:“快些以nlogn的速度归队”(快:快速排序,些:希尔排序,归:归并排序,队:堆排序)
空间复杂度:快速排序为O(logn),其他的都是O(1)
稳定性:“心情不稳定,快些选一堆好友聊天”(快:快速排序,些:希尔排序,选:直接选择排序,堆:堆排序)
完整的DEMO见:GitHub地址