排序算法合集

排序算法

一:直接插入排序

(一):算法思想

每一趟将一个待排序的关键字按照其值的大小插入到已经排序好的部分的适当位置,直到所有的待排关键字都被插入到有序序列中为止

(二):代码

void InsertSort(int A[],int numsize){
    if (numsize>1){
        int i,j;
        int temp;
        for(i = 1;i<numsize;i++){
            temp = A[i];
            j = i-1;
            while (j>=0&&temp<A[j]){
                A[j+1] = A[j];
                j--;
            }
            A[j+1] = temp;
        }
    }
}

(三):性能分析

时间复杂度:
最坏情况:整个序列都是逆序的,则内存循环while条件始终成立,则此时的时间复杂度为O(n^2);
最好情况:即整个序列基本有序,那么对于内层循环while的条件始终不成立,则此时的时间复杂度为O(n);
因此平均情况下插入排序的时间复杂度为O(n^2);
空间复杂度:
因为使用单位个辅助空间,所以空间复杂度为:O(1);
稳定算法,适用于顺序存储和链式存储

二:折半插入排序算法

(一):算法思想:

与直接插入排序类似,但是在查找合适的插入位置的时候使用的是二分法来进行查找,在确定好插入位置之后在统一进行移动元素,最后插入

(二):代码

//折半插入排序
void BinaryInsertSort(int A[],int numsize){
    if (numsize>1){
        int i,j,mid,heigh,low,temp;
        for (i = 1; i < numsize; i++) {
            temp = A[i];
            heigh = i-1;
            low = 0;
            //二分法查找插入位置
            while (low<=heigh){
                mid = (low+heigh)/2;
                if (A[mid]>temp){
                    heigh = mid-1;
                } else{
                    low = mid+1;
                }
            }
            //统一移动数据
            for (j = i-1; j >= heigh; j--) {
                A[j+1] = A[j];
            }
            //将插入数据
            A[j+1] = temp;
        }
    }
}

(三):性能分析

时间复杂度:
折半插入排序和直接插入排序移动关键字的次数是一样的,所以这部分的时间复杂度是与直接插入排序一样的。折半插入排序对于确定插入位置的操作与序列的初始状态无关,即插入排序的比较次数与初始状态无关仅与关键字的个数相关。
最好情况下的时间复杂度是:O(nlog2n);
最坏情况下:O(n^2);
平均情况下:O(n^2);
空间复杂度:
因为使用常数个辅助空间,所以空间复杂度为:O(1);
稳定算法,仅适用于顺序存储(因为折半查找算法只适用于有序的顺序表)

三:希尔排序

(一):算法思想

先将待排序的序列分割成若干形如L[i,i+d,i+2d,…,i+kd]的特殊子表,对于每一个子表分别进行直接插入排序,缩小增量d,并重复上述过程,直到d=1为止,此时表中元素基本有序,在对全体记录进行一次直接插入排序。

(二):代码

//希尔排序
void ShellSort(int A[],int numsize){
    int i,j,temp;
    for (int d = numsize/2;  d>=1 ;d=d/2) {
        for (i = d ; i < numsize; i++) {
            if (A[i] < A[i-d]){
                temp = A[i];
                for (j =i-d;  j>0 ; j=j-d) {
                    if (temp<A[j]){
                        A[j+d] = A[j];
                    }
                }
                A[j+d] = temp;
            }
        }
    }
}

(三):性能分析

时间复杂度:O(n^2)
空间复杂度:O(1)
不稳定算法
仅使用于顺序存储

四:冒泡排序

(一):算法思想

从前往后(从后往前)两两比较相邻的元素的值,若为逆序,则交换它们,直到序列比较完,将最小(最大的)元素交换到待排序序列的第一个位置(最后一个位置)。进行下一趟冒泡排序时,前一趟确定的最小元素不再参与比较,每趟冒泡的结果是把序列的最小元素(或最大)放到了序列的最终位置,如果某一天趟的排序过程中没发生交换,则算法可以提前结束

(二):代码

//冒泡排序
void BubbleSort(int A[],int numsize){
    int i,j,flag,temp;
    for (i=0;i<numsize;i++){
        flag = 0;
        for (j=1;j<numsize-1;j++){
            if (A[j-1]>A[j]){
                temp = A[j-1];
                A[j-1] = A[j];
                A[j] = temp;
                flag = 1;
            }
        }
        if (flag == 0){
            return;
        }
    }
}

(三):性能分析

时间复杂度:
最坏情况:待排序为逆序,此时对于外层循环的每次执行,内层循环的if语句条件始终成立,则时间复杂度为O(n^2);
最好情况:待排序的有序,此时内层循环中的if语句条件始终不成立,交换不发生,且内层循环执行n-1次后整个算法结束,因此时间复杂度为O(n);
因此平均情况下时间复杂度为O(n^2);
空间复杂度:
常数个辅助空间:O(1);
稳定算法,适用于顺序存储

五:快速排序

(一):算法思想

首先选取一个元素作为枢纽,然后以此枢纽为界将待排序列分为两个部分,左面小于枢纽,右面大于枢纽,然后在这两个部分分别递归的进行上述步骤。

(二):代码

//快速排序
void QuickSort(int A[],int low,int high){
    int temp;
    int i = low,j = high;
    if (low<high){
        temp = A[low];
        //下面这个循环完成一趟,就可以将数组中小于temp的数放在左边,大于temp的关键字放在右边
        while (i<j){
            while (j>i&&A[j]>=temp){    //从右边往左边扫面,找到一个小于temp的关键字
                j--;
            }
            if (i<j){
                A[i] = A[j];            //放在temp的左边
                i++;                    //i右移一位
            }
            while (i<j&&A[i]<temp){     //从左往右扫描,找到一个大于temp的关键字
                i++;
            }
            if (i<j){
                A[j]=A[i];              //放在temp的右边
                j--;                    //j左移一位
            }
        }
        A[i] = temp;                    //将temp放在最终位置
        QuickSort(A,low,i-1);       //递归对于temp左边的关键字进行排序
        QuickSort(A,i+1,high);      //递归对于temp右边的关键字进行排序
    }
}

(三):性能分析

时间复杂度:
快速排序的运行时间与划分的是否对称有关,快速排序的最坏情况发生在两个区域分别含有n-1和0个元素的时候,这种最大限度的不对称性若发生在每一层的递归上,及对应于初始排序表基本有序或基本逆序时,就得到了最坏情况下的时间复杂度为O(n^2);
快速排序的平均时间复杂度为O(nlog2n);
空间复杂度
由于快速排序是递归的,需要借助一个递归工作栈来保存每一层递归调用的必要信息,其容量与递归调用的最大深度一致
最好形况下:O(log2n)
最坏情况下:要进行n-1次递归调用,所以栈的深度是O(n)
平均情况下:栈的深度为O(log2n);
不稳定算法
适用于顺序存储

六:简单选择排序

(一):算法思想

将表分成两个部分,有序部分和无序部分,每次从徐徐部分选取一个最小的元素,然后放入有序部分中。

(二):代码

//简单选择排序
void SelectSort(int A[],int numsize){
    int i,j,k;
    int temp;
    for (i = 0;i<numsize;i++){
        k = i;
        for (j = i+1;j<numsize;j++) {
            if (A[k]>A[j]){
                k = i;
            }
        }
        temp = A[i];
        A[i] = A[k];
        A[k] = temp;
    }
}

(三):性能分析

时间复杂度:
通过代码可以看出,两层循环的执行次数与初始序列是没有关系的,所以时间复杂度为:O(n^2);
空间复杂度:
使用常数个辅助空间,所以空间复杂度为O(1);
不稳定的排序算法
适用于顺序存储

七:堆排序

(一):算法思想

建堆:按照大根堆或者小根堆的规则建立起相应的二叉树,那么根节点一定是最大值或者最小值
调整堆:当根结点输出后,整颗二叉树可能会被破坏,这是要根据相应的建堆规则,自底向上,自左向右,进行父结点与子节点交换以满足相应的建堆规则。

(二):代码

//调整堆
void shift(int A[],int low,int high){
    int i = low,j = i*2;
    int temp = A[i];
    while (i<=high){
        if (j<high&&A[j]<A[j+1]){
            j++;
        }
        if (temp<A[j]){
            A[i] = A[j];
            i = j;
            j = i*2;
        } else{
            break;
        }
    }
    A[i] = temp;
}

//堆排序函数
void heapSort(int A[],int numsize){
    int i;
    int temp;
    for (i=numsize/2;i>=1;i--) {        //建立堆
        shift(A,i,numsize);        
    }
    for (i = numsize;i>=2;i--) {
        //以下三局代码将根节点的关键字换出,将其放入到最终的位置上
        temp = A[1];
        A[1] = A[i];
        A[i] = temp;
        shift(A,1,i-1);
    }
}

(三):性能分析

时间复杂度:
建堆时间为O(n),之后有n-1次向下调整,每次调整的时间复杂度为O(h),因此最好,最坏和平均情况下,堆排序的时间复杂度均为O(nlog2n)
空间复杂度:
使用常数个辅助单位,所以空间复杂度为O(1)
不稳定的排序算法

八:归并排序算法

(一):算法思想

归并的含义是将两个或者两个以上的有序子表组合成一个新的有序表。假定待排序表中有n个记录,则可以将其视为n个有序的子表,每个子表的长度为1,然后两两归并,得到n/2个长度为2或者为1的有序表,继续两两归并。。。如此重复直到合并成一个长度为n的有序表为止,这种排序方法称为2路归并排序。

(二):代码

//二路归并排序
#define numsize 20	//数组长度
int B[numsize];    //定义辅助数组
void Merge(int A[],int low,int mid,int high){
    int i,j,k;
    for (k = low; k <=high; ++k) {
        B[k] = A[k];        //将数组A中的元素复制到B中  
    }
    for (i = low,j=mid+1,k=i; i <=mid&&j<=high ; k++) {
        if (B[i]<=B[j]){
            A[k] = B[i++];
        } else{
            A[k] = B[j++];
        }
    }
    while (i<=mid){
        A[k++] = B[i++];        //若第一个表没有检测完,复制
    }
    while (j<=high){            //若第二个表未检测完,复制
        A[k++] = B[j++];
    }
}

void MergeSort(int A[],int low ,int high){
    if (low<high){
        int mid = (low+high)/2;
        MergeSort(A,low,mid);
        MergeSort(A,mid+1,high);
        Merge(A,low,mid,high);
    }
}

(三):性能分析

时间复杂度:
每趟归并的时间复杂度为O(n),共需要进行log2n趟归并,所以算法的时间复杂度为O(nlog2n)。
空间复杂度:
需要使用n个辅助空间,所以空间复杂度为O(n);
稳定的排序算法

九:基数排序

(一):算法思想

最高位优先MSD:按照关键字位权重递减依次逐层划分为若干更小的子序列,最后将所有的子序列依次连接成一个有序的序列
最低位优先LSD:按照关键字权重递增依次进行排序,最终形成一个有序序列

(二):代码

LSD算法

#define Radix 10            //0-9,共十个数据

typedef int ElemType;
//结点(桶结点)
typedef struct LinkNode{
    ElemType key;       //数据
    struct LinkNode* next;
}LinkNode,*LinkList;

//链式队列
typedef struct {
    LinkNode* front;
    LinkNode* rear;
}LinkQueue;

//十个队列
typedef LinkQueue Bucket[Radix];        //定义是个桶,每个桶是一个队列,队列中有front和rear分别指向其头和尾

/**
 * 获取数组中最大位数是多少位
 * @param Array
 * @param numsize
 * @return 返回最大位数
 */
int HowMany(ElemType * Array,int numsize){
    if (numsize == 0){
        return -1;
    }
   int index = 1;
    for (int i = 0; i < numsize; ++i) {
        int temp = 1;       //表示位数
        int stand = 10;
        while (Array[i]/stand > 0){
            temp++;         //表示位数
            stand*=10;      //基准位
        }
        if (index<temp){
            index = temp;
        }
    }
    return index;
}

/**
 * 获取位数上的值,如个、十、百位上的数据
 * @param data      关键字
 * @param d         第几位,默认为一,则表示的是个位,2位十位,3为百位
 * @return          返回指定位上的数据
 */
int GetDigit(int data,int D){
    int valu;
    for (int i = 1; i <= D; ++i) {
        valu = data%Radix;      //获取位数
        data/=Radix;            //将数据除去十
    }
    return valu;
}

/**
 * 低位优先基数排序
 * @param Array         待排序数组
 * @param numsize       数组中数据的个数
 */
void LSDRadixSort(ElemType Array[],int numsize){
    int D,Di,i;     //D表示的是位数,第1位,第2位。。,Di表示数据当前位数上的值,i用来循环
    Bucket B;       //桶
    LinkList tmp,p;
    LinkList List = NULL;

    //初始化每一个桶(每一个队列)
    for (i = 0; i < Radix; i++) {
        B[i].front = B[i].rear = NULL;
    }

    //将数组中的数据采取头插法逆序插入到链表List中
    for (i = 0;i < numsize; ++i) {
        tmp = (LinkList)malloc(sizeof (LinkNode));
        tmp->key = Array[i];
        tmp->next = List;
        List = tmp;
    }

    //获取数组中最大位数
    int MaxDigit = HowMany(Array,numsize);

    //开始进行基数排序,分为两步:分配和收集
    for (D = 1; D <= MaxDigit ; D++) {      //MaxDigit为数组中最大的数据的位数
        //下面是分配过程
        p = List;
        while (p){
            Di = GetDigit(p->key,D);        //获取D位上的数据Di
            //将p从List中摘掉
            tmp = p;
            p = p->next;
            //将结点从链表上摘掉之后将其插入到指定的桶中
            tmp->next = NULL;
            if (B[Di].front == NULL){       //表示当前该桶中没有数据
                B[Di].front = B[Di].rear = tmp;
            } else{                         //否则表示桶中有数据,那么就要将数据插入到rear后面
                B[Di].rear->next = tmp;
                B[Di].rear = tmp;
            }
        }

        //下面是收集的过程,将桶中的数据全部都收到链表List中
        //收集过程,从后往前收的原因是到最后List正好处于整个链表的头。
        List = NULL;
        for (Di = Radix-1; 0<=Di ; Di--) {
            if (B[Di].front){       //如果桶不为空,证明其中有数据,将整桶数据到如链表中
                B[Di].rear->next = List;
                List= B[Di].front;
                B[Di].front = B[Di].rear = NULL;        //到完数据之后需要清空桶
            }
        }
    }

    //在将链表中的数据倒入到数组Array中
    for (i = 0;i <numsize ; i++) {
        tmp = List;
        List = List->next;
        Array[i] = tmp->key;
        free(tmp);
    }
}

(三):性能分析

时间复杂度:
基数排序需要进行d趟分配和收集,一趟分配需要O(n),一趟收集需要O®,因此基数排序的时间复杂度为O(d(n+r)),与序列的初始状态无关。
空间复杂度
一趟排序需要的辅助空间为r(r个队列:r个队头指针和r和队尾指针),因此基数排序的空间复杂度为O®;

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值