对排序算法的介绍和实现,后续再增加更多...
基本原理简述
冒泡排序
复杂度: 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