排序算法稳定性及时间复杂度分析

不稳定算法记忆口诀 “快些选队”

区分标准

排序前后两个相等的数相对位置不变,则算法稳定;
排序前后两个相等的数相对位置发生了变化,则算法不稳定;

avatar

一、冒泡排序

void bubbleSort(int a[], int n) {
    int i,j,temp;
    for (j = 0; j < n-1; j++){                           
        for (i=0; i<n-1-j; i++){
            if(a[i] > a[i+1]){
                temp = a[i]; 
                a[i] = a[i+1];
                a[i+1] = temp;
            }
        }
    }    
}

二、快速排序

void quikSort(int a[], int low,int high){

    int i = low;
    int j = high;  
    int temp = a[i]; 
    if(low < high){          
        while(i < j) {
            while((a[j] >= temp) && (i < j)){ 
                j--; 
            }
            a[i] = a[j];
            while((a[i] <= temp) && (i < j)){
                i++; 
            }  
            a[j]= a[i];
        }
        a[i] = temp;
        quiksort(a,low,i-1);
        quiksort(a,j+1,high);
    }
}

avatar
avatar
不稳定实例 {2, 1, 3, 3, 1, 1, 1}

时间复杂度分析

快速排序平均每次把问题划分两个子问题,其递归关系式可表示如下
T ( n ) = 2 T ( n 2 ) + O ( n ) T(n) = 2T({n\over 2}) + O(n) T(n)=2T(2n)+O(n)
其中 O ( n ) O(n) O(n)为一次划分的时间复杂度,对比主定理 a = 2 a = 2 a=2 b = 2 b=2 b=2 n l o g b a = n l o g 2 2 = n n^{log^{a}_{b}}=n^{log^{2}_{2}}=n nlogba=nlog22=n,符合情况2,所以 时 间 复 杂 度 O ( n l o g b a l o g n ) = O ( n l o g n ) 时间复杂度O(n^{log^{a}_{b}}logn)=O(nlogn) O(nlogbalogn)=O(nlogn)
在极端的情况下,每次只能划分成一个子问题,一边一个,另外一边 n − 1 n-1 n1个,递推关系式退化成如下
T ( n ) = T ( n − 1 ) + T ( 1 ) + O ( n ) T(n)=T(n-1)+T(1)+O(n) T(n)=T(n1)+T(1)+O(n)
此时,时间复杂度为 O ( n 2 ) O(n^{2}) O(n2)

应用
  1. 利用快排思想寻找数组中第K大(小)的数字;
  2. 求数组中出现次数超过一半的数(时间复杂度O(n));

三、选择排序

void selectionSort(int *a,int n){
    int i, j, temp;
    for(i = 0; i < n-1; i++){
        for(j = i+1; j < n; j++){
            if(a[i] > a[j]){
                temp = a[i];
                a[i] =a [j];
                a[j] = temp;
            }
        }
    }

不稳定实例 {5, 8, 5, 2, 9}

四、插入排序

void insertionSort(int a[], int n) { 
    int i, key, j; 
    for (i = 1; i < n; i++) { 
        key = a[i]; 
        j = i - 1; 
  
        /* Move elements of a[0..i-1], that are 
          greater than key, to one position ahead 
          of their current position */
        while (j >= 0 && a[j] > key) { 
            a[j + 1] = a[j]; 
            j = j - 1; 
        } 
        a[j + 1] = key; 
    } 
} 

五、归并排序

void merge(int arr[], int l, int m, int r) { 
    int i, j, k; 
    int n1 = m - l + 1; 
    int n2 = r - m; 
    int L[n1], R[n2]; 
    
    for (i = 0; i < n1; i++) {
        L[i] = arr[l + i];
    }
         
    for (j = 0; j < n2; j++){
        R[j] = arr[m + 1 + j];
    } 
         
    i = 0, j = 0, k = l; 
    while (i < n1 && j < n2) { 
        if (L[i] <= R[j]) { 
            arr[k] = L[i]; 
            i++; 
        } else { 
            arr[k] = R[j]; 
            j++; 
        } 
        k++; 
    } 
  
    while (i < n1) { 
        arr[k] = L[i]; 
        i++; 
        k++; 
    } 
  
    while (j < n2) { 
        arr[k] = R[j]; 
        j++; 
        k++; 
    } 
} 
  
void mergeSort(int arr[], int l, int r) { 
    if (l < r) { 
        int m = l + (r - l) / 2;
        mergeSort(arr, l, m); 
        mergeSort(arr, m + 1, r); 
        merge(arr, l, m, r); 
    } 
} 
时间复杂度分析

归并排序每次把问题划分为两个规模相等的子问题,子问题排好序后再合并。合并函数merge时间复杂度为 O ( n ) O(n) O(n),所以归并排序递归表达式如下
T ( n ) = 2 T ( n 2 ) + O ( n ) T(n) = 2T({n \over 2 }) + O(n) T(n)=2T(2n)+O(n)
根据主定理可以计算该时间复杂度为 O ( n l o g n ) O(nlogn) O(nlogn)
因为不管元素在什么情况下都要做这些步骤,所以该算法最优时间复杂度和最差时间复杂度及平均时间复杂度都是一样的。

六、堆排序

void heapify(int arr[], int n, int i) { 
    int largest = i; // 根 
    int l = 2*i + 1; // 左
    int r = 2*i + 2; // 右 
  
    // 左大于根 
    if (l < n && arr[l] > arr[largest]) 
        largest = l; 
  
    // 右大于根
    if (r < n && arr[r] > arr[largest]) 
        largest = r; 
  
    if (largest != i) { 
        swap(arr[i], arr[largest]); 
  
        // 递归
        heapify(arr, n, largest); 
    } 
} 
  

void heapSort(int arr[], int n) { 
    // Build heap
    for (int i = n / 2 - 1; i >= 0; i--) 
        heapify(arr, n, i); 
  
    for (int i=n-1; i>0; i--) { 
        // Move current root to end 
        swap(arr[0], arr[i]); 
        heapify(arr, i, 0); 
    } 
} 

时间复杂度:
O ( n ) + ( n − 1 ) ∗ O ( l g n ) = O ( n l g n ) O(n) + (n-1)*O(lgn)=O(nlgn) O(n)+(n1)O(lgn)=O(nlgn)

其中

  1. O ( n ) O(n) O(n): 建堆时间复杂度
  2. ( n − 1 ) O ( l g n ) (n-1)O(lgn) (n1)O(lgn) : 每次将堆顶和最后一个元素交换后,调整堆时间复杂度为 O ( l g n ) O(lgn) O(lgn),交换 n − 1 n-1 n1

因为每次都要把根节点移到最后,所以假定数组值是一样的,很显然原来的顺序不能维持;

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值