• 数据结构之常用排序---C++实现


    简要介绍一些概念:

    (1)排序就是整理文件中的记录,使之按关键字递增(或递减)次序排列起来。

    (2)排序按策略可以划分为五类,即插入排序、交换排序、选择排序、归并排序、分配排序。

    (3)插入排序每次讲一个待排序的记录,按其关键字大小插入到前面已排好序子文件的适当位置,知道全部待排序记录插入完成为止。常用的插入排序方法有直接插入排序、折半插入排序。

    (4)交换排序是将待排序记录的关键字两两比较,发现两个记录的次序相反即金星交换,直到没有反序的记录为止。交换排序的主要方法有冒泡排序和快速排序。

    (5)归并排序是指将若干已排序的子文件合并成一个有序的文件,归并排序有自底向上和自顶向下两种实现方法。

    (6)分配排序过程无需比较关键字,是通过“分配”和“收集”实现排序。常见的分配排序有箱排序和基数排序。

    实现的代码如下:

    #include "SqList.h"
    #define KEYNUM 3
    #define RADIX 10
    template<typename ElemType>
    class SqListSort :public SqList<ElemType>
    {
    public:
        //直接插入排序
        void insertSort();
        //折半插入排序
        void binaryInsertSort();
        //冒泡排序
        int bubbleSort();
        //快速排序
        void quickSort();
        //直接选择排序
        void selectSort();
        //堆排序
        void heapSort();
        ////归并排序
        void mergeSort();
        //希尔排序
        void shellSort();
        //基数排序
        void radixSort();
    private:
        //快速排序辅助函数
        void quickSort_aux(int low, int high);
        //一趟快速排序
        void quickSortPartition_aux(int low, int high);
        ////堆调整
        void heapSortAdjust_aux(int low, int high);
        //归并排序辅助函数
        void mergeSort_aux(int low, int high);
        //将前后两个有序序列归并为一个有序序列
        void mergeSortOne_aux(int low, int mid, int high);
        //希尔排序辅助函数
        void shellSort_aux(int dk);
        ////基数排序的收集函数(静态链表存储)
        void radixSortCollect_aux(int front[], int end[], int time);
        //基数排序的分配函数
        void radixSortDistribute_aux(int i, int front[], int end[]);
    protected:
        int *index;//存储数据元素原始的位置
    };
    
    //////////////////////////////////////////////////直接插入排序//////////////////////////////////////////
    template<typename ElemType>
    void SqListSort<ElemType>::insertSort()
    {
        ElemType key;//预存放当前待排序的元素
        int temp_index;
        //记录每个元素对应的原始位置
        for (int  i = 0; i < n; i++)
        {
            index[i] = i;
        }
        //从第二个结点开始直到结束,逐个做直接插入排序
        for (int  i = 1; i < n; i++)
        {
            key = elem[i];
            //记录当前待排序元素对应的原始位置
            temp_index = index[i];
            //从最后一个元素开始所有大于key的元素后移一位
            for (int j = i-1; j >=0&&key<elem[j]; --j)
            {
                elem[j + 1] = elem[j];
                index[j + 1] = index[j];//对应记录原始位置的数组也更新
            }
            elem[j + 1] = key;
            index[j + 1] = temp_index;
        }
    }
    
    //////////////////////////////////////////////////折半插入排序//////////////////////////////////////////
    //步骤如下:
    //1记录当前待排序元素
    //2记录顺序表有序查找区域下界和上界
    //3在顺序表有序查找区域中折半查找待排序元素的位置
    //4把顺序表有序查找区域与某些元素后移一个位置,以空出位置给当前待排序元素
    //5在空出的位置上填写当前待排序的元素
    template<typename ElemType>
    void SqListSort<ElemType>::binaryInsertSort()
    {
        int low;//存放有序区的下界
        int mid;//存放有序区中间位置
        int high;//存放有序区的上界
        ElemType key;//预存放当前待排序元素
       //从顺序表的第二个元素开始,直到结束,逐个做折半插入排序
        for (int i = 1; i < n; i++)
        {
            key = elem[i];
            low = 0;
            high = i - 1;
            while (low<=high)
            {
                mid = (low + high) / 2;
                if (key<elem[mid])
                {
                    high = mid - 1;
                }
                else 
                {
                    low = mid + 1;
                }
            }
        //把第high+1个及后边的所有元素后移一个位置,
            for (int j = i-1; j >= high+1; --j)
            {
                elem[j + 1] = elem[j];
            }
            elem[high+1] = key;//在high+1的下标位置填写当前待排序元素
        }
    }
    /////////////////////////////////////////////冒泡排序///////////////////////////////////////////////////
    //
    //步骤:从上往下(从前往后)依次比较两相邻气泡的质量。若发现轻者在下、重者在上,则交换二者的位置。经过一趟比较
    //“最重”的沉到了最后一个单元elem[n-1]。第二趟,依旧从上往下两两比较,直到elem[2],使得次重的气泡沉到elem[2]
    //依次类推。直到n-1次冒泡或者不再有元素交换为止。
    template<typename ElemType>
    void SqListSort<ElemType>::bubbleSort()
    {
        bool flag = true;//标价某趟冒泡是否有元素交换
        ElemType t;//临时变量
        //从上向下依次比较两相邻气泡的重量。若发现轻者在下,重者在上,则交换二者的位置。
        //直到n-1次冒泡或者不再有元素交换为止。
        for (int i = 0; i < n&&flag; ++i)
        {
            flag = false;
            for (int j = 0; j <n-i ; ++j)
            {
                if (elem[j + 1] < elem[j])
                {
                    flag = true;
                    t = elem[j + 1];
                    elem[j + 1] = elem[j];
                    elem[j] = t;
                }
    
            }
        }
    }
    
    /////////////////////////////////////////////快速排序///////////////////////////////////////////////////
    //快速排序
    template<typename ElemType>
    void SqListSort<ElemType>::quickSort()
    {
        quickSort_aux(0, n - 1);
    }
    //快速排序辅助函数
    //输入待排序区的下界和上界
    //步骤:
    //(1)以elem[low]为基准,将elem[low....high]划分为符合下面条件的左右两个区间,elem[low..keyLoc-1]<=elem[keyLoc]
    //<=elem[keyLoc+1...high],原来的elem[low]放到elem[keyLoc]上
    //(2)将左区间elem[low....keyLoc-1]自递归完成左右区间的排序
    //(3)将右区间elem[keyLoc+1....high]自递归完成左右区间的排序
    template<typename ElemType>
    void SqListSort<ElemType>::quickSort_aux(int low,int high)
    {
        int keyLoc;//预存放elem[low]排序后的位置下标
        if (low<high)
        {//以elem[low]为基准将elem[low...high]划分为如下两个区间
            //elem[low..keyLoc-1]<=elem[keyLoc]<=elem[keyLoc+1...high]
            //原来的elem[low]放到elem[keyLoc]上
            keyLoc = quickSortPartition_aux(low, high);
            //将左区间自递归完成左区间的排序
            quickSort_aux(low, keyLoc - 1);
            //将右区间自递归完成右区间的排序
            quickSort_aux(keyLoc + 1, high);
        }
    }
    
    //一趟快速排序
    template<typename ElemType>
    void SqListSort<ElemType>::quickSortPartition_aux(int low,int high)
    {
        int i = low;//左指针初始化为下界
        int j = high;//右指针初始化为下界
        ElemType t;//临时变量
        t = elem[i];//暂存当前记录
        while (i<j)
        {//将右指针j从右向左扫描,直到找到第1个小于基准记录的elem[j]
            while (i<j&&elem[j]>=t)
            {
                --j;
            }
            elem[i] = elem[j];
        //将左指针i从左向右扫描,直到找到第1个大于基准记录的elem[i]
            while (i<j&&elem[i]<t)
            {
                ++i;
            }
            elem[j] = elem[i];
        }
        //此时i即为基准最终的位置,将基准位置放在此位置就完成了一次划分
        elem[i] = t;
        return i;
    }
    
    //////////////////////////////////////////////////直接选择排序//////////////////////////////////////////
    //步骤:
    //初始时顺序表未排序元素为整个区域,执行以下操作,每次从未排序的元素中选择一个最小的,插入到以排序元素的后面,
    //重复执行,直到未排序的元素只剩下一个元素位置
    //(1)在顺序表elem[i-1....n-1]未排序区域选择一个最小的元素,记录其下标
    //(2)最小元素与elem[i-1]记录交换
    template<typename ElemType>
    void SqListSort<ElemType>::selectSort()
    {
        int min;//预存放当前最小元素的下标
        ElemType t;//临时变量
        for (int i = 1; i < n; ++i)
        {
            min = i - 1;
            for (int j = i; j < n; ++j)
            {
                if (elem[j] < elem[min])
                    min = j;//记录最小元素的下标
            }
            if (min!=i-1)
            {
                t = elem[min];
                elem[min] = elem[i - 1];
                elem[i - 1] = t;
            }
        }
    }
    
    //////////////////////////////////////////////////堆排序//////////////////////////////////////////
    //大/小根堆是完全二叉树
    //步骤:
    //(1)把elem[0...n-1]建成初始大根堆
    //(2)将当前无序区的堆顶记录elem[0]和当前未经排序的最后一个记录交换,然后将新的无序区调整为堆(即重建堆),
    //依次操作,直到无序区只有一个元素为止。
    template<typename ElemType>
    void SqListSort<ElemType>::heapSort()
    {
        int i, j = 1;
        ElemType t;
        //把elem[0....n-1]建成初始大根堆
        for (int i = n/2-1;  i>-1; --i)
        {
            heapSortAdjust_aux(i, n - 1);
        }
        //将当前无序区的堆顶记录elem[0]和当前未经排序的最后一个记录交换
        //然后将新的无序区调整为堆(即重建堆),一次操作,知道无序区只有一个元素为止
        for ( i = n-1; i >=1; --i)
        { //将堆顶记录和当前未排序的最后一个记录交换
            t = elem[i];
            elem[i] = elem[0];
            elem[0] = t;
            //把elem[0....i-1]重建大根堆
            heapSortAdjust_aux(0, i - 1);
        }
    }
    
    //堆调整
    //步骤:只有堆顶结点有可能违反堆定义,所以把堆顶结点设为当前结点,从当前结点开始,把较小的关键字逐层筛选
    //      下去,而将较大的关键字逐层选上来,直到当前被调整的结点已满足堆性质,或者该结点已是叶子为止,具体如下:
    //(1)如果有左右子堆,选择数值较大的子堆根结点elem[max]
    //(2)如果当前结点的数据值大于或等于数据值较大的子堆根结点,则该区间已调整为堆。否则,将数据值较大的子堆根结点
    //  elem[max]上移,即与当前结点的位置“交换”
    //(3)交换后又可能使结点elem[max]违反堆性质,修正等待堆调整区间的下标low,重复上述调整
    template<typename ElemType>
    void SqListSort<ElemType>::heapSortAdjust_aux(int low, int high)
    {
        ElemType t = elem[low];//暂存堆顶结点,只有此结点可能违反堆堆定义
        //当前结点从堆顶结点开始,把较小的关键字筛选下去,而将较大的关键字逐层筛选上来
        for (int max = 2*low+1; max <= high; )
        {  //如果有左右子堆,选择数值较大的子堆根结点
            if (max + 1 <= high&&elem[max] < elem[max + 1])
                ++max;
            //如果当前结点的数据值大于或等于数据值较大的子堆根结点
            //说明该区间已经调整为堆,跳出循环
            if (t>=elem[max])
            {
                break;
            }
            //将数据值较大的子堆根结点上移,即与当前结点的位置“互换”
            elem[low] = elem[max];
            //交换后又可能使结点elem[max]违反堆性质
            //修正等待调整区间的 下标low,重复上述调整过程
            low = max;
            max = 2 * low + 1;
        }
        elem[low] = t;//插入元素
    }
    /////////////////////////////////////////////////归并排序/////////////////////////////////////////////
    template<typename ElemType>
    void SqListSort<ElemType>::mergeSort()
    {
        mergeSort_aux(0, n - 1);
    }
    
    //归并排序辅助函数
    //步骤:如果待排序的区间不只一个元素,则将此区域分成前后两半,执行以下操作:
    //     (1)将前半部分自递归归并为有序序列
     //    (2)将后半部分自递归为有序序列
    //     (3)将已经有序的前半部和后半部合并为有序序列
    template<typename ElemType>
    void SqListSort<ElemType>::mergeSort_aux(int low, int high)
    {
        int mid;
        if (low!=high)
        {   //将顺序表元素一分为二
            mid = (low + high) / 2;
            //将elem[low....mid]归并为有序序列
            mergeSort_aux(low, mid);
            //将elem[mid...high]归并为有序序列
            mergeSort_aux(mid, high);
            //将elem[low....mid]和elem[mid...high]归并为有序序列
            mergeSortOne_aux(low, mid, high);
        }
        
    }
    
    //将前后两个有序序列归并为一个有序序列
    //步骤
    //(1)从前后有序区的第一个元素开始,逐一比较,每次把数据值较小的一方赋值到暂存空间tempt中,直到其中的一个有序区
    //   结束
    //(2)如果前面的有序区没有结束,则把剩余部分全部赋值到暂存空间tempt中
    //(3)如果后面的有序区咩有结束,则把剩余部分全部复制到暂存空间tempt中
    //(4)暂存空间tempt的结果再重新转存回原来的有序区
    template<typename ElemType>
    void SqListSort<ElemType>::mergeSortOne_aux(int low, int mid, int high)
    {
        ElemType temp[LIST_MAX_SIZE];//预存放前后有序区合并的暂存空间
        int k;    //预存放暂存空间的下标
        //前后两个有序区中的元素逐一比较,将较小的放入temp中
        for (int i = k = low, j = mid + 1; i <= mid &&j <= high;)
        {
            if (elem[i] <= elem[j])
            {
                temp[k++] = elem[i++];
            }
            else
            {
                temp[k++] = elem[j++];
            }
    
        }
        //如果前段有序区元素有剩余,则将剩下的元素放入temp中
        if (i<=mid)
        {
            for (; i <= mid;)
            {
                temp[k++] = elem[i++];
            }
    
        }
        //如果后段有序区元素有剩余,则将剩下的元素放入temp中
        if (j<=high)
        {
            for (; j <= high;)
            {
                temp[k++] = elem[j++];
            }
    
        }
        //将temp中的元素放回原有序区中
        for ( k = low; k < =high; ++k)
        {
            elem[k] = temp[k];
        }
    }
    
    //////////////////////////////////////////希尔排序////////////////////////////////////////////////
    //希尔排序
    //步骤:从希尔增量数组第一个增量开始,每次把顺序表下标具有相同增量值的元素划分为一组,一组一组地执行插入排序,
    //     直到希尔增量数组最后一个增量值为1为止
    template<typename ElemType>
    void SqListSort<ElemType>::shellSort()
    {
        int dlta[LIST_MAX_SIZE];//希尔增量数组
        //计算希尔增量数组各元素的值(若该数组有值,则可省略此步)
        for (int d = n/2,k=0; d >=1; d=d/2)
        {
            dila[k++] = d;
        }
    
        //从希尔增量数组第一个增量值开始,每次把顺序表下标具有相同增量值的元素划分为一组,一组一组的执行插入排序
        //直到希尔增量数组最后一个增量为1,为止。
        for (int i = 0; i < k; ++i)
        {
            shellSort_aux(dlta[i]);
        }
    
    }
    
    //希尔排序辅助函数
    //步骤:把顺序表下标具有相同增量值dk的元素划分为一组,同组的元素执行如下的插入排序:
    //       假设当前待排序的元素elem[i],则在elem[0...i-1]范围内与其elem[i]同组的元素已经有序,
    //       所以只要从后往前,在同组有序元素中寻找elem[i]的插入位置。如果elem[i]小于同组的某个元素,则该元素
    //       要在同组中做后移
    
    template<typename ElemType>
    void SqListSor<ElemType>::shellSort_aux(int dk)
    {
        int j;
        static int time = 1;//预记录希尔排序的趟数
        ElemType key;//预存放当前待排序的元素
    
        //把顺序表下标具有相同增量值dk的元素划分为一组,同组的元素执行插入排序
        for (int i = dk; i < n; ++i)
        {
            j = i - dk;
            if (elem[i]<elem[j])
            {
                key = elem[i];//暂存当前待排序元素
                for (; j >= 0&&key < elem[j]; j-=dk)
                {
                    elem[j + dk] = elem[j];
                }
                elem[j + dk] = key;//把当前待排序的元素填写到下标为j+dk的单元中
    
            }
        }
    }
    
    ///////////////////////////////基数排序////////////////////////////////////////////////////////////
    //步骤:(静态链表存储)
    //(1)静态链表指针域初始化,即每个结点的指针域都指向其下一个结点,最后一个结点的指针域为0,表示静态链表结束
    //(2)从低位到高位,对十进制数按先个位,再十位,百位....顺序对各位进行分配和收集
    template<typename ElemType>
    void SqLsitSort<ElemType>::radixSort()
    {
        int front[RADIX], end[RADIX];//预存放基数排序各队列的对头及队尾
        //RADIX为基数排序基数的常量,设置为10,可根据需要修改
    
        //静态链表指针初始化
        for (int  i = 0; i < n; ++i)
        {
            elem[i].next = i + 1;
        }
        
        //最后一个结点的支指针域置0
        elem[n - 1].next = 0;
        //对十进制数按先个位、再十位、百位...顺序对各位进行分配和收集
        //(基数排序十进制数位数的常量KEYNUM,设置为3,可以根据需要更改)
        for ( int i = i; i < KEYNUM;  ++i)
        {   //第i趟分配
            radixSortDistribute_aux(i, front, end);
            //第i趟收集
            radixSortCollect_aux(forn, end,i);
        }
    }
    
    //基数排序的收集函数(静态链表存储)
    //步骤:
    //(1)寻找第一个非空的队列链表
    //(2)第一个非空队列链表的第一个结点即为此趟排序的第一个结点
    //(3)记录第一个非空队列链表最后一个结点的下标
    //(4)按下面的方法依次收集其余的非空队列链表
    //  1寻找下一个非空队列链表
    //  2如果下一个非空队列链表存在,则把下一个非空队列链表链接到先前链表的尾部
    //  3记录最后一个结点的下标
    //(5)全部链表收集完毕,最后一个结点的指针域next置空
    template<typename ElemType>
    void SqListSort<ElemType>::radixSortCollect_aux(int front[], int end[], int time)
    {
        int rear;
        //寻找第一个非空队列链表
        for (int j = 0; !front[j];++j)
        {
    
        }
        //第一个非空队列的第一个结点即为此趟排序的第一个结点
        elem[0].next = front[j];
        rear = end[j];
        //依次收集其余非空的队列链表
        while (j<RADIX)
        {
            for (; j<RADIX-1&&!front[j]; ++j)
            {
                if (j<RADIX&&front[j])
                {
                    elem[rear].next = front[j];
                    rear = end[j];
                }
            }
        }
        elem[rear].next = 0;//全部链表收集完毕,最后一个结点的指针域next置空
    }
    
    //基数排序的分配函数
    //根据指定的位数,执行如下步骤把静态链表所有的结点进行分配
    //(1)取静态链表当前结点的数据域
    //(2)取当前结点数据域的指定位
    //(3)如果当前结点的数据域指定位的队列链表为空,则当前结点成为该队列的第一个结点,否则,把当前结点链接到该队列
    //   尾部
    template<typename ElemType>
    void SqListSort<ElemType>::radixSortDistribute_aux(int i, int front[], int end[])
    {
        int p;//预存放静态链表的指针
        int pos;//预存放此次排序分配基于的位数
        int t;//预存放当前待排序分配的元素
        for (int j = 0; j < RADIX; ++j)
        {
            front[j] = 0;
        }
        //根据指定的位数,把静态链表的所有结点进行排序分配
        for (p = elem[0].next; p;p=elem[p].next)
        {
            t = elem[p].data;//取静态链表当前结点的数据域
            //取当前结点数据域的指定位
            pos = i;
            while (pos>1)
            {
                t /= 10;
                --pos;
            }
            j = t % 10;
            if (!front[j])//如果当前结点数据域指定位的队列链表为空,则
            {
                front[j] = p;//当前结点称为该队列的第一个结点
            }
            else
            {    //如果当前结点数据域指定位的队列不为空,则当前结点链接到该队列的尾部
                elem[end[j]].next = p;
            }
        }  
    end[j]
    = p; }
  • 相关阅读:
    你的人生许多痛苦源于盲目较劲
    这些HTML、CSS知识点,面试和平时开发都需要 (转)
    拿什么拯救你,我的代码--c#编码规范实战篇 (转)
    最近的面试总结
    感恩和珍惜现在的生活
    我眼中的领域驱动设计(转)
    《生活就像练习》读书笔记(四)——意识状态和类型
    《生活就像练习》读书笔记(三)——发展路线
    .NET面试题解析(07)-多线程编程与线程同步 (转)
    C#进阶系列——WebApi身份认证解决方案:Basic基础认证 (转)
  • 原文地址:https://www.cnblogs.com/ITxiansheng/p/3675556.html
Copyright © 2020-2023  润新知