排序算法(C语言)

冒泡排序

选择排序

插入排序

希尔排序

快速排序

计数排序

睡眠排序

猴子排序
 
 

冒泡排序

冒泡排序(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;
        }
    }
}
时间复杂度:
  • 原始希尔序列 Tworse=O(n2)
  • Hibbard增量序列 Tworse=O(n3/2)
  • Sedgewick增量序列 Tworse=O(n4/3)
稳定性:不稳定

 
 

快速排序

快速排序(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,不是睡眠和它的数值一样大的原因是,当数值太小时,误差太大,睡眠的时间不比输出的时间少,那么就会存在不正确的输出结果。

某dalao的代码

#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)都不会结束。”

  • 4
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值