算法——排序——十大排序

排序算法:

    排序算法的稳定性:

        在待排序的数据中,如果有值相同的数据,在排序的全程中都不会改变它们的先后顺序,则认为该排序算法是稳定的

    冒泡:数据左右进行比较,把最大的数一直交换到最后,特点是该算法对数据的有序性敏感,在排序过程中发现有序可以立即停止排序,如果待排序的数据基本有序,则冒泡的效率非常高

        时间复杂度:最优O(N) 平均:O(N^2)

        稳定的

   

    选择:假定最开始的位置是最小值并记录下标min,然后与后面的数据比较,如果有比min为下标的数据还要小,则更新min,最后判断如果min的值发生了改变,则交换min位置的数据与最开始位置的数据

        虽然选择排序的时间复杂度较高,但是数据交换次数少,因此实际运行速度并不慢

        是冒泡排序的变种,但是没有对数据有序性敏感,数据混乱情况下比冒泡快

        时间复杂度:O(N^2)

        不稳定的    (10 10 1)

        注意:算法的时间复杂度并不能代表算法的实际时间,有时候时间复杂度高的反而速度更快

    插入:把数据看作两个部分,一部分是有序的,把剩余的数据逐个插入进去

        时间复杂度:O(N^2)

        稳定的

   

    希尔:是插入排序的增强版,由于插入排序数据移动的速度比较慢,所以在此基础上增加了增量的概念,从而提高排序的速度

        时间复杂度:O(N^(1.3~2))

        不稳定

   

    快速:

        找到一个标杆p,备份标杆p的值val,一面从左找比val大的数据,找到后赋值给p,更新标杆p的位置到左标杆,然后从右边找比val小的数,找到后也赋值给p,同样更新p到右标杆,反复执行直到左右标杆相与停止,最后把val赋值回p的位置,最终会形成p左边的数都比它小,右边的数都比它大;然后再按照同样的方式对左右两边进行快排,最后全部有序

        快速排序的综合性能最高,因此叫做快速排序,笔试面试考最多

        时间复杂度:O(NlogN)

        不稳定

    归并:

        先把一组待排序的数据拆分成单独的个体,存放到临时空间中,然后两两比较合并,全部合并完成后再从临时空间中拷贝给原内存

        由于使用额外的内存空间避免了数据交换的耗时,是一种典型的以空间换时间的算法

        时间复杂度:O(NlogN)

        稳定

   

    堆:把数据当做当做完全二叉树看待,然后把树调整成大顶堆,然后把堆顶数据交换到末尾,然后数量--,然后重新调整回大顶堆,重复操作,直到数量为1时结束,既可以循环实现也可以递归实现(参考heap.c)

        时间复杂度:O(NlogN)

        不稳定

    计数:

        找出数据中的最大值和最小值,并创建哈希表,把 数据-最小值 作为数组的下标访问哈希表并标记数量,标记完后,遍历哈希表,当表中的值大于0,把 下标+最小值 还原数据依次放回数组中,是一种典型的以空间换时间的算法

        该排序算法理论上速度非常快,它不是基于比较的算法,在一定范围内整数排序时快于任意的一种比较排序算法,但是有很大的局限性:适合排序整形数据,而且数据的范围差别不宜过大,否则会非常浪费内存反而慢于比较的排序,如果数据越平均、重复数越多,性价比越高

        时间复杂度:Ο(N+k)(其中k是整数的范围)

        稳定的

    桶:

        根据数据的值存储到不同的桶中,然后再调用其它的排序算法,度桶中的数据进行排序,然后再从桶中依次拷贝回数组中,从而降低排序的规模以此提高排序的速度,是一种典型的以空间换时间的算法

        缺点:如何分桶、桶范围多大,这些都需要对数据有一定的了解

        时间复杂度:Ο(N+k)

        桶排序的稳定性取决于桶内排序使用的算法

    基数:

        是桶排序的具体实现,首先创建10个队列(链式队列),然后逆序计算出数据的个、十、百...位数,然后入到对应的队列中,结束后依次从队列中出队回数组中,数据下一位继续入队,依次循环,最大值的位数就是循环次数

        缺点:只适合排序正整数数据,又要准备队列

        时间复杂度:Ο(N+k)

        稳定的

#include <stdio.h>

#include <stdlib.h>

#include <stdbool.h>

#include <string.h>

#include <math.h>

#include "list_queue.h"

#define LEN 15

#define TYPE int

#define swap(a,b) {typeof(a) t=(a);(a)=(b);(b)=t;}

void show_arr(TYPE* arr,size_t len)

{

    for(int i=0; i<len; printf("%02d ",arr[i++]));

    printf("\n");

}

typedef void (*SortFP)(TYPE*,size_t);

//  冒泡排序

void bubble_sort(TYPE* arr,size_t len)

{

    printf("%s:\n",__func__);

    //  标志位用于判断排序是否完成,可以提前继续排序

    bool flag = true;

    for(int i=len-1; i>0 && flag; i--)

    {

        flag = false;

        for(int j=0; j<i; j++)

        {

            if(arr[j] > arr[j+1])

            {

                swap(arr[j],arr[j+1]);  

                flag = true;

            }

        }

    }

    show_arr(arr,len);

}

//  选择排序

void select_sort(TYPE* arr,size_t len)

{

    printf("%s:\n",__func__);

    for(int i=0; i<len-1; i++)

    {

        int min = i;

        for(int j=i+1; j<len; j++)

        {

            if(arr[j] < arr[min]) min = j;  

        }

        if(i!=min) swap(arr[i],arr[min]);

    }

    show_arr(arr,len);

}

//  插入排序

void insert_sort(TYPE* arr,size_t len)

{

    printf("%s:\n",__func__);

    //  i是待插入的数据下标

    for(int i=1,j=0; i<len; i++)

    {

        int val = arr[i];

        //  在有序部分找合适的位置插入

        for(j=i-1; j>=0 && arr[j]>val; j--)

        {

            arr[j+1] = arr[j];  

        }

        if(j+1 != i) arr[j+1] = val;

    }

    show_arr(arr,len);

}

//  希尔排序

void shell_sort(TYPE* arr,size_t len)

{

    printf("%s:\n",__func__);

    for(int k=len/2; k>0; k/=2)

    {

        //  i是待插入的数据下标

        for(int i=k,j=0; i<len; i++)

        {

            int val = arr[i];

            //  在有序部分找合适的位置插入

            for(j=i-k; j>=0 && arr[j]>val; j-=k)

            {

                arr[j+k] = arr[j];  

            }

            if(j+k != i) arr[j+k] = val;

        }

    }

    show_arr(arr,len);

}

void _quick_sort(TYPE* arr,int left,int right)

{  

    if(left >= right) return;

    //  计算标杆下标

    int pi = (left+right)/2;

    //  备份标杆的值

    TYPE pv = arr[pi];

    //  备份左右标杆下标

    int l = left, r = right;

    while(l < r)

    {

        //  在pi左边找比pv大的数据

        while(l<pi && arr[l]<=pv) l++;

        if(l < pi)

        {

            //  找到了比pv大数

            arr[pi] = arr[l];

            //  更新pi

            pi = l;

        }

       

        //  从pi右边找比pv小的数

        while(r>pi && arr[r]>=pv) r--;

        if(r > pi)

        {

            //  找到比pv小的数

            arr[pi] = arr[r];

            //  更新pi

            pi = r;

        }

    }

    //  pi左边小于pv  右边大于pv

    //  还原pv

    arr[pi] = pv;

    if(pi - left > 1) _quick_sort(arr,left,pi-1);

    if(right - pi >1) _quick_sort(arr,pi+1,right);

}

//  快速排序

void quick_sort(TYPE* arr,size_t len)

{

    _quick_sort(arr,0,len-1);

    printf("%s:\n",__func__);

    show_arr(arr,len);

}

//  拆分合并 从l到r进行归并

void _merge_sort(TYPE* arr,TYPE* tmp,int l,int r)

{  

    if(l >= r) return;

    int p = (l+r)/2;

    //  对左部分归并

    _merge_sort(arr,tmp,l,p);

    //  对右部分归并

    _merge_sort(arr,tmp,p+1,r);

    //  左右部分各自有序

    //  l左部分最左 p左部分最右  p+1右部分最左 r右部分最右

    if(arr[p] <= arr[p+1]) return;

    int i = l, j = p+1, k = l;

    while(i<=p && j<=r)

    {

        if(arr[i] <= arr[j])    //一定是<= 才能稳定

            tmp[k++] = arr[i++];

        else

            tmp[k++] = arr[j++];

    }

    //  比完后还没比较的放入tmp末尾

    while(i<=p) tmp[k++] = arr[i++];

    while(j<=r) tmp[k++] = arr[j++];

    memcpy(arr+l,tmp+l,sizeof(TYPE)*(r-l+1));

}

//  归并排序

void merge_sort(TYPE* arr,size_t len)

{

    TYPE* tmp = malloc(sizeof(TYPE)*len);

    _merge_sort(arr,tmp,0,len-1);

    free(tmp);

    printf("%s:\n",__func__);

    show_arr(arr,len);

   

}

//  计数排序

void count_sort(TYPE* arr,size_t len)

{

    TYPE max = arr[0], min = arr[0];    

    for(int i=1; i<len; i++)

    {

        if(arr[i] > max) max = arr[i];

        if(arr[i] < min) min = arr[i];

    }

    //  哈希表

    int* tmp = calloc(4,max-min+1);

    //  标记哈希表

    for(int i=0; i<len; i++)

    {

        tmp[arr[i]-min]++;  

    }

   

    //  还原数据到arr中

    for(int i=0,j=0; i<=max-min; i++)

    {

        while(tmp[i]--)

        {

            arr[j++] = i+min;  

        }

    }

    free(tmp);

    printf("%s:\n",__func__);

    show_arr(arr,len);

}

//  cnt桶数 ragne桶中数据范围

void _bucket_sort(TYPE* arr,size_t len,int cnt,TYPE range)

{

    //  申请桶内存

    //  bucket指向每个桶的开头

    //  bucket_end 指向每个桶的末尾

    TYPE* bucket[cnt], *bucket_end[cnt];

    for(int i=0; i<cnt; i++)

    {

        //  数据可能全部在一个桶中

        bucket[i] = malloc(len*sizeof(TYPE));

        //  末尾指针指向开头

        bucket_end[i] = bucket[i];

    }

    //  把所有数据 按照桶的范围放入对应桶中

    for(int i=0; i<len; i++)

    {

        for(int j=0; j<cnt; j++)

        {

            if(range*j<=arr[i] && arr[i]<range*(j+1))

            {

                *(bucket_end[j]) = arr[i];

                bucket_end[j]++;

            }

        }

    }

    for(int i=0; i<cnt; i++)

    {

        //  计算每个桶中元素数量

        int size = bucket_end[i] - bucket[i];

        //  使用其他排序算法对每个桶进行单独排序

        if(1 < size) select_sort(bucket[i],size);

        //  把桶按照先后顺序,重新放入数组中

        memcpy(arr,bucket[i],size*sizeof(TYPE));

        arr += size;

        free(bucket[i]);

    }

}

//  桶排序

void bucket_sort(TYPE* arr,size_t len)

{

    _bucket_sort(arr,len,4,25);

    printf("%s:\n",__func__);

    show_arr(arr,len);

}

//  基数排序

void radix_sort(TYPE* arr,size_t len)

{

    //  创建10个队列

    ListQueue* queue[10] = {};

    for(int i=0; i<10; i++)

    {

        queue[i] = create_list_queue();

    }

    //  计算最大值的位数

    TYPE max = arr[0];

    for(int i=1; i<len; i++)

    {

        if(arr[i] > max) max = arr[i];  

    }

    int cnt_max = 0;

    while(max)

    {

        cnt_max++;

        max /= 10;

    }

    // i是1表示个位 2表示十位...

    for(int i=1; i<=cnt_max; i++)

    {

        int mod = pow(10,i);

        int div = mod/10;

        //  把所有数据入队

        for(int j=0; j<len; j++)

        {

            //  逆序获取每个数的每一位数

            int index = arr[j]%mod/div;

            //  入到对应下标的队列中

            push_list_queue(queue[index],arr[j]);

        }

        int k = 0;

        //  依次把队列数据出队回arr

        for(int j=0; j<10; j++)

        {

            while(!empty_list_queue(queue[j]))  

            {

                arr[k++] = front_list_queue(queue[j]);  

                pop_list_queue(queue[j]);

            }

        }

    }

    for(int i=0; i<10; i++)

    {

        destory_list_queue(queue[i]);  

    }

    printf("%s:\n",__func__);

    show_arr(arr,len);

}


 

int main(int argc,const char* argv[])

{

    TYPE arr[LEN] = {};

    //  sort 函数指针数组

    SortFP sort[] = {bubble_sort,select_sort,insert_sort,shell_sort,quick_sort,merge_sort,count_sort,bucket_sort,radix_sort};

    for(int i=0; i<sizeof(sort)/sizeof(sort[0]); i++)

    {

        for(int j=0; j<LEN; j++)

        {

            arr[j] = rand()%100;    

        }

        printf("-------------------------------\n");

        printf("排序前:\n");

        show_arr(arr,LEN);

        sort[i](arr,LEN);

    }

}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

xiaoyu1381

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值