zoukankan      html  css  js  c++  java
  • ❤️排序❤️

    插入排序

    插入排序

    每次将一个待排序的记录按其关键字大小'插入'到前面'已排好序的子序列中',类似于扑克牌的插入
    例如 n=6,数组R的六个排序码分别为:17,3,25,14,20,9。它的直接插入排序的执行过程
    


    void InsertSort(ElemType A[],int n){
        int i,j;
        for(i=2; i<=n; i++){
            if(A[i] < A[i-1]){
                A[0] = A[i];    //复制给哨兵,A[0]不存元素
                for(j=i-1; A[0]<A[i]; --j){
                    A[j+1] = A[j];      //向后移动
                }
                A[j+1] = A[0];      //复制到插入位置
            }
        }
    }
    

    希尔排序

    先将整个待排序的记录序列分割成为若干子序列分别进行'直接插入排序',待整个序列中的记录“基本有序”时,
    再对全体记录进行依次直接插入排序。假设增量为5,则i,i+5....;下图增量为5
    

    void ShellSort(ElemType A[],int n){
        //A[0]用来暂存单元
        for(i=dk+1; dk>=1; dk=dk/2){    //步长变化
            for(i=id+1; i<=n; i++){
                if(A[i] < A[i-dk]){     //需将A[i]插入有序增量表
                    A[0] = A[i];        //暂存在A[0]
                    for(j=i-dk; j>0&&A[0]<A[j]; j-=dk){
                        //记录后移,查找插入的位置
                        A[j+dk] = A[j];     
                    }
                    A[j+dk] = A[0]      //插入
                }
            }
        }
    }
    

    交换排序

    冒泡排序

    对待排序序列从后向前(从下标较大的元素开始),'依次比较相邻元素'的排序码,若发现'逆序则交换',
    使排序码较小的元素逐渐从后部移向前部,就象水底下的气泡一样逐渐向上冒。
    例如,n=6,数组R的六个排序码分别为:17,3,25,14,20,9。下面图给出冒泡排序算法的执行过程。
    

    void BubbleSort(ElemType A[],int n){
        for(i=0; i<n-1; i++){
            flag = false;   //表示本趟冒泡是否发生交换
            for(j=n-1; j<i; j--){   //一趟冒泡过程
                if(A[j-1] > A[j]){
                    swap(A[j-1],A[i]);  //交换
                    flag = true;
                }
                if(flag == false){
                     //表示遍历后没有发生交换,说明表已有序
                    return;    
                }
            }
        }
    }
    

    快速排序

    基本思想是:取待排序序列中的某个元素(一般第一个元素)作为基准,通过一趟排序,将待排元素分为'左右两个子序列',
    '左子序列元素'的排序码均'小于或等于基准元素'的排序码,
    '右子序列'的排序码则'大于基准元素'的排序码,
    然后分别对两个子序列继续进行快速排序,直至整个序列有序。
    元素的比较和交换是从两端向中间进行的,排序码较大的元素一次就能够交换到后面,排序码较小的记录一次就能够交换到前面,
    记录每次移动的距离较远,因而总的比较和移动次数较少。
    例如,给定排序码为:'(46,55,13,42,94,05,17,70)'
    

    一次划分

    void QuickSort(ElemType A[],int low,int high){
        if(low < high){
            /*
            Partition()就是划分操作,将A[low....high]
            划分为满族上述条件的两个子表
            */
           int pivotpos = Partition(A,low,high);    //划分
           QuickSort(A,low,pivotpos-1);    //依次对两个子表进行递归排序
           QuickSort(A,pivotpos+1,high);    
        }
    }
    
    int Partition(ElemType A[],int low,int high){
        //一次划分
        ElemType pivot = A[low];    //将当前表中第一个元素设为轴,对表进行划分
        while(low < high){
            while(low<high && A[low]>=pivot){
                --high;
            }
            A[low] = A[high];   //将比轴小的元素移动到左端
            while(low<high && A[low]<=pivot){
                ++low;
            }
            A[high] = A[low];   //将比轴大的元素移动到右端
        }
        A[low] = pivot; //将轴元素放到最终位置
        return low;     //返回存放轴饿最终位置
    }
    

    选择排序

    简单选择排序

    基本思想是:第一次从'array[0]~array[n-1]'中'选取最小值',与'array[0]'交换,
    第二次从'array[1]~array[n-1]'中选取最小值,与'array[1]'交换,
    第三次从'array[2]~array[n-1]'中选取最小值,与'array[2]'交换,…
    例如,给定n=8,数组R中的8个元素的排序码为:'(8,3,2,1,7,4,6,5)'
    

    void SelectSort(ElemType A[],int n){
        for(i=0; i<n-1; i++){
            //一共进行n-1趟
            min = i;    //记录最小元素位置
            for(j=i+1; j<n; j++){   //找最小元素
                if(A[j] < A[min]){
                    min = j;    //更新最小元素的位置
                }
                if(min != j){
                    swap(A[i],A[min]);  //交换
                }
            }
        }
    }
    

    堆排序

    将排序码k1,k2,k3,…,kn表示成'一棵完全二叉树',然后从第n/ 个排序码(即树的最后一个非叶结点)开始筛选,
    使由该结点作根结点组成的子二叉树符合堆的定义,然后从第n/2 -1个排序码重复刚才操作,直到第一个排序码止。
    这时候,该二叉树符合堆的定义,初始堆已经建立。 
    可以按如下方法进行堆排序:将堆中第一个结点(二叉树根结点)和最后一个结点的数据进行交换(k1与kn),
    再将k1~kn-1重新建堆,然后k1和kn-1交换,再将k1~kn-2重新建堆,然后k1和kn-2交换,如此重复下去,
    每次重新建堆的元素个数不断减1,直到重新建堆的元素个数仅剩一个为止。
    

    void BuildMaxHeap(ElemType A[],int len){
        for(i=len/2; i>0; i--){     //从i=[n/2]~1,反复调整堆
            HeadAdjust(A,i,len);    
        }
    }
    
    void HeadAdjust(ElemType A[],int k,int len){
        A[0] = A[k];    //A[0]暂存子树的根节点
        for(i=2*k; i<=len; i*=2){   //沿key较大的子节点向下筛选
            if(i<len && A[i]<A[i+1]){
                i++;    //去key较大的子节点的下标
            }
            if(A[0] >= A[i])    break;  //筛选结束
            else{
                A[k] = A[i];    //将A[i]调整到双亲结点
                k = i;  //修改k值,以便继续向下筛选
            }
        }
        A[k] = A[0];    //被筛选节点的值的最终位置
    }
    

    归并排序

    将两个(或两个以上)有序表合并成一个'新的有序表',即把待排序序列'分为若干个子序列',每个子序列是'有序的'。然后再把有序子序列合并为整体有序序列。
    

    //辅助数组B
    ElemType *B=(ElemType*)malloc((n+1)*sizeof(ElemType));
    
    void Merge(ElemType A[],int low,int mid,int high){
        //表A的两段A[low...mid]和A[mid+1...high]各自有序,将他们合并成一个有序表
        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]){   //比较B的左右两段中的元素
                A[k] = B[i++];  //将较小值复制到A中
            }else{
                A[k] = B[j++];
            }   
        }
        while (i<=mid) A[k++] =B [i++]; //若第一个表未检测完,进行复制
        while (i<=mid) A[k++] =B [j++]; //若第二个表未检测完,进行复制   
    }
    
    void MergeSort(ElemType 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);  //归并        
        }
    }
    

    基数排序

    数排序(radix sort)属于“分配式排序”(distribution sort),又称“桶子法”(bucket sort)或bin sort,顾名思义,
    它是透过键值的部份资讯,将要排序的元素分配至某些“桶”中,藉以达到排序的作用,
    基数排序法是属于稳定性的排序,其时间复杂度为O (nlog(r)m),其中r为所采取的基数,而m为堆数,在某些时候,基数排序法的效率高于其它的稳定性排序法。
    


    各个排序比较

    '不稳定':'快选堆希'(快速排序、选择排序、堆排序、希尔排序)
    '稳定':'插冒归计基'(简单插入排序、冒泡排序、归并排序、计数排序、基数排序)
    移动次数和关键字顺序无关的排序:
    '一堆(堆排序)海龟(归并排序)选(选择排序)基(基数排序)友'--->'堆排序、归并排序、选择排序、基数排序'
    

    败者树balabala.. 不想写了 写吐了

    部分图片来自王道数据结构书中

  • 相关阅读:
    Javascript Promise技术
    什么是CPS
    移动端input file 提示没有应用可执行此操作
    黄聪:wordpress+Windows下安装Memcached服务及安装PHP的Memcached扩展
    黄聪:Windows下安装Memcached服务及安装PHP的Memcached扩展
    用VScode配置Python开发环境
    在VSCode中使用码云(Gitee)进行代码管理
    网页链接分享到微信朋友圈带图标和摘要的完美解决方法
    解决百度统计被刷广告的办法,屏蔽非法广告
    Visual Studio代码PHP Intelephense继续显示不必要的错误
  • 原文地址:https://www.cnblogs.com/xiaofff/p/13288748.html
Copyright © 2011-2022 走看看