排序算法(c语言实现)

来源:互联网 发布:vr软件下载 编辑:程序博客网 时间:2024/04/29 16:06

作为一个程序猿,编写程序,排序算法算是最基本的了。这篇博客中,我尽量以最通俗易懂的语言,最简单的代码来讲清楚这些比较常见的排序算法。

好了,首先来看一下各种排序算法的效率和稳定性方面的总结(这张图很好,网上找的):


下面我就根据这张图来说明各种排序算法。

一.插入排序

1.直接插入排序

直接插入排序就是把后面的元素依次插入到前面的有序列中,(第一次以第一个元素为有序列),插入位置后面的元素后移。下面是代码.

#include<stdio.h>int main(){    int sqList[]= {1,3,6,7,2,4,4,0,9,8};    insertSortDirect(sqList,10);    printArr(sqList, 10);    return 0;}void insertSortDirect(int *sqList, int length){    int i;    for(i = 1; i < length; i++)//第一次把下标为0的作为有序序列    {        int temp = sqList[i];        int j;        for(j = i; j > 0; j--)        {            if(sqList[j - 1] > temp)            {                 sqList[j] = sqList[j - 1];            }            else                break;        }        sqList[j] = temp;    }}void printArr(int *sqList, int length){    int i;    for(i = 0; i < length; i++)    {        printf("%d ", sqList[i]);    }}

2.shell排序

shell排序其实就是直接插入排序的扩展。举个例子,shell排序先将待排数组以3为间隔分为几个子数组,对这几个子数组进行直接插入排序,然后以2为间隔重复,最后以1为间隔重复(此时就是直接插入排序)。因为对基本有序的数组进行直接插入排序效率很高。下面是代码。

#include<stdlib.h>void shellInsert(int *sqList, int length, int increment){    int i;    for(i = increment; i < length; i++)    {        int temp = sqList[i];        int j;        for(j = i; j >= increment; j = j - increment)//不是把愿数组真的分为几个子数组,因为每一次的shellInsert的步长是固定的,所以可以这样        {            if(temp < sqList[j - increment])            {                sqList[j] = sqList[j - increment];            }            else                break;        }        sqList[j] = temp;    }}void shellSort(int *sqList, int length1, int *dlta, int length2){    int i;    for(i = 0; i < length2; i++)    {        shellInsert(sqList, length1, dlta[i]);    }}void printArr(int *sqList, int length){    int i;    for(i = 0; i < length; i++)    {        printf("%d ", sqList[i]);    }}int main(){    int sqList[]= {1,3,6,7,2,4,4,0,9,8};    int dlta[] = {3, 2, 1};    shellSort(sqList, 10, dlta, 3);    printArr(sqList, 10);    return 0;}

扩展几个:

1.折半插入排序

在直接插入排序时,用二分查找法来寻找待插入的位置。

#include <stdio.h>void insertSortB(int *sqList, int length){    int i;    for(i = 1; i < length; i++)    {        int temp = sqList[i];        int max = i - 1;        int min = 0;        while(min <= max)        {            int center = (max + min)/2;            if( temp < sqList[center])                max = center - 1;            else                min = center + 1;        }        int j;        for(j = i - 1; j >= max + 1; j--)        {            sqList[j + 1] = sqList[j];        }        sqList[max + 1] = temp;    }}void printArr(int *sqList, int length){    int i;    for(i = 0; i < length; i++)    {        printf("%d ", sqList[i]);    }}int main(){    int sqList[]= {1,3,6,7,2,4,4,0,9,8};    insertSortB(sqList, 10);    printArr(sqList, 10);    return 0;}

2.2-路插入排序

3.表插入排序

二.选择排序

1.直接选择排序

直接选择排序就是每次寻找最大(小)的元素位置,然后将此元素与紧跟在有序列后面的第一个元素交换。直到有序列等于数组长度。虽然有交换,但是是基于选择的。

#include<stdlib.h>void printArr(int *sqList, int length){    int i;    for(i = 0; i < length; i++)    {        printf("%d ", sqList[i]);    }}int selectMin(int *sqList, int low, int  high){    int min = sqList[low];    int minLocation = low;    int i;    for(i = low; i < high; i++)    {        if(sqList[i] < min)        {            min = sqList[i];            minLocation = i;        }    }    return minLocation;}void simpleSelectSort(int *sqList, int length){    int i;    for(i = 0; i < length; i++)    {        int temp = sqList[i];        int minLocation = selectMin(sqList, i, length);        sqList[i] = sqList[minLocation];        sqList[minLocation] = temp;    }}int main(){    int sqList[]= {1,3,6,7,2,4,4,0,9,8};    simpleSelectSort(sqList, 10);    printArr(sqList, 10);    return 0;}


2.堆排序

堆排序也很简单,原理很容易懂,主要是循环的结束判断用到了完全二叉树的性质。其实二叉树的性质大家也不用死记硬背,用的时候画一颗树推一下就好了。下面是代码,很容易看懂。

#include<stdio.h>void print(int *sqList, int length){    int i;    for(i = 0; i < length; i++)    {        printf("%d ", sqList[i]);    }}//m为调整的开始点,n为m堆得节点的最大下标void heapAdjust(int *sqList, int i, int n){    int index = i;    int temp = sqList[i];    i = 2 * i + 1;    while(i <= n)    {        if(i + 1 <= n && sqList[i] < sqList[i + 1])            i++;        if(temp >= sqList[i])            break;        sqList[index] = sqList[i];        sqList[i] = temp;        index = i;        temp = sqList[i];        i = 2 * i + 1;    }}//从倒数第二层的第一个元素开始依次向上,对每个节点进行调整//            0//          /    \//         1      2//       /   \   /  \//      3     4  5   6//     ///     7//所以构造最大堆时,调整函数的参数n可以一直是数组长度void creatHeap(int *sqList, int length){    int i;    for(i = (length - 1) / 2; i >= 0; i--)        heapAdjust(sqList, i, length - 1);}int main(){    int sqList[] = {65, 0, 78, 18, 4, 66, 97};    creatHeap(sqList, 7);    int temp = sqList[0];    sqList[0] = sqList[6];    sqList[6] = temp;    int i;    for(i = 5; i >= 0; i--)    {        heapAdjust(sqList, 0, i);        temp = sqList[0];        sqList[0] = sqList[i];        sqList[i] = temp;    }    print(sqList, 7);}


三.交换排序

1.冒泡排序

每趟冒泡的过程,就是不断交换相邻元素,以此把最大(小)的元素交换到有序列后面。

#include<stdlib.h>void printArr(int *sqList, int length){    int i;    for(i = 0; i < length; i++)    {        printf("%d ", sqList[i]);    }}void swap(int *sqList, int i, int j){    sqList[i] = sqList[i] + sqList[j];    sqList[j] = sqList[i] - sqList[j];    sqList[i] = sqList[i] - sqList[j];}void bubbleSort(int *sqList, int length){    int i;    for(i = 0; i < length; i++)    {        int j;        for(j = 0; j < length - i -1; j++)        {            if(sqList[j] > sqList[j + 1])                swap(sqList, j, j + 1);        }    }}int main(){    int sqList[]= {1,3,6,7,2,4,4,0,9,8};    bubbleSort(sqList, 10);    printArr(sqList, 10);    return 0;}

2.快速排序

快速排序就是每次选择一个元素,然后把比他大的放前面,比他小的放后面,他放中间。递归(或循环)直到子数组只有一个元素。

#include<stdlib.h>void printArr(int *sqList, int length){    int i;    for(i = 0; i < length; i++)    {        printf("%d ", sqList[i]);    }}int partition(int *sqList, int low, int high){    int temp = sqList[low];    while(low < high)    {        //第一个判断是边界的检验        while(low < high && sqList[high] >= temp)  high--;        sqList[low] = sqList[high];        while(low < high && sqList[low] <= temp)    low++;        sqList[high] = sqList[low];    }    sqList[low] = temp;    return low;}void quickSort(int *sqList, int low, int high){    if(low < high)    {        int mid = partition(sqList, low, high);        quickSort(sqList, low, mid -1);        quickSort(sqList, mid + 1, high);    }}int main(){    int sqList[]= {1,3,6,7,2,4,4,0,9,8};    quickSort(sqList, 0, 9);    printArr(sqList, 10);    return 0;}


四.归并排序

归并排序是分治法应用的很好例子。所谓分治法,就是把原问题分为规模较小,问题一样的子问题,不断分割知道子问题很容易就解出,记录子问题的结果,然后合并为原问题的解。下面是代码,很容易就可以看懂其中的过程。

#include<stdio.h>void print(int *sqList, int length){    int i;    for(i = 0; i < length; i++)    {        printf("%d ", sqList[i]);    }}void merge(int *sqList, int first, int mid, int last, int *temp){    int m = first;    int i = first;    int j = mid + 1;    while(i <= mid && j <= last)    {        if(sqList[i] < sqList[j])        {            temp[m] = sqList[i];            i++;            m++;        }        else        {            temp[m] = sqList[j];            j++;            m++;        }    }    if(i <= mid)    {        for(i; i <= mid; i++)        {            temp[m] = sqList[i];            m++;        }    }    if(j <= last);    {        for(j; j <= last; j++)        {            temp[m] = sqList[j];            m++;        }    }    for(i = first; i <= last; i++)        sqList[i] = temp[i];}void mergingSort(int *sqList, int begin, int end, int *temp){    if(begin == end)        temp[begin] = sqList[begin];    else    {        int i = (begin + end) / 2;        mergingSort(sqList, begin, i, temp);        mergingSort(sqList, i + 1, end, temp);        merge(sqList, begin, i, end, temp);    }}int main(){    int sqList[] = {65, 70, 78, 18, 19, 66, 97};    int temp[7] = {};    mergingSort(sqList, 0, 6, temp);    print(sqList, 7);}

五.基数排序

基数排序是基于多关键字的排序,k0,k1,k2,k3....kn,(一个关键字可以拆分为几个关键字,比如对数字排序,个十百可以分别作为关键字进行基数排序)。

基数排序可以分为两种:

1.从主关键字开始,即MSD基数排序

这种方法根据关键字的主次顺序依次把数组分为子数组,等到拆分到只剩最次关键字时,排序即可。

2.从最次关键字开始,即LSD基数排序。但对k i(0 <= i <= n - 1)排序时,只能用稳定的排序算法。

这种方法是从最次关键字开始,进行若干次分配,收集。下面以例子进行说明。

#include<stdio.h>/***这里用数组模拟链表*当然也可以使用真实的链表*//***对3位整数进行排序,则有三个关键字:个十百。*每个关键字的基数都是0-9,所以有3次分配收集的过程。*每一次分配,需要有一个包含10个元素的数组,用来记录每个基数对应的链表(逻辑上的)的头元素的开始下标*///求出数字的个十或百位的数字作为关键字//location取值为1,2,3.分别对应个十百int getKey(int data, int location){    int key;    int i;    for(i = 0; i < location; i++)    {        key = data % 10;        data = data / 10;    }    return key;}typedef struct{    //因为data里包含了关键字数组,所以不用关键字数组了。    int data;    int next;}SLCell;//分配的算法void distribute(SLCell *sqList, int length, int *baseBegin, int *baseEnd, int keyLocation){    int key;//key其实也是这个数在base数组中的下标    int i;    for(i = 0; i < length; i++)    {        key = getKey(sqList[i].data, keyLocation);        if(baseBegin[key] == -1)        {            baseBegin[key] = i;            baseEnd[key] = i;        }        else        {            sqList[baseEnd[key]].next = i;            baseEnd[key] = i;        }    }}//收集的算法void collect(SLCell *sqList, int length, int *baseBegin){    int index = 0;    SLCell sqCell;    SLCell sqListTemp[1000];//c不能声明为length长度    int i;    for(i = 0; i < 10; i++)    {        if(baseBegin[i] != -1)        {            int flag = 1;            sqCell = sqList[baseBegin[i]];            baseBegin[i] = -1;//为下一次重新分配做好准备(还原)            while(flag == 1)            {                sqListTemp[index].data = sqCell.data;                sqListTemp[index].next = -1;baseBegin[i]                index++;                if(sqCell.next == -1)                    flag = 0;                else                    sqCell = sqList[sqCell.next];            }        }    }    for(i = 0; i < length; i++)    {        sqList[i].data = sqListTemp[i].data;        sqList[i].next = -1;    }}int main(){    /////////////////////////////////////    //构造一个待排序的链表    SLCell sqList[11] = {};    int datas[] = {789, 123, 780, 123, 345, 321, 124, 98, 567 , 190,111};    int i;    for(i = 0; i < 11; i++)    {        sqList[i].data = datas[i];        sqList[i].next = -1;    }    int baseBegin[10] = {-1,-1,-1,-1,-1,-1,-1,-1,-1,-1};    int baseEnd[10] = {-1,-1,-1,-1,-1,-1,-1,-1,-1,-1};    for(i = 1; i <= 3; i++)    {        distribute(sqList, 11, baseBegin, baseEnd, i);        collect(sqList, 11, baseBegin);    }    for(i = 0; i < 11; i++)    {        printf("%d ", sqList[i]);    }}
































































0 0