排序算法合集

排序算法合集,第1张

排序算法合集 排序算法

文章目录
  • 排序算法
    • 一:直接插入排序
      • (一):算法思想
      • (二):代码
      • (三):性能分析
    • 二:折半插入排序算法
      • (一):算法思想:
      • (二):代码
      • (三):性能分析
    • 三:希尔排序
      • (一):算法思想
      • (二):代码
      • (三):性能分析
    • 四:冒泡排序
      • (一):算法思想
      • (二):代码
      • (三):性能分析
    • 五:快速排序
      • (一):算法思想
      • (二):代码
      • (三):性能分析
    • 六:简单选择排序
      • (一):算法思想
      • (二):代码
      • (三):性能分析
    • 七:堆排序
      • (一):算法思想
      • (二):代码
      • (三):性能分析
    • 八:归并排序算法
      • (一):算法思想
      • (二):代码
      • (三):性能分析
    • 九:基数排序
      • (一):算法思想
      • (二):代码
      • (三):性能分析

一:直接插入排序 (一):算法思想

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

(二):代码
void InsertSort(int A[],int numsize){
    if (numsize>1){
        int i,j;
        int temp;
        for(i = 1;i=0&&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 
(三):性能分析 

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

四:冒泡排序 (一):算法思想

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

(二):代码
//冒泡排序
void BubbleSort(int A[],int numsize){
    int i,j,flag,temp;
    for (i=0;iA[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 (lowi&&A[j]>=temp){    //从右边往左边扫面,找到一个小于temp的关键字
                j--;
            }
            if (i 
(三):性能分析 

时间复杂度:
快速排序的运行时间与划分的是否对称有关,快速排序的最坏情况发生在两个区域分别含有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;iA[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=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 
(三):性能分析 

时间复杂度:
每趟归并的时间复杂度为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分别指向其头和尾


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 (indexkey = 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 next;
        Array[i] = tmp->key;
        free(tmp);
    }
}
(三):性能分析

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

欢迎分享,转载请注明来源:内存溢出

原文地址: http://outofmemory.cn/zaji/5697334.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-12-17
下一篇 2022-12-17

发表评论

登录后才能评论

评论列表(0条)

保存