总结
稳定 时间复杂度 平均时间复杂度 空间复杂度
冒泡排序 √ O(n2) O(n2) O(1)
选择排序 × O(n2) O(n2) O(1)
插入排序 √ O(n2) O(n2) O(1)
二叉排序 √/× O(n2) O(n*log2n) O(n)
快速排序 × O(n2) O(n*log2n) O( log2n )~O(n)
基数排序 √ O(n*logrm) O(n*logrm) O(2r) 【注】r为基数,m为堆数
希尔排序 × O O O(1)
堆排序 × O(n*log2n) O(n*log2n) O(1)
归并排序 √
锦标赛排序,类似于二叉树排序。
冒牌排序
for (int i = 0; i < size - 1; i++) {
for (int j = 0; j < size - i - 1; j++) {
if (array[j+1] < array[j]) {
int tmp = array[j+1]; array[j+1] = array[j]; array[j] = tmp;
}
}
}
选择排序
//每次选择最小的一个元素,进行位置交换。
void select_sort(int array[], int size)
{
int i, j, tmp;
for (i =0; i < size-1; i++ ) {
index = i;
for (j = i+1; j < size; j++) {
//选择剩余元素中最小的一个元素,记录在index中
if (array[index] > array[j])
index = j;
}
if (i != index) {
//进行元素的交换
tmp = array[index];
array[index] = array[i];
array[i] = tmp;
}
}
}
插入排序
//在一个已经有序的小序列基础上,一次插入一个元素。
void insert_sort(int array[], int size)
{
int i, j, tmp;
for (i = 1; i <= size; i++) {
tmp = array[i]; //待插入的元素
j = i-1;
//与已排序的数逐一比较大小,大于tmp时,该数移后
while ((j > 0) && (array[j] > tmp)) {
array[j+1] = array[j];
j--;
}
array[j+1] = tmp;
}
}
快速排序
- 快速排序是C.R.A.Hoare于1962年提出的一种划分交换排序。它采用了一种分治的策略,通常称其为分治法(Divide-and-ConquerMethod)。
基本思想
- 先从数列中取出一个数作为基准数。
- 分区过程,将比这个数大的数全放到它的右边,小于或等于它的数全放到它的左边。
- 再对左右区间重复第二步,直到各区间只有一个数。
【注】:进行函数调用时,传入的参数为:quick_sort(data,0, size-1);
void swap(int *array, int x, int y)
{
int tmp = array[x];
array[x] = array[y];
array[y] = tmp;
}
int partition1(int *array, int start, int end) {
int pos = (start + end) / 2;
swap(array,pos,end);
int key = array[end];
int i = start-1;
for(int j=start;j<=end;++j){
if(array[j]<key){
swap(array,++i,j);
}
}
swap(array,++i,end);
return i;
}
int partition(int *array, int start, int end) {
int pos = (start+end)/2;
int low = start+1;
int high = end;
int key = array[pos];
swap(array, pos, start);
while (low < high) {
while (low < high && array[low] <= key)
low++;
while (low < high && array[high] > key)
high--;
if (low >= high)
break;
swap(array, low, high);
}
if (array[low] > key) {
swap(array, start, low-1);
return low-1;
} else {
swap(array, start, low);
return low;
}
}
void quick_sort(int *array, int start, int end){
if (start >= end) {
return;
}
int pos = partition(array, start, end);
quick_sort(array, start, pos-1);
quick_sort(array, pos+1, end);
}
二叉排序
//生成二叉排序树,然后将二叉树采用中序遍历重新对array赋值
struct node{
int value;
struct node *left;
struct node *right;
};
struct node *new_node(int value)
{
struct node *new_node = (struct node*)malloc(sizeof(struct node));
if (node) {
node->value = value;
node->left = NULL;
node->right = NULL;
}
return node;
}
void insert_to_tree(struct node *root, struct node *node)
{
struct node *tmp = root, *prev = tmp;
while (tmp) {
prev = tmp;
if (tmp->value < node->value) {
tmp = tmp->right;
} else {
tmp = tmp->left;
}
}
if (prev->value < node->value) {
prev->right = node;
} else {
prev->left = node;
}
}
int binary_tree_sort(int array[], int size)
{
int i = 0;
struct node *root = new_node(array[i]);
struct node *node = NULL;
struct stack *s = NULL;
for (i = 1; i < size; i++) {
node = new_node(array[i]);
if (!node) {
return 0;
}
insert_to_tree(root, node);
}
//中序遍历二叉树,重新填充数组
s = create_stack();
if (s == NULL) {
printf("Create stack failed!\n");
return 0;
}
node = root;
i = 0;
while (node || !is_empty(s)){
while (node) {
push(&s, node);
node = node->left;
}
node = pop(&s);
array[i] = node->value;
node = node->right;
i++;
if (i >= size) {
break;
}
}
destroy_stack(&s);
return (i <= size);
}
基数排序
int find_max_num(int array[], int size)
{
int i = 0, max = 0;
for (; i < size; i ++) {
if (array[i] > max) {
max = array[i];
}
}
return max;
}
int get_loop_times(int num)
{
int loop = 0, tmp = 0;
do {
tmp = num / 10;
loop++;
} while (tmp != 0);
return loop;
}
//将数字分配到各自的桶中,然后按照桶的顺序输出排序结果
void __radix_sort(int array[], int size, int loop)
{
//建立一组桶
int buckets[10][size] = {0};
/*
求桶的index的除数
如798
个位桶index = (798 / 1) % 10 = 8;
十位桶index = (798 / 10) % 10 = 9;
百位桶index = (798 / 100) % 10 = 7
*/
int tmp_num = (int) pow(10, loop-1);
int i, j, index, k;
for (i = 0; i < size; i++) {
index = (array[i] / tmp_num) % 10;
for (j = 0; j < size; j++) {
if (buckets[index][j] == 0) {
buckets[index][j] = array[i];
break;
}
}
}
//将桶中的数,倒回到原有数组中
k = 0;
for (i = 0; i < 10; i++) {
for (j = 0; i < size; j++) {
if (buckets[i][j] != 0) {
array[k] = buckets[i][j];
buckets[i][j] = 0;
k++;
}
}
}
}
//从个位开始,向高位依次放入桶中,进行排序
void radix_sort(int array[], int size)
{
//获取数组中的最大数
int max_num = find_max_num(array, size);
//获取最大数的位数,次数也是再分配的次数
int loop_times = get_loop_times(max_num);
int i = 0;
//对每一位进行桶分配
for (i = 1; i <= loop_times; i++) {
__radix_sort(array, size, i);
}
}
希尔排序
- 插入排序的一种,是针对直接插入排序算法的改进。本质上是一种分组插入排序法。
基本思想:
- 先取一个小于n的整数d1作为第一个增量,把文件的全部记录分成d1个组。所有距离为dl的倍数的记录放在同一个组中。先在各组内进行直接插人排序;
- 然后,取第二个增量d2<d1重复上述的分组和排序,直至所取的增量dt=1(dt<dt-l<…<d2<d1),即所有记录放在同一组中进行直接插入排序为止。
代码实现
void shellPass(int *R,int n, int d)
{//希尔排序中的一趟排序,d为当前增量
int tmp;
for(i=d+1; i<=n;i++) //将R[d+1..n]分别插入各组当前的有序区
if(R[i]<R[i-d]){
tmp=R[i];
j=i-d; //R[0]只是暂存单元,不是哨兵
do {//查找R[i]的插入位置
R[j+d] = R[j]; //后移记录
j=j-d; //查找前一记录
}while(j>0 && tmp < R[j]);
R[j+d]=tmp; //插入R[i]到正确的位置上
}
}
}
void shellSort(int *R, int n)
{
int increment = n; //增量初值,不妨设n>0
do {
increment=increment/3 +1; //求下一增量
shellPass(R,increment); //一趟增量为increment的Shell插入排序
}while(increment>1)
}
堆排序
堆:
- n个元素序列{k1,k2,...,ki,...,kn},当且仅当满足下列关系时称之为堆:
- 堆中某个节点的值总是不大于或不小于其父节点的值
- 堆总是一棵完全树(若树的高度为H,则1~H-1层节点的个数都达到最大值)
- 堆一般用数组来表示,假设节点的下标为i,则:
- Parent(i) : return Floor(i/2);
- Left(i): return 2*i + 1;
- Right(i): return 2*i + 2;
- 含有N个元素的堆A的高度为Floor(lgN)
基本思想:
- 堆是一个完全二叉树,故而其最下层的叶子节点是从左至右存在的
- 最小堆的建立:
- 先建立一个最小堆,然后依次进行如下操作:
- 将堆顶与 index交换,然后重新调整size为0~index-1的堆
- 重复以上操作,直到堆的大小为0
算法实现:
inline void swap(int *a, int num1, int num2)
{
int tmp = a[num1];
a[num1] = a[num2];
a[num2] = tmp;
}
/* 对该数进行下滤操作,直到该数比左右节点都小就停止下滤。
即对某个根节点的值进行位置下降调整,使该值比其左右子节点都小;
若该节点是叶子节点,则无法while循环 */
void PercolateDown(int num[] , int index,int size) {
int min;//最小指向下标
while (index * 2 + 1<size) {
//若该节点有左子结点,则假设该节点为最小
min = index * 2 + 1;
if (index * 2 + 2<size) {//若有右子节点,比较获得左右子节点的最小值
if (num[min] > num[index * 2 + 2]) {
min = index * 2 + 2;
}
}
if (num[index] < num[min]) {//当前的节点小于左右子节点,则循环结束
break;
} else {
Swap(num, index, min);//将当前节点与最小子节点进行交换
index = min;// 重设下标,继续对该节点进行下虑
}
}// while
}
/* 建堆方法,只需线性时间建好;
建堆的结果:数组的第一个元素(即树根)是所有元素中的最小值,索引小于等于size/2-1的其它元素(即其它非叶子节点)的值都是其所在子树的最小值 */
void BuildHeap(int num[] ,int size) {
int i;
//从最后一个非叶子节点开始,对每个非叶子节点进行最小根调整,保证每个根节点都是其子树中的最小值
for (i = size / 2 - 1; i >= 0; i--) {
PercolateDown(num, i,size);
}
}
void HeapSort(int num[] ,int size)
{
int i;
int iLength=size;
//建立小顶堆
BuildHeap(num,size);
/*
堆排序-建立大顶堆,数组按由大到小排序
基本思路:
从数组的最后一个元素开始,将其与第一个元素交换,然后对该元素进行下虑操作
此时第一个元素总是当前堆的最小值。
*/
for (i = iLength - 1; i >= 1; i--) {
Swap(num, 0, i);
size--; //每交换一次,将堆的规模减小一次
PercolateDown(num, 0, size);//将新的首元素下虑操作
}
}
归并排序
工作原理
- 将序列每相邻两个数字进行归并操作,形成个序列,排序后每个序列包含两个元素
- 将上述序列再次归并,形成个序列,每个序列包含四个元素
- 重复步骤2,直到所有元素排序完毕
代码实现
递归实现
void merge_sort(int array[], unsigned int first, unsigned int last)
{
int mid = 0;
if(first<last)
{
/*mid = (first+last)/2;*/ /*注意防止溢出*/
/*mid = first/2 + last/2;*/
/*mid = ((first & last) + (first ^ last) >> 1);*/
mid = ((first & last) + ((first ^ last) >> 1)); /*修正上一句优先级错误*/
merge_sort(array, first, mid);
merge_sort(array, mid+1,last);
merge(array,first,mid,last);
}
}
原地merge
void merge(int array[], int low,int mid,int high)
{
int j = mid+1, k = 0;
while(j <= high) {
if(array[j-1] < array[j])
break;
tmp = array[j];
k = j-1;
while ( k >= 0 && tmp < array[k]){
array[k + 1] = array[k];
k--;
}
array[k] = tmp;
j++;
}
}
增加一个临时数组来merge
void merge(int array[], int low,int mid,int high)
{
int tmp_arr[high-low+1] = {0};
int i = low, j = mid+1, k = 0;
while(i <= mid && j <= high){
if(array[i] < array[j]) {
tmp_arr[k++] = array[i++];
} else {
tmp_arr[k++] = array[j++];
}
}
while(i <= mid)
tmp_arr[k++] = array[i++];
while(j <= high)
tmp_arr[k++] = array[j++];
i = low, k = 0;
while(i <= high)
array[i++] = tmp_arr[k++];
}