十大常用算法排序算法及对比

100 阅读2分钟

前言

  • 常用术语

  • 时间复杂度
    • 执行时间和数据规模的关系
  • 空间复杂度
    • 储存空间与数据规模的关系
  • 稳定性
    • 稳定:一组数据中,a在b前面并且a=b,排序之后a依旧在b的前面
    • 不稳定:一组数据中,a在b前面并且a=b,排序后a可能在b的后面
  • 面试考点
    • 分析某具体的排序算法:时间,空间,稳定性
    • 对比某几种排序算法
    • 代码实现某个排序算法

十大排序算法

  • 冒泡
  • 选择
  • 插入
  • 希尔
  • 归并
  • 快速
  • 计数
  • 基数 补充:
  • Tim排序(插入排序 + 归并排序)

时间复杂度对比

未命名文件 (4).png

常见的时间复杂度说明

  • O(1)

Temp=i; i=j; j=temp;

以上三条单个语句的频度均为1,该程序段的执行时间是一个与数据规模n无关的常数。算法的时间复杂度为常数阶,计作T(n) = O(1)。

注意:如果算法的执行时间不随着数据规模n 的增长而增长,及时算法中有上千条语句,其执行时间也不过是一个较大的常数。此类算法的时间复杂度是O(1)

  • O(n²)

sum=0;                              (1)
for(i = 0;i <= n;i++){              (n+1)
   for(j = 0;i=j <= n;i=j++){       (n^2)
       sum ++                       (n^2)
    } 
}

解:因为Θ(2n²+n+1)=n²(Θ即:去低阶项,去掉常数项,去掉高阶项的常参得到),所以T(n)=O(n²);

 sum=0;  
 for(i=1;i<n;i++){
   sum++               ①
   for(j=0;i<=(2*n);j++){
      sum ++           ②
   }
 }

解:语句①的频度是n-1,语句②的频度是(n-1)*(2n+1)=2n²-n-1。f(n)=2n²-n-1+(n-1)=2n²-2; Θ(2n²-2)=n²,时间复杂度T(n)=O(n²) 一般情况下,对不进行循环语句只要考虑循环体中的执行测试,忽略该语句中步长+1,终止判别、控制转移到等。当有若干个循环语句时,算法的时间复杂度是由嵌套层数最多的循环语句中最内层语句的频度f(n)决定

  • O(n)

a=0;  
b=1;                      ①  
for (i=1;i<=n;i++)        ②  
{    
     s=a+b ;   ③  
     b=a;     ④    
     a=s;     ⑤  
} 

解:①的频度:2;②的频度:n;③的频度:n-1;④的频度:n-1;⑤的频度:n-1;T(n)=2+n+3(n-1)=4n-1 = O(n)

  • O(n³)

sub = 0
for(i=0;i<n;i++){       ①
  for(j=0;j<n;j++){     ②
   for(k=0;k<n;k++){    ③
       sum = sum + 2    ④
   }
  }
}

解:①的频度:n;②的频度:n²;③的频度:n³;④的频度:n³;T(n) = n³+n³+n²+n = 2*n³+n²+n; Θ(2*n³+n²+n) = n³;T(n) = O(n³)

冒泡排序

特性:

  • 比较相邻元素
  • 大的元素后置

复杂度分析:

  • 时间
    • 最好O(n)
      • 已经排序好
    • 最坏O(n²)
      • 完全倒叙
      • n + n*(n-1)+ ... => O(n²)
      • 正常O(n²)
  • 空间
    • O(1)
  • 稳定性
    • 稳定
// 冒号排序
    NSUInteger times = 0;
    NSMutableArray *mutableArray = [[NSMutableArray alloc] initWithArray:@[@9,@8,@7,@6,@4,@3,@2,@1,@0,@5]];
    for (NSInteger i = 0; i<mutableArray.count; i++) {
        for(NSInteger j = 0; j < mutableArray.count - i -1; j++) { // -1是因为下面判断多取一位,防止数组越界
            times ++;
            if([mutableArray[j] integerValue] > [mutableArray[j + 1] integerValue]){
                [mutableArray exchangeObjectAtIndex:j withObjectAtIndex:j+1];
            }
            NSLog(@"times:%ld; %@",times,[**self** returnStringWithArray:mutableArray]);
        }
    }
    
- (NSString *)returnStringWithArray:(NSArray *)array{
    NSString *string = @"返回数组:";
    if(array.count <= 0) return string;
    for (NSNumber *number in array) {
        string = [NSString stringWithFormat:@"%@%@,",string,number];
    }
    return string;
}

输出结果:times:45; 返回数组:0,1,2,3,4,5,6,7,8,9

优化

  1. 设置标记来记录是否发生交换,如果没有就提前结束
// 1.有序数据排序优化
    NSUInteger times = 0;
    NSMutableArray *mutableArray = [[NSMutableArray alloc] initWithArray:@[@9,@8,@7,@6,@4,@3,@2,@1,@0,@5]];
    BOOL isbreak = true;
    for (NSInteger i = 0; i<mutableArray.count && isbreak; i++) {
        isbreak = false;
        for (NSInteger j = 0; j < (mutableArray.count - i -1) ; j++) {//-1是因为下面判断多取一位,防止数组越界
            times ++;
            if([mutableArray[j] integerValue] < [mutableArray[j + 1] integerValue]){
                isbreak = true;
                [mutableArray exchangeObjectAtIndex:j withObjectAtIndex:j+1];
            }
            NSLog(@"times:%ld; %@",times,[**self** returnStringWithArray:mutableArray]);
        }
    }
- (NSString *)returnStringWithArray:(NSArray *)array{
    NSString *string = @"返回数组:";
    if(array.count <= 0) return string;
    for (NSNumber *number in array) {
        string = [NSString stringWithFormat:@"%@%@,",string,number];
    }
    return string;
}

输出结果:times:39; 返回数组:9,8,7,6,5,4,3,2,1,0,

选择排序

特性

  • 选择最小的元素
  • 将元素放在已排序的末尾

复杂度分析

  • 时间
    • 最好O(n)
      • 已经排序好
    • 最坏O(n²)
      • 完全倒叙
      • n + n*(n-1)+ ... => O(n²)
    • 正常O(n²)
      • n + n*(n-1)+ ... => O(n²)
  • 空间
    • O(1)
  • 稳定性
    • 不稳定
    • 例如:[3,3,1] => [1,3,3];两个3前后顺全发生了变化
{
    // 选择排序
    NSUInteger mine_index = 0;
    for (NSUInteger i = 0; i<mutableArray.count; i++) {
        mine_index = i;
        for (NSUInteger j = i + 1; j < mutableArray.count; j++) {
            times++;
            if([mutableArray[mine_index] integerValue] > [mutableArray[j] integerValue]){
                mine_index = j;
                [mutableArray exchangeObjectAtIndex:mine_index withObjectAtIndex:i];
            }
            NSLog(@"times:%ld; %@",times,[**self** returnStringWithArray:mutableArray]);
        }
    }    
    NSLog(@"times:%ld; %@",times,[self returnStringWithArray:mutableArray]);
}
- (NSString *)returnStringWithArray:(NSArray *)array{
    NSString *string = @"返回数组:";
    if(array.count <= 0) return string;
    for (NSNumber *number in array) {
        string = [NSString stringWithFormat:@"%@%@,",string,number];
    }
   return string;
}

输出结果:times:45; 返回数组:0,1,2,3,5,4,6,7,8,9,

插入排序

特性

  • 找到数的位置
  • 插入数

复杂度分析

  • 时间
    • 最好O(n)
      • 已经排序好
    • 最坏O(n²)
      • 完全倒叙
      • n + n*(n-1)+ ... => O(n²)
    • 正常O(n²)
      • n + n*(n-1)+ ... => O(n²)
  • 空间
    • O(1)
  • 稳定性
    • 不稳定
    • 例如:[3,3,1] => [1,3,3];两个3前后顺全发生了变化
  • 优化
    • 二分法
    // 插入排序
    if(mutableArray.count < 1) return;
    for (NSUInteger i = 0; i < mutableArray.count; i++) {
        NSInteger value = [mutableArray[i] intValue];
        NSInteger j = i - 1;
        for (; j>=0; --j) {
            if([mutableArray[j] intValue] > value){
                times ++;
                [mutableArray exchangeObjectAtIndex:j withObjectAtIndex:j+1];
            }else{
                break;
            }
            NSLog(@"times:%ld; %@",times,[self returnStringWithArray:mutableArray]);
        }
        mutableArray[j+1] = @(value);
    }
    
- (NSString *)returnStringWithArray:(NSArray *)array{
    NSString *string = @"返回数组:";
    if(array.count <= 0) return string;
    for (NSNumber *number in array) {
        string = [NSString stringWithFormat:@"%@%@,",string,number];
    }
    return string;
}

输出结果:times:40; 返回数组:0,1,2,3,4,5,6,7,8,9,

未完待续