[算法学习] 排序算法(一)——选择、插入、归并

这里开始整理常见的排序算法.
本文包含 [ 选择排序, 插入排序, 归并排序 ]
type right by Thomas Alan 光风霁月023 .XDU

准备测试用例

准备main.cpp文件, 这个文件就是测试用例的入口, 写完算法以后在这里添加内容.

另附, 各种排序算法的时间复杂度. https://blog.csdn.net/weixin_43207025/article/details/114902065

/*
 * @Description:
 * @Version: 1.0
 * @Autor: Thomas
 * @Date: 2022-07-14 18:00:49
 * @LastEditors: Thomas
 * @LastEditTime: 2022-12-29 19:08:39
 */

// 这里include了全局的定义文件和helper文件
#include "define/common_include.h"
#include "helper/sort_test_helper.h"

// 这里include各种排序算法的算法实现文件, 根据个人的目录安排来include
#include "sort/selection_sort.h"
#include "sort/insertion_sort.h"
#include "sort/merge_sort.h"
#include "sort/quick_sort.h"
#include "sort/heap_sort.h"


int main()
{
    int num = 500000;

    int *arr = SortTestHelper::generateRandomArray(num, 0, num);
    // int *arr = SortTestHelper::generateNearlyOrderedArray(num, 100);

    // sort examples
    if (1) // 因为main函数我已经加了不少东西, 所以把测试用例部分放到一个单独的代码块中
    {
        int *arr2 = SortTestHelper::copyIntArray(arr, num);
        int *arr3 = SortTestHelper::copyIntArray(arr, num);
        int *arr4 = SortTestHelper::copyIntArray(arr, num);
        int *arr5 = SortTestHelper::copyIntArray(arr, num);
        int *arr6 = SortTestHelper::copyIntArray(arr, num);
        int *arr7 = SortTestHelper::copyIntArray(arr, num);
        int *arr8 = SortTestHelper::copyIntArray(arr, num);
        int *arr9 = SortTestHelper::copyIntArray(arr, num);
        int *arr10 = SortTestHelper::copyIntArray(arr, num);

        // ortTestHelper::testSort("selection sort", selectionSort, arr, num);
        // SortTestHelper::testSort("insertion sort", insertionSort, arr2, num);
        SortTestHelper::testSort("merge sort", mergeSort, arr3, num);
        SortTestHelper::testSort("merge sort bu", mergeSortBU, arr4, num);
        SortTestHelper::testSort("quick sort", quickSort, arr5, num);
        SortTestHelper::testSort("quick sort 2", quickSort2, arr6, num);
        SortTestHelper::testSort("quick sort 3 ways", quickSort3Ways, arr7, num);
        SortTestHelper::testSort("heap sort", heapSort1, arr8, num);
        SortTestHelper::testSort("heap sort2", heapSort2, arr9, num);
        SortTestHelper::testSort("heap sort3", heapSort, arr10, num);

        delete[] arr3;
        delete[] arr4;
        delete[] arr5;
        delete[] arr6;
        delete[] arr7;
        delete[] arr8;
        delete[] arr9;
        delete[] arr10;
    }

    delete[] arr;

    system("pause");

    return 0;
}

一、选择排序

从头遍历数组, 找出后面最小的元素, 与开头交换位置.

template 
void selectionSort(T arr[], TINT32 num)
{
    for (TINT32 idx = 0; idx < num; idx++)
    {
        // 寻找[idx,n)中的的最小值的索引, 将其与idx位置的元素交换位置
        TINT32 min_idx = idx;

        for (TINT32 idj = idx + 1; idj < num; idj++)
        {
            if (arr[idj] < arr[min_idx])
            {
                min_idx = idj;
            }
        }
        swap(arr[idx], arr[min_idx]);
    }
}

二、插入排序

遍历整个数组, 将新遍历的数插入到之前已经排序好的数组之中.

template 
void insertionSort(T arr[], TINT32 num)
{
    for (TINT32 idx = 1; idx < num; idx++)
    {
        T key = arr[idx];
        TINT32 idj = idx; // idj就是元素最后插入的位置, 后面的过程就是把已经排序好的, 大的元素向后移一位, 直到key找到合适的位置.
        for (idj = idx; idj > 0 && arr[idj - 1] > key; idj--)
        {
            arr[idj] = arr[idj - 1];
        }
        arr[idj] = key;
    }
}

对数组指定范围进行插入排序

template 
void insertionSort(T arr[], TINT32 l, TINT32 r)
{
    for (TINT32 idx = l + 1; idx <= r; idx++)
    {
        T key = arr[idx];
        TINT32 idj = idx;
        for (idj = idx; idj > l && arr[idj - 1] > key; idj--)
        {
            arr[idj] = arr[idj - 1];
        }
        arr[idj] = key;
    }
}

三、归并排序

1. 自顶向下的归并

递归将整个数组不断分割两部分, 分割到只有一个元素时合并.

// 1. 归并操作
template 
// 对arr的[l...mid]部分和arr的[mid+1...r]部分进行归并
void __merge(T arr[], TINT32 l, TINT32 mid, TINT32 r)
{
    T aux[r - l + 1];
    for (TINT32 idx = l; idx <= r; idx++)
    {
        aux[idx - l] = arr[idx];
    }

    TINT32 idx = l;
    TINT32 idj = mid + 1;
    for (TINT32 k = l; k <= r; k++)
    {
        if (idx > mid)
        {
            // 左边的组都归并完毕
            arr[k] = aux[idj - l];
            idj++;
        }
        else if (idj > r)
        {
            // 右边的组都归并完毕
            arr[k] = aux[idx - l];
            idx++;
        }
        else if (aux[idx - l] < aux[idj - l])
        {
            arr[k] = aux[idx - l];
            idx++;
        }
        else
        {
            arr[k] = aux[idj - l];
            idj++;
        }
    }
}

// 2. 递归对数组arr的[l...r]范围元素进行排序
void __mergeSort(T arr[], TINT32 l, TINT32 r)
{
    // if (l >= r)
    // {
    //     return;
    // }

    if (r - l <= 15)
    {
        // 长度小的时候用插入排序效率高
        insertionSort(arr, l, r);
        return;
    }

    TINT32 mid = (l + r) / 2;       // l + r 可能发生数据溢出, 暂时不考虑
    __mergeSort(arr, l, mid);       // 对 arr的[l...mid]范围进行归并排序
    __mergeSort(arr, mid + 1, r);   // 对 arr的[mid+1...r]范围进行归并排序
    if (arr[mid] <= arr[mid + 1])
    {
        // 如果arr的[l...mid]最大的数小于或等于arr的[mid+1...r]最小的数, 那自然不用再归并
        return;
    }

    __merge(arr, l, mid, r);        // 对arr的[l...mid]部分和arr的[mid+1...r]部分进行归并
}

// 3. 入口
template 
void mergeSort(T arr[], TINT32 num)
{
    __mergeSort(arr, 0, num - 1);
}

2. 自底向上的归并

循环着从一个元素开始, 将数组归并.

template 
void mergeSortBU(T arr[], TINT32 num)
{
    for (TINT32 sz = 1; sz <= num; sz += sz)
    {
        if (sz <= 15)
        {
            // 长度小的时候用插入排序效率高
            for (TINT32 idx = 0; idx + sz < num; idx += sz + sz)
            {
                insertionSort(arr, idx, min(idx + sz + sz - 1, num - 1));
            }
        }
        else
        {
            for (TINT32 idx = 0; idx + sz < num; idx += sz + sz)
            {
                __merge(arr, idx, idx + sz - 1, min(idx + sz + sz - 1, num - 1));
            }
        }
    }
}

自底向上的归并算法, 需要明确将数组的分组情况, 尤其需要注意索引越界的问题. idx + sz + sz - 1有可能超过num - 1, 这里需要取两者的较小值.

你可能感兴趣的:([算法学习] 排序算法(一)——选择、插入、归并)