c++ 八大排序代码总结

2024-06-03 02:38
文章标签 代码 c++ 总结 排序 八大

本文主要是介绍c++ 八大排序代码总结,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

//(1)并归排序


//归并排序,其的基本思路就是将数组分成二组A,B,如果这二组组内的数据都是有序的,
//那么就可以很方便的将这二组数据进行排序。如何让这二组组内数据有序了?
//可以将A,B组各自再分成二组。依次类推,当分出来的小组只有一个数据时,
//可以认为这个小组组内已经达到了有序,然后再合并相邻的二个小组就可以了。这样通过先递归的分解数列,再合并数列就完成了归并排序。
template<typename T>
void merge(T A[], T B[], T C[], int lengthA, int lengthB)  //用来就将两个有序的数组合并成一个
{
    int indexA = 0, indexB = 0, indexC = 0;   //indexA是数组A下标,indexB是数组B的下标,indexC是新数组C的下标
    while (indexC < lengthA + lengthB)    //当indexC的大小是A B数组的和的时候,说明A B数组已经比较完了,出循环
    {
        if (A[indexA] < B[indexB])        //如果A数组的当前值比较小,将其放在C中。
        {
            C[indexC++] = A[indexA++];
        }
        else                               //A B数组的各个值比较,将小的先放在C中,合并完后,C是一个有序的数组
        {
            C[indexC++] = B[indexB++];
        }
        if (indexA == lengthA)            //说明A数组的数字已经全部遍历完了,然后把B剩下的数全部放进C
        {
            while (indexB < lengthB)
            {
                C[indexC++] = B[indexB++]
            }
        }
        else if (indexB == lengthB)      //说明B数组的数字已经全部遍历完了,然后把A剩下的数全部放进C
        {
            while (indexA < lengthA)
            {
                C[indexC++] = A[indexA++];
            }
        }
    }
}


template<typename T>
void mergSort(T  A[], int n)//将原数组分解,分而治之,每次折半
{
    if (n > 1)
    {
        int i = n / 2;
        int j = n - n / 2;
        T B[n / 2];
        T C[n - n / 2];
        for (int k = 0; k < i; ++K)
            B[k] = A[k];
        for (int k = 0; k < j; ++K)
            C[k] = A[k + i];
        mergSort(B, i);
        mergSort(C, j);
        merge(B, C, A, i, j);
    }
}




//(2) 快排


//快速排序是找出一个元素(平时常用作为基准的就是第一个,最后一个,中间)作为基准(pivot), 然后对数组进行分区操作, 
//使基准左边元素的值都不大于基准值, 基准右边的元素值 都不小于基准值,
//如此作为基准的元素调整到排序后的正确位置。递归快速排序,
//将其他n - 1个元素也调整到排序后的正确位置。最后每个元素都是在排序后的正 确位置,排序完成。
//所以快速排序算法的核心算法是分区操作,即如何调整基准的位置以及调整返回基准的最终位置以便分治递归。
template<typename T>
void quickSort(T data[], int min, int max)
{
    int m_min = min;
    int m_max = max;
    T mid = data[min];    //将第一个作为基准
    if (min < max)        //前后的指针未相遇
    {
        while (m_min < m_max)
        {
            while (m_min < m_max && data[m_max]>mid)  //从后向前移动max指针。如果当前值比基准值大,指针继续向前移动,后面是大数。
            {                                         //每次判断m_min < m_max,因为指针在一直移动。
                --m_max;
            }
            data[m_min] = data[m_max];                //出了循环,说明当前值比基准小,所以把当前值放了基准的位置
            while (m_min < m_max && data[m_min] <= mid)//从前向后移动min指针。如果当前值比基准值小,指针继续向后移动,前面是小数。
            {
                ++m_min;
            }
            data[m_max] = data[m_min];
        }
        data[m_min] = mid;                              //刚开始选择的基准,就在这个位置
        quickSort(data, min, m_min - 1);
        quickSort(data, m_min + 1, max);
    }
}


//(3)插入排序
//插入排序就是每一步都将一个待排数据按其大小插入到已经排序的数据中的适当位置,直到全部插入完毕。
template<typename T>
void insertSort(T data[], int size)
{
    int i = 0;
    int j = 0;
    T key;
    for (j = 1; j < size; ++j)
    {
        key = data[j];
        i = j - 1;
        while (i >= 0 && key < data[i])//这里是找当前值的正确位置,比当前值大的话,将当前值后移,直到找到合适位置
        {
            data[i + 1] = data[i];
            --i;
        }
        data[i + 1] = key;              //找到正确位置了。原先这个位置的值已经移动到了后面,将key放入
    }
}


//(4)冒泡排序
//相邻两个数比较,依次交换。
template<typename T>
void BubbleSort(T data[], int size)
{
    bool flag = false;
    int i = 0;
    int j = 0;
    do
    {
        for (j = 0; j < size - i - 1; ++j)
        {
            if (data[j]>data[j + 1])
            {
                data[j] = data[j] + data[j + 1];
                data[j + 1] = data[j] - data[j + 1];         //这里不用异或^(data[j] = data[j] ^ data[j + 1])是因为异或能用于double
                data[j] = data[j] - data[j + 1];
                flag = true;//防止数字已经有序,程序会多执行几次循环
            }
        }
        ++i;
    } while (i < size && true == flag);
}


//(5)堆排序
//利用大顶堆(小顶堆)堆顶记录的是最大关键字(最小关键字)这一特性,使得每次从无序中选择最大记录(最小记录)变得简单。
//
//其基本思想为(大顶堆):
//1)将初始待排序关键字序列(R1, R2....Rn)构建成大顶堆,此堆为初始的无序区;
//2)将堆顶元素R[1]与最后一个元素R[n]交换,此时得到新的无序区(R1, R2, ......Rn - 1)和新的有序区(Rn), 且满足R[1, 2...n - 1] <= R[n];
//3)由于交换后新的堆顶R[1]可能违反堆的性质,因此需要对当前无序区(R1, R2, ......Rn - 1)调整为新堆,
//然后再次将R[1]与无序区最后一个元素交换,得到新的无序区(R1, R2....Rn - 2)和新的有序区(Rn - 1, Rn)。
//不断重复此过程直到有序区的元素个数为n - 1,则整个排序过程完成。
template<typename T>
void keepHeap(T data[], int heap_size, int k)//每个父节点和自己的叶子结点比较,看是否满足堆的性质
{
    int left = 2 * k + 1;               //堆的深度,因为下标成从0开始的,所以左孩子是加1,右孩子加2
    int right = 2 * k + 2;
    int largest = k;
    if (left<heap_size && data[left]>data[k])
    {
        largest = left;
    }
    if (right<heap_size && data[right]>data[largest])
    {
        largest = right;
    }
    if (largest != k)             //上面两个if是为了找到左右孩子里面,较大的数,然后和当前父节点交换
    {
        data[k] = data[largest] + data[k];
        data[largest] = data[k] - data[largest];
        data[k] = data[k] - data[largest];


        keepHeap(data, heap_size, largest);        //选择当前最大的值,继续看是否符合堆的性质
    }
}


template<typename T>
void buildHeap(T data[], int heap_size)
{
    int i = heap_size / 2 - 1;
    while (i >= 0)
    {
        keepHeap(data, heap_size, i);      //从小往上,建立堆
        --i;
    }
}


template<typename T>
void heapSort(T data[], int n)
{
    int heap_size = n;
    buildHeap(data, heap_size); //建立堆
    for (int i = heap_size - 1; i > 0; --i)   //最后一个叶子和堆顶交换
    {
        data[0] = data[0] + data[i];
        data[i] = data[0] - data[i];
        data[0] = data[0] - data[i];


        --heap_size;
        keepHeap(data, heap_size, 0);
    }
}


//(6)希尔排序
//希尔排序的实质就是分组插入排序,该方法又称缩小增量排序
//基本思想是:先将整个待排元素序列分割成若干个子序列(由相隔某个“增量”的元素组成的)分别进行(直接插入,冒泡)排序,
//然后依次缩减增量再进行排序,
//待整个序列中的元素基本有序(增量足够小)时,再对全体元素进行一次排序。因为(直接插入,冒泡)某些排序在元素基本有序的情况下高效
//(接近最好情况),
//效率是很高的,因此希尔排序在时间效率上比前两种方法有较大提高。
template<typename T>
void shellSort(T *A, int n)
{
    int  i, j, gap = n;
    int counter = 1;
    bool flag;
    while (gap > 1)//增量小于一时,对全体进行排序
    {
        gap = gap / 2;
        counter = 1;
        do{
            flag = false;
            for (i = 0; i <= n - gap - counter; ++i)//用冒泡排序对一个小子序列进行排序
            {


                j = i + gap;
                if (A[i] > A[j])
                {
                    A[i] = A[i] + A[j];
                    A[j] = A[i] - A[j];
                    A[i] = A[i] - A[j];
                    flag = true;
                }
            }
            ++counter;
        } while (counter < n && flag == 1);
    }
}




//(7)选择排序
//每一趟排序从序列中未排好序的那些元素中选择一个值最小(最大)的元素,然后将其与这些未排好序的元素的第一个(最后一个)元素交换位置。
template<typename T>
void selectionSort(T data[], int n)
{
    int i = 1, j;
    int max;            //记录最大值
    while (i < n)
    {
        max = n - i;
        for (j = 0; j < n - i + 1; ++j)  //为了找最大值
        {
            if (data[j]>data[max])
                max = j;
        }
        if (max != n - i)              //判断是否找到新的最大值,进行交换
        {
            data[max] = data[max] + data[n - i];
            data[n - i] = data[max] - data[n - i];
            data[max] = data[max] - data[n - i];
        }
        ++i;
    }
}
//(8)计数排序
//计数排序是一个类似于桶排序的排序算法,其优势是对已知数量范围的数组进行排序。
//初始化一个计数数组,大小是输入数组中的最大的数。
//遍历输入数组,遇到一个数就在计数数组对应的位置上加一。例如:遇到5,就将计数数组第五个位置的数加一。
//把计数数组直接覆盖到输出数组(节约空间)。
template<typename T>
void countingSort(T *a, int n)
{
    int k;//数组B长度
    int dx = 0;//反向填充目标数组时的位置标示
    T max = a[0];//待排序数组的最大值
    T min = a[0];//待排序数组的最小值


    for (int i = 1; i < n; ++i)//找到最大最小值,然后算应该分配多大空间
    {
        if (a[i]>max)
            max = a[i];
        else if (a[i] < min)
            min = a[i];
    }
    k = max - min + 1;//根据最大最小值计算K
    int *b = new int[k];
    memset(b, 0, k*sizeof(int));  //初始化0
    for (int i = 0; i < n; ++i)  //记录数字
        ++b[a[i] - min];
    for (int i = 0; i < k; ++i)
    {
        while (b[i]--)          //填充数组
        {
            a[dx++] = i + min;
        }
    }
    delete[] b;
}

这篇关于c++ 八大排序代码总结的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1025799

相关文章

Windows Docker端口占用错误及解决方案总结

《WindowsDocker端口占用错误及解决方案总结》在Windows环境下使用Docker容器时,端口占用错误是开发和运维中常见且棘手的问题,本文将深入剖析该问题的成因,介绍如何通过查看端口分配... 目录引言Windows docker 端口占用错误及解决方案汇总端口冲突形成原因解析诊断当前端口情况解

Linux下如何使用C++获取硬件信息

《Linux下如何使用C++获取硬件信息》这篇文章主要为大家详细介绍了如何使用C++实现获取CPU,主板,磁盘,BIOS信息等硬件信息,文中的示例代码讲解详细,感兴趣的小伙伴可以了解下... 目录方法获取CPU信息:读取"/proc/cpuinfo"文件获取磁盘信息:读取"/proc/diskstats"文

使用Java将DOCX文档解析为Markdown文档的代码实现

《使用Java将DOCX文档解析为Markdown文档的代码实现》在现代文档处理中,Markdown(MD)因其简洁的语法和良好的可读性,逐渐成为开发者、技术写作者和内容创作者的首选格式,然而,许多文... 目录引言1. 工具和库介绍2. 安装依赖库3. 使用Apache POI解析DOCX文档4. 将解析

C++使用printf语句实现进制转换的示例代码

《C++使用printf语句实现进制转换的示例代码》在C语言中,printf函数可以直接实现部分进制转换功能,通过格式说明符(formatspecifier)快速输出不同进制的数值,下面给大家分享C+... 目录一、printf 原生支持的进制转换1. 十进制、八进制、十六进制转换2. 显示进制前缀3. 指

C++中初始化二维数组的几种常见方法

《C++中初始化二维数组的几种常见方法》本文详细介绍了在C++中初始化二维数组的不同方式,包括静态初始化、循环、全部为零、部分初始化、std::array和std::vector,以及std::vec... 目录1. 静态初始化2. 使用循环初始化3. 全部初始化为零4. 部分初始化5. 使用 std::a

使用Python实现全能手机虚拟键盘的示例代码

《使用Python实现全能手机虚拟键盘的示例代码》在数字化办公时代,你是否遇到过这样的场景:会议室投影电脑突然键盘失灵、躺在沙发上想远程控制书房电脑、或者需要给长辈远程协助操作?今天我要分享的Pyth... 目录一、项目概述:不止于键盘的远程控制方案1.1 创新价值1.2 技术栈全景二、需求实现步骤一、需求

Java中Date、LocalDate、LocalDateTime、LocalTime、时间戳之间的相互转换代码

《Java中Date、LocalDate、LocalDateTime、LocalTime、时间戳之间的相互转换代码》:本文主要介绍Java中日期时间转换的多种方法,包括将Date转换为LocalD... 目录一、Date转LocalDateTime二、Date转LocalDate三、LocalDateTim

C++ vector的常见用法超详细讲解

《C++vector的常见用法超详细讲解》:本文主要介绍C++vector的常见用法,包括C++中vector容器的定义、初始化方法、访问元素、常用函数及其时间复杂度,通过代码介绍的非常详细,... 目录1、vector的定义2、vector常用初始化方法1、使编程用花括号直接赋值2、使用圆括号赋值3、ve

如何高效移除C++关联容器中的元素

《如何高效移除C++关联容器中的元素》关联容器和顺序容器有着很大不同,关联容器中的元素是按照关键字来保存和访问的,而顺序容器中的元素是按它们在容器中的位置来顺序保存和访问的,本文介绍了如何高效移除C+... 目录一、简介二、移除给定位置的元素三、移除与特定键值等价的元素四、移除满足特android定条件的元

jupyter代码块没有运行图标的解决方案

《jupyter代码块没有运行图标的解决方案》:本文主要介绍jupyter代码块没有运行图标的解决方案,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录jupyter代码块没有运行图标的解决1.找到Jupyter notebook的系统配置文件2.这时候一般会搜索到