常见排序算法及时间复杂度


  • 选择排序

1、首先在未排序序列中找到最小(大)元素,存放到排序序列的起始位置

2、再从剩余未排序元素中继续寻找最小(大)元素,然后放到已排序序列的末尾

3、将两个排序好的子序列合并成一个最终的排序序列,直到所有元素均排序完毕

4、选择排序是非常稳定的

5、无论什么数据进去时间复杂度都是 O(n2)

6、所以用到它的时候,数据规模越小越好

7、选择排序,实例代码如下.

#include <stdio.h>

#define LEN   10
int data[LEN] = {10, 1, 6, 3, 4, 8, 2, 9, 5, 0};


void print_data(void)
{
    printf("%d %d %d %d %d %d %d %d %d %d\n",
            data[0], data[1], data[2], data[3],
            data[4], data[5], data[6], data[7],
            data[8], data[9]);
}

int main(int argc, char *argv[])
{
    int i, j, temp;

    print_data();

    for(i = 0; i < LEN-1; i++){
        for(j = i+1; j < LEN; j++) {
            if(data[i] > data[j]) {//找最小的数
                temp = data[i];
                data[i] = data[j];
                data[j] = temp;
            }
            print_data();
        }
    }

    return 0;
}

  • 冒泡排序

1、冒泡排序算法属于 非线性时间比较类排序 中的交换排序

2、比较相邻的元素。如果第一个比第二个大,就交换它们两个

3、把长度为n的输入序列分成两个长度为n/2的子序列

4、从开始第一对到结尾的最后一对,这样在最后的元素应该会是最大的数

5、时间复杂度(平均和最坏)都是: O(n*n)

6、时间复杂度(最好): O(n)

7、冒泡排序,实例代码如下.

#include <stdio.h>

#define LEN   10
int data[LEN] = {10, 1, 6, 3, 4, 8, 2, 9, 5, 0};

void print_data(void)
{
    printf("%d %d %d %d %d %d %d %d %d %d\n",
            data[0], data[1], data[2], data[3],
            data[4], data[5], data[6], data[7],
            data[8], data[9]);
}

int main(int argc, char *argv[])
{
    print_data();
    for(int i = 0; i < LEN; i++) {
        for(int j = 0; j < LEN-i-1; j++) {
            if(data[j] > data[j+1]) {
                int temp = data[j];
                data[j] = data[j+1];
                data[j+1] = temp;
                print_data();
            }
        }
    }

    return 0;
}

  • 归并排序

1、归并排序算法采取分而治之 (Divide-and-Conquer)的策略.

$ 时间复杂度为: Θ(nlgn)

2、把长度为n的输入序列分成两个长度为n/2的子序列

3、对这两个子序列分别采用归并排序,这是一个递归的过程

4、将两个排序好的子序列合并成一个最终的排序序列

5、归并排序,实例代码如下.

#include <stdio.h>

#define LEN   10
int data[LEN] = {10, 1, 6, 3, 4, 8, 2, 9, 5, 0};

void print_data(const char *head, int start, int mid, int end)
{
    printf("%s (%d-%d, %d-%d) %d %d %d %d %d %d %d %d %d %d\n",
            head, start, mid, mid+1, end,
            data[0], data[1], data[2], data[3],
            data[4], data[5], data[6], data[7],
            data[8], data[9]);
}

void merge(int start, int mid, int end)
{
    int n1 = mid - start + 1;
    int n2 = end - mid;
    int left[n1], right[n2];
    int i, j, k;

    for (i = 0; i < n1; i++) /* left holds a[start..mid] */
        left[i] = data[start+i];
    for (j = 0; j < n2; j++) /* right holds a[mid+1..end] */
        right[j] = data[mid+1+j];
    i = j = 0;
    k = start;
    while (i < n1 && j < n2)
        if (left[i] < right[j])
            data[k++] = left[i++];
        else
            data[k++] = right[j++];

    while (i < n1) /* left[] is not exhausted */
        data[k++] = left[i++];

    while (j < n2) /* right[] is not exhausted */
        data[k++] = right[j++];
}

void sort(int start, int end)
{
    int mid;

    if (start < end) {
        mid = (start + end) / 2;
        print_data("sort", start, mid, end);

        sort(start, mid);
        sort(mid+1, end);
        merge(start, mid, end);
        print_data("merge", start, mid, end);
    }
}

int main(int argc, char *argv[])
{
    sort(0, LEN-1);
    return 0;
}

  • 插入排序

1、插入排序算法采取增量式(Incremental)的策略解决问题,
每添加一个元素到已排序的子序列中,会逐步将整个数组排序完.

$ 所以插入排序的时间复杂度为: O(n*n)

2、正向插入排序,实例代码如下.

#include <stdio.h>

#define LEN   10
int data[LEN] = {10, 1, 6, 3, 4, 8, 2, 9, 5, 0};

extern void print_data(void);

int main(int argc, char *argv[])
{

    int i, j, temp;

    for(j = 1; j < LEN; j++) {  //LEN-1
        print_data();
        temp = data[j];         //a1
        i = j - 1;              //a2
        while(i >= 0 && data[i] > temp){//m
            data[i+1] = data[i];//a3
            i--;                //a4
        }
        data[i+1] = temp;       //a5
    }
    print_data();

    return 0;
}

void print_data(void)
{
    for(int i = 0; i < LEN; i++)
    printf("%d ", data[i]);
    printf("\n");
}

3、解决同一个问题可以有很多种算法,比较算法的好坏主要标准就是时间复杂度.

1)实例中计算复杂度时忽略print_data和printf输出语句

2)最外层的for循环执行了 LEN-1 次,内层while循环假设执行m次,
  主要取决于原始数据,这里将括号内的比较和赋值的时间忽略了,
  总的执行时间粗略估计: (LEN-1)(a1+a2+a5+m(a3+a4))

3)最好的情况下(即数据已正向排好)执行时间为:
  (a1+a2+a5)(LEN-1) = (a1+a2+a5)LEN - (a1+a2+a5)
  表示成: a*LEN - b,是LEN的线性函数

4)最坏的情况下(数据逆向排好)执行时间为:
  (LEN-1)(a1+a2) + (a3+a4)(LEN*(LEN-1)/2)

5)最坏和随机平均情况下都可以表示成:
  a*LEN*LEN + b*LEN + c  是LEN的二次函数

6)分析算法时间复杂度时,主要关心最坏情况,该算法当LEN愈来愈大时,
  常数时间可以忽略不计,所以该插入排序算法的时间复杂度为:
  O(LEN*LEN) O表示小于等于