排序算法汇总

对排序算法的介绍和实现,后续再增加更多...

基本原理简述

冒泡排序

复杂度: O(n^2)

从未排序集合A中按顺序一一比较得到最大(小)的元素放入已排序集合B中,从而保证B集合一直有序;

插入排序

复杂度: O(n^2)

将未排序集合A中的每个元素依次插入已排序的集合B的恰当位置,从而保证B集合一直有序

归并排序

复杂度: O(nlgn)

归并:就是依次取出两个有序集合B1,B2的第一个元素进行比较,比较大小后依次放入集合A中,从而保证集合A有序;

归并排序:就是将未排序集合,一等分为二,二等分为四,一直往下拆分,直到拆分出的集合只有一个元素,一个元素的集合肯定是有续的,然后自下向上做归并操作,从而得到最终有序的集合;

堆排序

复杂度: O(nlgn)

首先将集合A逻辑化为一个完全二叉树,然后将这个完全二叉树调整为最大(小)堆,调整后树的根节点即为最大(小)元素,将根节点对应的元素放到集合B中,从而保证B集合一直有序。基本思想跟冒泡排序有点类似,但是查找最大(小)元素的方式效率比冒泡排序高。

堆化(heapify): 假设以i为根的二叉树,左右子树都已经是堆,则通过A[i]可能小于其左右孩子节点,通过A[i]值“逐级下沉”, 从而使得以i为根的二叉树也是堆。

建堆(build heap): 首先,完全二叉树的所有叶子节点都是一个独立的子树,并且满足堆的性质。则依次从最后一个非叶子节点开始,以其为根执行堆化(heapify)过程,则最后可将整个完全二叉树构建成一个最大(小)堆。

快速排序

最坏复杂度:O(n^2) 平均复杂度:O(nlgn)

快速排序和归并排序类似也采用了分治思想,和归并排序直接等分不同的是,快速排序是选择数组的最后一个元素做为主元,然后将数组分为三部分小于等主元的部分A,主元部分B,大于主元部分C,这一过程叫做划分(Partition),然后再对A,C递归执行这一划分过程,知道A部分或B部分的大小等于1,递归开始回退。

随机化快速排序

快速排序有个问题就是,当划分(Partition)不平衡的时候快速排序的性能就接近插入排序。比如,当待排序的集合是已经排序的或者是反序排序的就会导致每次划分都是不平衡的。

所以通过随机选择主元可以避免这一问题,这就是随机化快速排序。

实现(C语言)

//
// Created by spiro on 19-4-8.
//

#include <stdlib.h>
#include "sort.h"

/* 冒泡排序 */
void bubble_sort(int * arr, int len) {
    int i, j;
    int temp;
    for (i = 0; i < (len - 1); ++i) {
        for (j = len - 1; j > i; --j) {
            if (arr[j] < arr[j - 1]) {
                temp = arr[j - 1];
                arr[j - 1] = arr[j];
                arr[j] = temp;
            }
        }
    }
}

/* 插入排序 */
void insertion_sort(int * arr, int len) {
    int i;
    int j;
    int key;
    for (i = 1; i < len; ++i) {
        key = *(arr + i);
        for (j = (i - 1); j >= 0; j--) {
            if (*(arr + j) > key) {
                *(arr + j + 1) = *(arr + j);
            } else {
                break;
            }
        }
        *(arr + j + 1) = key;
    }
}

static void merge(int * arr, int p, int q, int r) {
    int l_len = q - p + 1;
    int r_len = r - q;

    int l_arr[l_len];
    int r_arr[r_len];

    int i, j;

    for (i = 0; i < l_len; ++i) {
        l_arr[i] = arr[p + i];
    }

    for (j = 0; j < r_len; ++j) {
        r_arr[j] = arr[q + j + 1];
    }

    i = j = 0;

    for (int k = p; k < (r + 1); ++k) {

        if (i == l_len) {
            arr[k] = r_arr[j];
            j++;
            continue;
        }

        if (j == r_len) {
            arr[k] = l_arr[i];
            i++;
            continue;
        }

        if (l_arr[i] <= r_arr[j]) {
            arr[k] = l_arr[i];
            i++;
        } else {
            arr[k] = r_arr[j];
            j++;
        }
    }
}

static void _merge_sort(int * arr, int p, int r) {
    if (p < r) {
        int q = (p + r) / 2;
        _merge_sort(arr, p, q);
        _merge_sort(arr, q + 1, r);
        merge(arr, p, q, r);
    }
}

/* 归并排序 */
void merge_sort(int * arr, int len) {
    _merge_sort(arr, 0, len - 1);
}

/**
 * Max heapify arr, index i as root.
 * if sub left tree exist, it is heap, if sub right tree exist, it same is heap.
 * @param arr
 * @param len
 * @param i
 */
static void max_heapify(int * arr, int len, int i) {

    int r = (i + 1) << 1;  // right index = 2*(i + 1)
    int l = r - 1;         // left index = 2*(i + 1) - 1

    int largest = i;

    if (l >= len) {
        return;
    }

    if (arr[l] > arr[largest]) {
        largest = l;
    }

    if (r < len && arr[r] > arr[largest]) {
        largest = r;
    }

    if (largest != i) {
        // exchange
        arr[i] = arr[i] ^ arr[largest];
        arr[largest] = arr[largest] ^ arr[i];
        arr[i] = arr[largest] ^ arr[i];

        max_heapify(arr, len, largest);
    }
}

/**
 * Build a max heap from array
 * @param arr
 * @param len
 */
static void build_max_heap(int * arr, int len) {
    int first_nonleaf = (len / 2) - 1;   // floor(len / 2) - 1
    for (int i = first_nonleaf; i >= 0; --i) {
        max_heapify(arr, len, i);
    }
};

/* 堆排序 */
void heap_sort(int * arr, int len) {
    build_max_heap(arr, len);
    int temp;
    for (int i = (len - 1); i > 0; --i) {
        temp = arr[0];
        arr[0] = arr[i];
        arr[i] = temp;
        max_heapify(arr, i, 0);
    }
}

/**
 * Partition arr[p..r] to arr[p..q-1] and arr[q+1, r], satisfy below:
 * 1. each element in arr[p..q-1] less than arr[q],
 * 2. each element in arr[q+1..r] greater than arr[q]
 * @param arr
 * @param p
 * @param r
 * @return  q
 */
static int qs_partition(int *arr, int p, int r) {
    int x = arr[r];
    int i = p - 1;
    int temp;
    int q;

    for (int j = p; j < r; ++j) {
        if (arr[j] < x) {
            i++;
            if (i != j) {
                temp = arr[j];
                arr[j] = arr[i];
                arr[i] = temp;
            }
        }
    }
    q = i + 1;
    if (r != q) {
        arr[r] = arr[q];
        arr[q] = x;
    }

    return q;
}

static void _quick_sort(int * arr, int p, int r) {
    int q;
    if (p < r) {
        q = qs_partition(arr, p, r);
        _quick_sort(arr, p, q - 1);
        _quick_sort(arr, q + 1, r);
    }
}

/* 快速排序 */
void quick_sort(int * arr, int len) {
    _quick_sort(arr, 0, len - 1);
}

static int randomized_qs_partition(int * arr, int p, int r) {
    int len = r - p + 1;
    int i = (rand() % len) + p;
    int temp = arr[i];
    arr[i] = arr[r];
    arr[r] = temp;
    qs_partition(arr, p, r);
}

static void _randomized_quick_sort(int * arr, int p, int r) {
    int q;
    if (p < r) {
        q = randomized_qs_partition(arr, p, r);
        _randomized_quick_sort(arr, p, q - 1);
        _randomized_quick_sort(arr, q + 1, r);
    }
}

/* 随机化快速排序 */
void randomized_quick_sort(int * arr, int len) {
    _randomized_quick_sort(arr, 0, len - 1);
}


测试程序

git仓库:

https://gitee.com/hsp8712/test-sort-algorithm

示例输出:

./test-sort-algorithm 10000
---------------------- rand-ordered
>>>>> bubble:
take 349315 us
>>>>> insertion:
take 73987 us
>>>>> merge:
take 1856 us
>>>>> heap:
take 2833 us
>>>>> quick:
take 1320 us
>>>>> randomized_quick:
take 1428 us
---------------------- ordered
>>>>> bubble:
take 161877 us
>>>>> insertion:
take 40 us
>>>>> merge:
take 1150 us
>>>>> heap:
take 2348 us
>>>>> quick:
take 121440 us
>>>>> randomized_quick:
take 836 us
---------------------- nearly ordered
>>>>> bubble:
take 193285 us
>>>>> insertion:
take 20210 us
>>>>> merge:
take 1899 us
>>>>> heap:
take 2562 us
>>>>> quick:
take 1306 us
>>>>> randomized_quick:
take 1352 us
---------------------- reverse ordered
>>>>> bubble:
take 246178 us
>>>>> insertion:
take 144916 us
>>>>> merge:
take 1141 us
>>>>> heap:
take 2404 us
>>>>> quick:
take 169411 us
>>>>> randomized_quick:
take 1123 us

转载于:https://my.oschina.net/shipley/blog/3036345

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
排序算法中,时间复杂度是评估算法性能的重要指标。根据引用和引用的内容,下面是一些常见排序算法的时间复杂度汇总: 1. 冒泡排序:冒泡排序是一种简单但效率较低的排序算法。最坏情况下,冒泡排序的时间复杂度是O(n^2),其中n是待排序元素的数量。最好情况下,当数据已经有序时,冒泡排序的时间复杂度是O(n)。 2. 插入排序:插入排序算法根据待排序序列中的元素逐个插入已排序序列的合适位置。最坏情况下,插入排序的时间复杂度也是O(n^2)。最好情况下,当数据已经有序时,插入排序的时间复杂度是O(n)。 3. 选择排序:选择排序是一种简单的排序算法,每次从未排序的部分选择最小(或最大)的元素,然后放到已排序部分的末尾。选择排序的时间复杂度始终为O(n^2),无论数据是否有序。 4. 快速排序:快速排序是一种高效的排序算法,基于分治的思想。最坏情况下,快速排序的时间复杂度是O(n^2),但通常情况下,快速排序的平均时间复杂度是O(nlogn)。 5. 归并排序:归并排序是一种稳定且高效的排序算法,基于分治和合并的思想。归并排序的时间复杂度始终为O(nlogn),无论数据是否有序。 综上所述,不同的排序算法其时间复杂度不同。冒泡排序和插入排序的时间复杂度是O(n^2),选择排序的时间复杂度也是O(n^2),而快速排序和归并排序的时间复杂度是O(nlogn)。请注意,这些时间复杂度都是在最坏情况下估计的。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值