数据结构(排序,冒泡排序、选择排序、插入排序、希尔排序、堆排序、快速排序、归并排序,C++)

冒泡排序

冒泡排序(Bubble Sort)一种交换排序,它的基本思想是:两两比较相邻记录的关键字,如果反序则交换,知道没有反序的记录为止。冒泡排序是一种比较简单的排序方法,实现代码如下:


// 交换两个下标所指的值
template<typename  T>
void swap(T *ptr, int i, int j)
{
    T temp = ptr[i];
    ptr[i] = ptr[j];
    ptr[j] = temp;
}

// 冒泡排序
template<typename  T>
void bubbleSort(T * ptr, int length)
{
    for (int i = 0; i < length; i++)
    {
        for (int j = length - 1; j > i; j--)
        {
            if (ptr[j] < ptr[j - 1])
            {
                swap(ptr, j, j - 1);
            }
        }
    }
}

测试代码如下:


int main()
{
    int ptr[15] = { 4, 5, 7, 3, 8, 9, 1, 2, 6, 2 , 49, 23, 10, 30, 3 };

    bubbleSortEx(ptr, 15);
    show(ptr, 15);

    getchar();
    return 0;
}

测试结果如下图:

数据结构(排序,冒泡排序、选择排序、插入排序、希尔排序、堆排序、快速排序、归并排序,C++)_第1张图片

排序过程

排序过程如下:

数据结构(排序,冒泡排序、选择排序、插入排序、希尔排序、堆排序、快速排序、归并排序,C++)_第2张图片

上图显示了,第一次循环过程,第一次把最小的关键字移到了第一位。后面依次类推。只是下一次循环只循环到下标1。依次到2、3、… n-2。(n为元素个数)。

冒泡排序改进

如果我们待排序的序列是{2, 1, 4, 5, 6},则除了第一个和第二个关键字需要交换外,别的都已经是正常的顺序了。也就是说后面的其实不用再进行比较了。代码如下:


// 冒泡排序改进
template<typename  T>
void bubbleSortEx(T * ptr, int length)
{
    bool flag = true;
    // 如果有交换数据则说明未排完序,false表示上一次没有数据交换,即后面已经拍好序了,所以不用再排了
    for (int i = 0; i < length && flag; i++)
    {
        flag = false;
        for (int j = length - 1; j > i; j--)
        {
            if (ptr[j] < ptr[j - 1])
            {
                swap(ptr, j, j - 1);
                flag = true;
            }
        }
    }
}

简单选择排序

简单选择排序法(Simple Delection Sort)就是通过n - i 次关键字间的比较,从n - i + 1个记录中选取出关键字最小的记录,并和第i (1 <= i <= n)个记录交换。实现代码如下:


// 简单选择排序
template<typename  T>
void simpleSelectionSort(T * ptr, int length)
{
    // 最小值下标
    int min = 0;
    for (int i = 0; i < length; i++)
    {
        min = i;
        for (int j = i; j < length; j++)
        {
            if (ptr[min] > ptr[j])
            {
                min = j;
            }
        }
        if (min != i && ptr[min] != ptr[i])
        {
            swap(ptr, min, i);
        }
    }
}

测试代码如下:


int main()
{
    int ptr[15] = { 4, 5, 7, 3, 8, 9, 1, 2, 6, 2 , 49, 23, 10, 30, 3 };
    simpleSelectionSort(ptr, 15);

    getchar();
    return 0;
}

测试结果如下图:

数据结构(排序,冒泡排序、选择排序、插入排序、希尔排序、堆排序、快速排序、归并排序,C++)_第3张图片

排序过程

数据结构(排序,冒泡排序、选择排序、插入排序、希尔排序、堆排序、快速排序、归并排序,C++)_第4张图片

选择排序相当于依次选出后面中的最小值放到最前面。上图中第一次循环,选出最小值1,后面依次从下标1、2…n - 2(n为元素个数)开始向后查找最小值。

直接插入排序

直接插入排序(Straight Insertion Sort)的基本操作是将一个记录插入到已经排好序的有序表中,从而得到一个新的、记录数增1的有序表。直接插入排序中把每一个单独的关键字看作一个有序的序列。实现代码如下:


template<typename  T>
void insertSort(T * ptr, int length)
{
    int i, j;
    T temp;
    for (i = 1; i < length; i++)
    {
        if (ptr[i] < ptr[i - 1])
        {
            temp = ptr[i];
            for (j = i - 1; ptr[j] > temp && j >= 0; j--)
            {
                ptr[j + 1] = ptr[j];
            }
            ptr[j + 1] = temp;
        }
    }
}

测试代码如下:


int main()
{
    int ptr[15] = { 4, 5, 7, 3, 8, 9, 1, 2, 6, 2 , 49, 23, 10, 30, 3 };
    insertSort(ptr, 15);
    show(ptr, 15);

    getchar();
    return 0;
}

测试结果如下图:

数据结构(排序,冒泡排序、选择排序、插入排序、希尔排序、堆排序、快速排序、归并排序,C++)_第5张图片

排序过程

数据结构(排序,冒泡排序、选择排序、插入排序、希尔排序、堆排序、快速排序、归并排序,C++)_第6张图片

上图是直接插入排序的一部分,后面依次类推。

堆排序

堆是具有以下性质的完全二叉树;每个结点的值都大于或等于其左右孩子结点的值,称为大顶堆;或者每个结点的值都小于或等于其左右孩子结点的值,称为小顶堆。如下图,是 一个大顶堆。

数据结构(排序,冒泡排序、选择排序、插入排序、希尔排序、堆排序、快速排序、归并排序,C++)_第7张图片

大顶堆一般是按照层序遍历放在一个数组中如下:

99 90 80 50 40 5 20 10 30

堆排序(Heap Sort)就是利用堆进行排序的方法。它的基本思想是,将待排序的序列构成一个大顶堆。此时,整个序列的最大值就是对顶的根结点。将它移走(其实就是将其与对数组的末尾元素交换,此时末尾元素就是最大值),然后将剩余的n - 1个序列重新构成一个堆,这样就会得到n个元素中的次小值。如此反复执行,就能得到一个有序序列了。如下图:

数据结构(排序,冒泡排序、选择排序、插入排序、希尔排序、堆排序、快速排序、归并排序,C++)_第8张图片

上图是排序的一部分,后面依次类推。实现代码如下:


// 堆排序
template<typename T>
void heapAdjust(T * ptr, int nRoot, int nEnd)
{
    int j;
    T temp = ptr[nRoot];
    for (j = 2 * nRoot; j < nEnd; j *= 2)// 完全二叉树性质,左孩子为2n,右孩子为2n + 1
    {
        if (j < nEnd && ptr[j] < ptr[j + 1])
            j++;
        if (temp >= ptr[j])
            break;
        ptr[nRoot] = ptr[j];
        nRoot = j;
    }
    ptr[nRoot] = temp;
}

template<typename T>
void heapSort(T *ptr, int nLength)
{
    if (nullptr == ptr)
        return;
    int i = 0;
    for (i = nLength / 2 - 1; i >= 0; i--)// 从0开始计数也是符合的
    {
        heapAdjust(ptr, i, nLength - 1);
    }
    for (i = nLength - 1; i > 0; i--)
    {
        swap(ptr, 0, i);
        heapAdjust(ptr, 0, i - 1);
    }
}

测试代码如下:


int main()
{
    int ptr[15] = { 4, 5, 7, 3, 8, 9, 1, 2, 6, 2 , 49, 23, 10, 30, 3 };
    heapSort(ptr, 15);
    show(ptr, 15);

    getchar();
    return 0;
}

测试结果如下图:

数据结构(排序,冒泡排序、选择排序、插入排序、希尔排序、堆排序、快速排序、归并排序,C++)_第9张图片

希尔排序

希尔排序算是直接插入排序的一种更加抽象的提升。希尔排序是多次组分插入排序。插入排序是希尔排序中间隔为1的一种情况。实现代码如下:


// 希尔排序,插入排序的改进,动态间隔来进行插入排序
template<typename  T>
void shellSort(T *ptr, int length)
{
    T temp;
    int increment = length;
    int i, j;
    do {
        increment = increment / 3 + 1;
        i = increment;
        for (i = increment; i < length; i++)
        {
            if (ptr[i] < ptr[i - increment])
            {
                temp = ptr[i];
                for (j = i - increment; j >= 0 && temp < ptr[j]; j -= increment)
                {
                    ptr[j + increment] = ptr[j];
                }
                ptr[j + increment] = temp;
            }
        }
    } while (increment > 1);
}

排序过程如下:

数据结构(排序,冒泡排序、选择排序、插入排序、希尔排序、堆排序、快速排序、归并排序,C++)_第10张图片

测试代码如下:


int main()
{
    int ptr[15] = { 4, 5, 7, 3, 8, 9, 1, 2, 6, 2 , 49, 23, 10, 30, 3 };
    shellSort(ptr, 15);
    show(ptr, 15);

    getchar();
    return 0;
}

测试结果如下图:

数据结构(排序,冒泡排序、选择排序、插入排序、希尔排序、堆排序、快速排序、归并排序,C++)_第11张图片

快速排序

快速排序(Quick Sort)的基本思想是:通过一趟排序将待排序记录分割成独立的两部分,其中一部分的关键字均比另一部分记录的关键字小,则可分别对这两部分记录继续进行快速排序,以达到整个序列有序的目的。

快速排序算是冒泡排序的一个改进。选一个数,左边比它小,右边比他大。相当于同时两端冒泡,左边冒大值,右边冒小值。排序过程如下图:

数据结构(排序,冒泡排序、选择排序、插入排序、希尔排序、堆排序、快速排序、归并排序,C++)_第12张图片

实现代码如下:


// 快速排序, 冒泡排序的改进,选一个数,左边比它小,右边比他大。
// 相当于同时两端冒泡,左边冒大值,右边冒小值
template<typename  T>
int partition(T* ptr, int low, int high)
{
    T temp = ptr[low];
    while (low < high)
    {
        while (low < high && ptr[high] >= temp)
        {
            high--;
        }
        swap(ptr, low, high);
        while (low < high && ptr[low] <= temp)
        {
            low++;
        }
        swap(ptr, low, high);
    }
    return low;
}

template<typename  T>
void qSort(T *ptr, int low, int high)
{
    if (low < high)
    {
        int par = partition(ptr, low, high);
        qSort(ptr, low, par - 1);
        qSort(ptr, par + 1, high);
    }
}

template<typename  T>
void quickSort(T *ptr, int length)
{
    qSort(ptr, 0, length - 1);
}

测试代码如下:


int main()
{
    int ptr[15] = { 4, 5, 7, 3, 8, 9, 1, 2, 6, 2 , 49, 23, 10, 30, 3 };
    quickSort(ptr, 15);
    show(ptr, 15);

    getchar();
    return 0;
}

测试结果如下图:

数据结构(排序,冒泡排序、选择排序、插入排序、希尔排序、堆排序、快速排序、归并排序,C++)_第13张图片

归并排序

归并排序(Merging Sort)真利用归并的思想事项的排序方法。它的原理是假设初始序列包含有n个记录,则可以看成是n个有序的子序列,每个子序列的长度为1,然后两两归并,得到不小于n/2个长度为2或1的有序子序列;然后在两两归并,依次类推。知道得到一个长度为n的有序序列为止。

归并排序的思想很好理解,实现代码如下:


// 归并排序,把每个单独的元素当作一个有序序列,然后分别把多个有序序列归并的一起
template<typename  T>
void merge(T *pl, T *pr, int left, int m, int right)
{
    int i = left, j = m + 1, k = left;
    while (i <= m && j <= right)
    {
        if (pl[i] > pl[j])
        {
            pr[k++] = pl[j++];
        }
        else
        {
            pr[k++] = pl[i++];
        }
    }
    if (i <= m)
    {
        for (int n = i; n <= m; n++)
        {
            pr[k++] = pl[n];
        }
    }
    if (j <= right)
    {
        for (int n = j; n <= right; n++)
        {
            pr[k++] = pl[n];
        }
    }
    for (int n = left; n <= right; n++)
    {
        pl[n] = pr[n];
    }
}

template<typename  T>
void mSort(T * pl, T * pr, int left, int right)
{
    int m;
    if (left == right)
    {
        pr[left] = pl[left];
    }
    if (left < right)
    {
        m = (left + right) / 2;
        mSort(pl, pr, left, m);
        mSort(pl, pr, m + 1, right);
        merge(pr, pl, left, m, right);
    }
}

template<typename  T>
void mergeSort(T *ptr, int length)
{
    T * temp = new T[length];
    mSort(ptr, temp, 0, length - 1);
    delete[] temp;
}

测试代码如下:


int main()
{
    int ptr[15] = { 4, 5, 7, 3, 8, 9, 1, 2, 6, 2 , 49, 23, 10, 30, 3 };
    mergeSort(ptr, 15);
    show(ptr, 15);

    getchar();
    return 0;
}

测试结果如下图:

数据结构(排序,冒泡排序、选择排序、插入排序、希尔排序、堆排序、快速排序、归并排序,C++)_第14张图片

归并排序除了用递归实现,也可以用迭代方法实现,迭代方法没有函数的多次调用,比递归方法效率更高。代码如下:


// 归并排序迭代版,效率比递归更高
template<typename  T>
void mergeSeque(T *pl, T *pr, int step, int length)
{
    int i = 0;
    for (; i <= length - 2 * step - 1; i += 2 * step)
    {
        merge(pl, pr, i, i + step - 1, i + 2 * step - 1);
    }
    if (i < length - step - 1)
    {
        merge(pl, pr, i, i + step - 1, length - 1);
    }
    else
    {
        merge(pl, pr, i - step - 1, i - 1, length - 1);
    }
}

template<typename  T>
void mergeSortIteration(T *ptr, int length)
{
    T * temp = new T[length];
    for (int i = 1; i < length; i += i)
    {
        mergeSeque(ptr, temp, i, length);
    }
    delete[]temp;
}

测试代码如下:


int main()
{
    int ptr[15] = { 4, 5, 7, 3, 8, 9, 1, 2, 6, 2 , 49, 23, 10, 30, 3 };
    mergeSortIteration(ptr, 15);
    show(ptr, 15);

    getchar();
    return 0;
}

测试结果如下图:

数据结构(排序,冒泡排序、选择排序、插入排序、希尔排序、堆排序、快速排序、归并排序,C++)_第15张图片

几种排序方法效果对比

随机生成10000个整数,使用不同的排序程序来排序,输出运行时间。代码如下:

#include "sort.h"
#include 
#include 
#include 
#include 
// 排序算法比较
template<typename T>
void runSort(T * ptr, int length, void (*func)(T *ptr, int length), std::string funcName)
{
    T * pTemp = new T[length];
    for (int i = 0; i < length; i++)
    {
        pTemp[i] = ptr[i];
    }
    clock_t start, stop;
    start = clock();
    func(pTemp, length);
    stop = clock();
    std::cout << funcName << "时间:" << stop - start << std::endl << std::endl;
}

void sortCompare()
{
    int length = 10000;
    int * ptr = new int[length];
    for (int i = 0; i < length; i++)
    {
        ptr[i] = std::rand();
    }
    runSort(ptr, length, bubbleSort, "冒泡排序");
    runSort(ptr, length, bubbleSortEx, "冒泡排序改进");
    runSort(ptr, length, simpleSelectionSort, "简单选择排序");
    runSort(ptr, length, insertSort, "插入排序");
    runSort(ptr, length, heapSort, "堆排序");
    runSort(ptr, length, shellSort, "希尔排序");
    runSort(ptr, length, quickSort, "快速排序");
    runSort(ptr, length, mergeSort, "归并排序");
    runSort(ptr, length, mergeSortIteration, "归并排序迭代版");
}

int main()
{
    sortCompare();
    getchar();
    return 0;
}

测试结果如下图:

数据结构(排序,冒泡排序、选择排序、插入排序、希尔排序、堆排序、快速排序、归并排序,C++)_第16张图片

你可能感兴趣的:(C/C++)