[C/C++ algorithm] advanced sorting algorithm

Table of contents

1. Hill sort

2. Heap sort

3. Merge sort

4. Quick Sort

5. Counting and sorting

6. Radix sorting

1. Hill sort

train of thought

  • Hill sorting belongs to insertion sorting, first pre-sorting the array to make the array relatively ordered, and then performing direct insertion sorting
  • The pre-sorted gap value can take any value >=1. The tested most efficient gap value is gap/3 + 1. The initial value is size
  • The higher the initial ordering degree of the array, the higher the efficiency of insertion sorting

The benefits of this article, free C++ learning materials package, technical video/code, 1000 Dachang interview questions, including (C++ basics, network programming, database, middleware, back-end development, audio and video development, Qt development) ↓↓↓ ↓↓↓See below↓↓Click at the bottom of the article to get it for free↓↓

The instability of time complexity is related to the initial order of the array and the calculation method of gap, between O(n) ~ O(n^2)

void ShellSort(int* arr, int size) 
{
    int gap = size;
    while (gap > 1) 
    {
        gap = gap / 3 + 1;
        int i = 0;
        for (i = 0; i < size - gap; ++i) 
        {
            int end = i;
            int tmp = arr[end + gap];
            while (end >= 0 && tmp < arr[end]) 
            {
                arr[end + gap] = arr[end];
                end -= gap;
            }
            arr[end + gap] = tmp;
        }
    }
}

2. Heap sort

train of thought

  • Create a heap to output the array in ascending order, build a large root heap, and output a descending order, build a small root heap
  • Exchange the element at the top of the heap with the last element and adjust downward from the top of the heap.
  • The heap can be viewed as a complete binary tree stored sequentially

The time complexity is stable and the total complexity is n * lg(n) every time it is adjusted downward from the top of the heap to lg(n)

#include <stdio.h>

void Swap(int* arr, int pi, int pj) 
{
    int tmp = arr[pi];
    arr[pi] = arr[pj];
    arr[pj] = tmp;
}

//大根堆升序
//小根堆降序
void AdjustDown(int* arr, int parent, int size) 
{
    int child = parent * 2 + 1;
    while (child < size) {
        if (child + 1 < size && arr[child + 1] > arr[child]) 
            ++child;
        if (arr[parent] < arr[child]) 
        {
            Swap(arr, parent, child);
            parent = child;
            child = parent * 2 + 1;
        }
        else 
        {
            break;
        }
    }
}

void HeapSort(int* arr, int size) 
{
    int parent = (size - 2) / 2;
    int i;
    for (i = parent; i >= 0; --i) 
    {
        AdjustDown(arr, i, size);   //构建大根堆
    }
    while (size) 
    {
        Swap(arr, 0, --size);
        AdjustDown(arr, 0, size);
    }
    
}

3. Merge sort

train of thought

  • The idea of ​​recursion divides an array from the middle into two sub-arrays and recursively merges and sorts the two sub-arrays respectively.
  • The minimum length of the sub-array is 2 or 3. At this time, set 4 subscripts begin1end1begin2end2
  • begin1, end1 represent the beginning and end of the left sub-array begin2, end2 represent the beginning and end of the right sub-array
  • Simultaneously traverse the two sub-arrays on the left and right to compare the values ​​corresponding to begin1 and begin2 and store them in the count array
  • After the traversal, copy the count to the arr space by address and byte by byte to change the time

Time complexity The complexity of stable recursion is lg(n) Each number will be traversed once and the total complexity is n * lg(n)

void _MergeSort(int* arr, int begin, int end, int* count) 
{
    if (begin >= end) 
        return;

    int mid = (begin + end) / 2;
    _MergeSort(arr, begin, mid, count);
    _MergeSort(arr, mid + 1, end, count);

    int begin1 = begin, end1 = mid;
    int begin2 = mid + 1, end2 = end;
    int i = begin;
    while (begin1 <= end1 && begin2 <= end2) 
    {
        if (arr[begin1] < arr[begin2]) 
            count[i++] = arr[begin1++];
        else 
            count[i++] = arr[begin2++];
    }
    while (begin1 <= end1) 
    {
        count[i++] = arr[begin1++];
    }
    while (begin2 <= end2) 
    {
        count[i++] = arr[begin2++];
    }

    memcpy(arr + begin, count + begin, sizeof(int) * (end - begin + 1));
}

void MergeSort(int* arr, int size) 
{
    int* count = (int*)malloc(sizeof(int) * size);
    int mid = size / 2;
    _MergeSort(arr, 0, mid, count);
    _MergeSort(arr, mid + 1, size - 1, count);
    free(count);
    count = NULL;
}

4. Quick Sort

train of thought

  • The recursive thinking array takes a keyword key value and arranges the number smaller than the key to the left of the key and the number larger than the key to the right of the key
  • Then divide the array into two sub-arrays from the key value, and perform fast sort recursion on the two sub-arrays respectively
  • The key value takes the first, middle, and last median to improve efficiency
  • The front and back pointer method first puts the median keyword at the first place, and then compares the value of the pointer with the first keyword every time, and finally returns to front
  • In order to improve efficiency, direct insertion sorting can be used when end - begin < n, and fast sorting recursion can be used instead of recursive n.

Time complexity Unstable average complexity is O(n * lg(n))

void Swap(int* arr, int pi, int pj) 
{
    int tmp = arr[pi];
    arr[pi] = arr[pj];
     arr[pj] = tmp;
}

//取中位数
int MidNum(int* arr, int l, int m, int r) 
{
    if (arr[l] < arr[r]) 
    {
        if (arr[m] < arr[l])
            return l;
        else if (arr[m] < arr[r])
            return m;
        else
            return r;
    }
    else 
    {
        if (arr[m] > arr[l])
            return l;
        else if (arr[m] > arr[r])
            return m;
        else
            return r;
    }
}

//前后指针法
int PtrPrt(int* arr, int begin, int end) 
{
    int mid = (begin + end) / 2;
    int keyi = MidNum(arr, begin, mid, end);
    Swap(arr, keyi, begin);
    int front = begin;
    int back = begin + 1;
    while (back <= end) 
    {
        if (arr[back] < arr[begin] && ++front != back) 
            Swap(arr, front, back);
        ++back;
    }
    Swap(arr, begin, front);
    return front;
}

void QuickSort(int* arr, int begin, int end) 
{
    if (begin >= end) 
        return;
    int keyi = PtrPrt(arr, begin, end);
    QuickSort(arr, begin, keyi - 1);
    QuickSort(arr, keyi + 1, end);
}

5. Counting and sorting

train of thought

  • Hash ideas use space for time to find the maximum value max and minimum value min in the arr array
  • Create a count array of (max - min + 1) elements and zero it to count the number of occurrences of each number in the arr array
  • Traversing the original array count[arr[i] - min]++ can use count to record the number of occurrences of each number in the original array
  • Then traverse the count array from small to large and write the value into the arr array to complete the sorting
  • Applicable to the case where the maximum and minimum values ​​in the arr array are not much different

The complexity is unstable between O(n) ~ O(n^2)

void CountSort(int* arr, int size) 
{
    int min = arr[0], max = arr[0];
    int i;
    for (i = 0; i < size; ++i) 
    {
        if (arr[i] < min) 
            min = arr[i];
        if (arr[i] > max) 
            max = arr[i];
    }

    int* count = (int*)malloc(sizeof(int) * (max - min + 1));
    memset(count, 0, max - min + 1);
    for (i = 0; i < size; ++i) 
    {
        count[arr[i] - min]++;
    }

    int j = 0;
    for (i = 0; i < max - min + 1; ++i) 
    {
        while (count[i]--) 
        {
            arr[j++] = i + min;
        }
    }
}

6. Radix sorting

train of thought

  • For code convenience, use C++ to send and receive data using queues
  • Create a queue array. The size of the array is 10. Each element is a number stored in a queue and modulo 1 ~ 9.
  • Complete sorting of data transmission and reception from low to high
  • Applicable to situations where the number of data bits is not high

The time complexity is related to the maximum number of digits K K * O(n) so the time complexity is O(n)

#include <iostream>
#include <queue>
using namespace std;

#define K 3     //数组中最大数据的位数
#define RADIX 10

//定义基数
queue<int> g_q[RADIX];

int GetKey(int val, int k) 
{
    int key = val % 10;
    while (k--) 
    {
        val /= 10;
        key = val % 10;
    }
    return key;
}

void Distribute(int* arr, int left, int right, int k) 
{
    for (int i = left; i <= right; ++i) 
    {
        int key = GetKey(arr[i], k);
        g_q[key].push(arr[i]);
    }
}

void Collect(int* arr) 
{
    int j = 0;
    for (int i = 0; i < RADIX; ++i) 
    {
        while (!g_q[i].empty()) 
        {
            arr[j++] = g_q[i].front();
            g_q[i].pop();
        }
    }
}

void RadixSort(int* arr, int size) 
{
    for (int i = 0; i < K; ++i) 
    {
        Distribute(arr, 0, size - 1, i);    //分发数据
        Collect(arr);       //收集数据
    }
}

The benefits of this article, free C++ learning materials package, technical video/code, 1000 Dachang interview questions, including (C++ basics, network programming, database, middleware, back-end development, audio and video development, Qt development) ↓↓↓ ↓↓↓See below↓↓Click at the bottom of the article to get it for free↓↓

Guess you like

Origin blog.csdn.net/m0_60259116/article/details/131743470