冒泡排序
冒泡排序(Bubble Sort),原理为从头(水底)开始,依次比较相邻元素的大小,若前一个元素比后一个大(以升序为例),则交换两个元素,直到把最大的元素放到尾部(浮出水面);接下来,依次把第二大,第三大…的元素放在尾部,直到整个序列有序。
冒泡排序的优化:检测每一次排序是否有元素交换发生,若某次排序没有元素交换,证明序列已有序,可退出循环。
#include <stdio.h>
void swap(int* a, int* b);
void maopao(int *arr, int size);
int main()
{
int size;
scanf("%d", &size);
int arr[1000];
for(int i = 0; i < size; i++)
scanf("%d", &arr[i]);
maopao(arr, size);
for(int i = 0; i < size; i++)
printf("%d ", arr[i]);
}
void maopao(int *arr, int size)
{
int tmp, change = 1;
for (int i = 0; i < size - 1 && change; i++)
{
change = 0;
for (int j = 0; j < size - i - 1; j++)
{
if (arr[j] > arr[j+1])//比较相邻的元素,如果第一个比第二个大,就交换它们两个
{
swap(&arr[j], &arr[j+1]);
}
}
}
}
void swap(int *a, int *b)
{
int tmp;
tmp = *a;
*a = *b;
*b = tmp;
}
时间复杂度:O(N2)
稳定性:稳定
选择排序
选择排序(Selection Sort),原理为寻找最大元素(以升序为例),将其放置尾部,再寻找第二大元素,将其置于尾部…直至整个序列有序。
#include <stdio.h>
void swap(int *a, int *b);
void xuanze(int *arr, int size);
int main()
{
int size;
scanf("%d", &size);
int arr[1000];
for(int i = 0; i < size; i++)
scanf("%d", &arr[i]);
xuanze(arr, size);
for(int i = 0; i < size; i++)
printf("%d ", arr[i]);
}
void xuanze(int *arr, int size)
{
int k, tmp;//k为待排序序列中最大值元素下标
for (int i = 0; i < size - 1; i++)
{
k = i;
for (int j = i + 1; j < size; j++)
if (arr[j] < arr[k])
k = j;
swap(&arr[i], &arr[k]);
}
}
void swap(int *a, int *b)
{
int tmp;
tmp = *a;
*a = *b;
*b = tmp;
}
时间复杂度:O(N2)
稳定性:不稳定
插入排序
插入排序(Insertion Sort),原理为将一个个元素插入到有序列的合适位置,使得新的序列依然有序。
#include <stdio.h>
void charu(int *arr, int size);
int main()
{
int size;
scanf("%d", &size);
int arr[1000];
for(int i = 0; i < size; i++)
scanf("%d", &arr[i]);
charu(arr, size);
for(int i = 0; i < size; i++)
printf("%d ", arr[i]);
}
void charu(int *arr, int size)
{
int tmp;
for (int i = 1; i < size; i++)//取未排序数列首位数
{
if (arr[i] < arr[i-1])//若该数大于有序数列末位数,则不用移动位置,置于尾端即有序
{
tmp = arr[i];//将该数存在tmp处
for (int j = i - 1; j >= 0 && arr[j] > tmp; j--)//将数列由尾端向前扫描,找到该数正确位置
{
arr[j+1] = arr[j];
}
arr[j+1] = tmp;//插入该数
}
}
}
时间复杂度:O(N2)
稳定性:稳定
希尔排序
希尔排序(Shell’s Sort)的核心是以某个增量h为步长跳跃分组(类似于按该增量报数,报相同数的元素为一组)进行插入排序。由于分组的步长h逐步缩小, 所以也叫“缩小增量排序”(Diminishing Increment Sort)。
希尔排序的时间复杂度是所取步长序列的函数, 该函数在数学上无确定的解, 所以一直以来SHELL算法的分析被认为是一个复杂的问题。下面,给出常见的shell增量。
-
原始希尔序列:d(max)=N/2, d(i)=d(i+1)/2
-
Hibbard增量序列:d(k)=2^k-1
-
Sedgewick增量序列:{1, 5, 19, 41, 109…} 猜想:d(i)=9×4i- 9x2i +1或d(i)=4i-3x2i+1
有关shell增量的研究
下面的代码以原始希尔序列为例
#include <stdio.h>
void xier(int *arr, int size);
int main()
{
int size;
scanf("%d", &size);
int arr[1000];
for(int i = 0; i < size; i++)
scanf("%d", &arr[i]);
xier(arr, size);
for(int i = 0; i < size; i++)
printf("%d ", arr[i]);
}
void xier(int *arr, int size)
{
int tmp, j, d;
for (d = size/ 2; d > 0; d /= 2)//确定增量大小
{
for (int i = d; i < size; i++)//对每个子数列进行插入排序
{
tmp = arr[i];
for (j = i - d; j >= 0 && tmp < arr[j]; j -= d)
{
arr[j + d] = arr[j];
}
arr[j + d] = tmp;
}
}
}
时间复杂度:
稳定性:不稳定
快速排序
快速排序(Quick Sort)是对冒泡排序的一种改进。其原理为通过一趟排序将要排序的数据分割成独立的两部分,其中一部分的所有元素都比另外一部分都要小,然后再按此方法对这两部分数据分别进行快速排序,直至整个序列有序。
#include<stdio.h>
void kuaisu(int *arr, int left, int right);
int main()
{
int size;
scanf("%d", &size);
int arr[1000];
for(int i = 0; i < size; i++)
scanf("%d", &arr[i]);
kuaisu(arr, 0, size-1);
for(int i = 0; i < size; i++)
printf("%d ", arr[i]);
}
void kuaisu(int arr[], int left, int right)
{
if (left >= right)
{
return;
}
int i = left;
int j = right;
int centre = arr[left];
while (i < j)
{
while (i < j && centre <= arr[j])//从右向左依次搜索,找一个比中心数小的
{
j--;
}
arr[i] = arr[j];//把这个数放在左端
while (i < j && arr[i] <= centre)//从左向右依次搜索,找一个比中心数大的
{
i++;
}
arr[j] = arr[i];//把这个数放在右端
}
arr[i] = centre;//更新中心数位置
kuaisu(arr, left, i - 1);
kuaisu(arr, i + 1, right);
}
时间复杂度:O(Nlog2N)
稳定性:不稳定
计数排序
计数排序(Counting Sort)是一种不基于比较的排序算法。其原理为对于序列中每一个元素,统计比它小(或大)的元素个数,以找到该元素的正确位置。
#include <stdio.h>
int max(int *arr, int size);
int min(int *arr, int size);
void countingSort(int *arr, int size, int range, int Min);
int main()
{
int size;//元素个数
scanf("%d", &size);
int *arr = (int *)malloc(size*sizeof(int));
//输入
for(int i = 0; i < size; i++)
scanf("%d", &arr[i]);
int range;//元素值的范围
range = max(arr,size) - min(arr,size) + 1;
int Min = min(arr,size);
countingSort(arr, size, range, Min);
//输出
for(int i = 0; i < size; i++)
printf("%d ",arr[i]);
}
int max(int *arr, int size)
{
int max = arr[0];
for(int i = 1; i < size; i++)
if(arr[i] > max) max = arr[i];
return max;
}
int min(int *arr, int size)
{
int min = arr[0];
for(int i = 1; i < size; i++)
if(arr[i] < min) min = arr[i];
return min;
}
void countingSort(int *arr, int size, int range, int Min)
{
int *counting = (int *)malloc(range*sizeof(int));
//初始计数数组
for(int i = 0; i < range; i++)
counting[i] = 0;
//计数数组开始计数
for(int i = 0; i < size; i++)
counting[arr[i]-Min]++;
int d = 0;//用于遍历计数数组时,表示当前位置
for(int i = 0; i < size; i++)
{
while(counting[d]==0) d++;
arr[i] = d + Min;
counting[d]--;
}
}
时间复杂度:O(N+k) (k为元素值的范围,即arrmax - arrmin+1)
稳定性:稳定
睡眠排序
睡眠排序(Sleep Sort):
通过为待排序的元素启动独立的任务,每个任务按照待排元素的key执行相对应的睡眠时间,然后及时的将序列中的元素收集到一起,达到排序的目的。
人话来讲:
睡眠排序主要是根据CPU的调度算法实现的,对一组数据进行排序,不能存在负数值,这个数是多大,那么就在线程里睡眠它的10倍再加10,不是睡眠和它的数值一样大的原因是,当数值太小时,误差太大,睡眠的时间不比输出的时间少,那么就会存在不正确的输出结果。
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
int main(int c, char **v)
{
while (--c > 1 && !fork());
sleep(c = atoi(v[c]));
printf("%d\n", c);
wait(0);
return 0;
}
猴子排序
猴子排序(Bogo Sort),望文生义 顾名思义,就是猴子的排序方法。
我们先介绍无限猴子定理:我们有一只会打字的猴子,不过这只猴子不识字,只会在键盘上乱敲(像极了写代码的我)。在无穷长的时间后,即使是随机打字的猴子也可以打出一些有意义的单词,比如,cat, dog。因此,可以类推,会有一个足够幸运的猴子或连续或不连续地打出一本书,即使其几率比连续抓到一百次同花顺还要低。但在足够长的时间(长到你数不清它的秒数有多少位)后,其发生是必定的。
于是,现在我们有一只会排序的猴子。它不懂算法,甚至看不懂数字,所以只会将数字随意排列。现有一无序数列,不断随机排列该数列元素的顺序,在无限长的时间内,该数列必定会变成有序的。我们只需在它变为有序时,停止随机排序,即可得到有序数列。
下面是伪代码:
#include <stdio.h>
#include "monkey.h"
int judge(int *arr, int size);
int main(int argc, char const *argv[])
{
int size;
scanf("%d",&size);
int *arr = (int *)malloc(size*sizeof(int));
while(judge(arr,size))
{
monkeysort(arr, size);
}
for(int i = 0; i < size; i++)
printf("%d ",arr[i]);
}
int judge(int *arr, int size)
{
for(int i=1;i<size;i++)
{
if(arr[i]<arr[i-1]) return 1;
}
return 0;
}
量子猴排
“其实我一直不知道这个怎么翻译比较好听,然后看到有人答了“量子猴排”觉得翻译得很好。猴子排序或者说Bogo sort肯定很多人都知道,但是它的问题很明显,太慢了。好在量子物理的发展,给了我们用这个方法解决问题的一线生机。
所谓量子猴排就是洗牌的时候,使用量子化随机排列(quantumly randomized)。这样的话,我们在观测这组数之前,这组数的状态是叠加的,参见Schrรถdinger’s cat。通过这种量子化随机排列,我们划分出来了个平行宇宙。接下来,在某个宇宙00729中,观测一下这组数,发现运气不好,没有排序好,那么我们就销毁掉这个宇宙。然后再看看其他宇宙的运气怎么样。终于,在一个宇宙04008中,发现刚好是排好序的数组。那么我们就保留宇宙04008。最后,没有被销毁的宇宙中,数组都是恰好一次被排好序的。
算法结束,我们来分析一下量子猴排的时间复杂度。嗯,O(N) ,看是不是快了很多。”
蠢猴排序
“了解了猴子排序和量子猴排,有的同学们可能并不会觉得慢或者麻烦。相反,说不定觉得其实猴子排序还是太快了呢?!所以就有了蠢猴排序。
蠢猴排序,就是先将一个元素猴子排序一下,这是最简单的。然后拷贝第二个元素进来,判断一下大小,如果顺序不对则对这两个元素猴子排序一下。接下里拷贝第三个元素进来,在判断一下这下顺序是不是错了,如果错了则再对这三个元素猴子排序一下……一直这么进行下去。
关于蠢猴排序的时间复杂度
有的人说是 O( n×(n!)n ) ,
有的人说是 O( (n!)n-k ),
有的人说是 O( (n!)n! )…
反正大家大致的意思是,用蠢猴排序来对一个稍微长点的乱序数组进行排序的话,就有可能一直运行到宇宙的热寂(Heat death of the universe)都不会结束。”