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

相关文章

C++11范围for初始化列表auto decltype详解

《C++11范围for初始化列表autodecltype详解》C++11引入auto类型推导、decltype类型推断、统一列表初始化、范围for循环及智能指针,提升代码简洁性、类型安全与资源管理效... 目录C++11新特性1. 自动类型推导auto1.1 基本语法2. decltype3. 列表初始化3

C++11右值引用与Lambda表达式的使用

《C++11右值引用与Lambda表达式的使用》C++11引入右值引用,实现移动语义提升性能,支持资源转移与完美转发;同时引入Lambda表达式,简化匿名函数定义,通过捕获列表和参数列表灵活处理变量... 目录C++11新特性右值引用和移动语义左值 / 右值常见的左值和右值移动语义移动构造函数移动复制运算符

C++中detach的作用、使用场景及注意事项

《C++中detach的作用、使用场景及注意事项》关于C++中的detach,它主要涉及多线程编程中的线程管理,理解detach的作用、使用场景以及注意事项,对于写出高效、安全的多线程程序至关重要,下... 目录一、什么是join()?它的作用是什么?类比一下:二、join()的作用总结三、join()怎么

Spring Boot 与微服务入门实战详细总结

《SpringBoot与微服务入门实战详细总结》本文讲解SpringBoot框架的核心特性如快速构建、自动配置、零XML与微服务架构的定义、演进及优缺点,涵盖开发环境准备和HelloWorld实战... 目录一、Spring Boot 核心概述二、微服务架构详解1. 微服务的定义与演进2. 微服务的优缺点三

Python实现MQTT通信的示例代码

《Python实现MQTT通信的示例代码》本文主要介绍了Python实现MQTT通信的示例代码,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一... 目录1. 安装paho-mqtt库‌2. 搭建MQTT代理服务器(Broker)‌‌3. pytho

MySQL进行数据库审计的详细步骤和示例代码

《MySQL进行数据库审计的详细步骤和示例代码》数据库审计通过触发器、内置功能及第三方工具记录和监控数据库活动,确保安全、完整与合规,Java代码实现自动化日志记录,整合分析系统提升监控效率,本文给大... 目录一、数据库审计的基本概念二、使用触发器进行数据库审计1. 创建审计表2. 创建触发器三、Java

C++中全局变量和局部变量的区别

《C++中全局变量和局部变量的区别》本文主要介绍了C++中全局变量和局部变量的区别,全局变量和局部变量在作用域和生命周期上有显著的区别,下面就来介绍一下,感兴趣的可以了解一下... 目录一、全局变量定义生命周期存储位置代码示例输出二、局部变量定义生命周期存储位置代码示例输出三、全局变量和局部变量的区别作用域

C++中assign函数的使用

《C++中assign函数的使用》在C++标准模板库中,std::list等容器都提供了assign成员函数,它比操作符更灵活,支持多种初始化方式,下面就来介绍一下assign的用法,具有一定的参考价... 目录​1.assign的基本功能​​语法​2. 具体用法示例​​​(1) 填充n个相同值​​(2)

c++ 类成员变量默认初始值的实现

《c++类成员变量默认初始值的实现》本文主要介绍了c++类成员变量默认初始值,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧... 目录C++类成员变量初始化c++类的变量的初始化在C++中,如果使用类成员变量时未给定其初始值,那么它将被

Java通过驱动包(jar包)连接MySQL数据库的步骤总结及验证方式

《Java通过驱动包(jar包)连接MySQL数据库的步骤总结及验证方式》本文详细介绍如何使用Java通过JDBC连接MySQL数据库,包括下载驱动、配置Eclipse环境、检测数据库连接等关键步骤,... 目录一、下载驱动包二、放jar包三、检测数据库连接JavaJava 如何使用 JDBC 连接 mys