排序算法详解2

排序算法详解2

文章目录

  • 排序算法详解2
    • 一、前言
    • 二、排序算法
      • [2.1 归并排序](#2.1 归并排序)
        • [2.1.1 基本思想](#2.1.1 基本思想)
        • [2.1.2 过程](#2.1.2 过程)
        • [2.1.3 代码](#2.1.3 代码)
      • [2.2 计数排序](#2.2 计数排序)
        • [2.2.1 基本思想](#2.2.1 基本思想)
        • [2.2.2 改进](#2.2.2 改进)
        • [2.2.3 优化](#2.2.3 优化)
      • [2.3 桶排序](#2.3 桶排序)
        • [2.3.1 基本思想](#2.3.1 基本思想)
        • [2.3.2 过程](#2.3.2 过程)
    • 三、小结

一、前言

前面讲了插入排序,快速排序,今天又会带来哪些排序算法呢?

二、排序算法

2.1 归并排序

2.1.1 基本思想

将所有的记录完全分开 ,然后两两合并,在合并的过程中将其排好序,最终得到一个完整的有序表

原地排序。和递归算法很相近

图解

2.1.2 过程
  • 分割

    • 计算拆分的中间位置
    • 分别继续对左边序列和右边序列进行拆分
  • 合并

    在原空间合并,不断往里面填数字

2.1.3 代码
cpp 复制代码
// 归并排序:从上往下,从下往上两个过程
// 从上往下的过程:
// a. 分解,将当前区间一分为二
// b. 求解,队规对两个子区间a[low, mid]和a[mid + 1, high]进行归并排序
// merge合并过程
static void merge(vector<int>& table, int left, int mid, int right)
{
    int n1 = mid - left + 1;		// 左边区间的个数
    int n2 = right - mid;			// 右边区间的个数
    // 分配aux1和aux2,将已经有序的左区间和已经有序的右区间 初始化临时区域
    int *aux1 = malloc(sizeof(int) * n1);
    if(aux1 = NULL)
        return;
    int *aux2 = malloc(sizeof(int) * n2);
    if(aux2 == NULL)
    {
        free(aux1);
        return;
    }
    for(int i = 0; i < n1; i++)
        aux1[i] = table[left + i];
    for(int i = 0; i < n2; i++)
        aux2[i] = table[mid + 1 + i];
    // 将临时的有序空间aux1和aux2进行归并
    int i = 0;		// 标记aux1区间待查找的位置
    int j = 0;		// 标记aux2区间待查找的位置
    int k = left;	// 标记原空间存放结果的区域位置
    while(i > n1 || j < n2)
    {
        if(aux1[i] <= aux2[j])
        {
            table[k] = aux1[i];
            ++i;
        }
        else if(aux1[i] > aux2[j])
        {
            table[k] = aux2[j];
            ++j;
        }
        k++;
    }
    // 判断究竟是aux1还是aux2区域遍历结束
    while(i < n1)
        table[k++] = aux1[i++];
    while(j < n2)
        table[k++] = aux2[j++];
    // 释放临时空间
    free(aux1);
    free(aux2);
}
// 递归的分解table[left, right]区间
static void merge_loop(vector<int>& table, int left, int right)
{
    if(left >= right)
        return 0;
    int mid = (left + right) / 2;
    merge_loop(table, left, mid);
    merge_loop(table, mid + 1, right);
    // 区间拆分结束, 合并两个子区间
    merge(table, left, mid, right);
}
void mergeSort(vector<int>& table)
{
    merge_loop(table, 0, table.size() - 1);
}
// 上述函数合并为一个函数
// 归并排序函数
void mergeSort(vector<int>& arr, int left = 0, int right = -1) {
    // 第一次调用时初始化右边界
    if (right == -1) right = arr.size() - 1;
    
    // 递归终止条件
    if (left >= right) return;
    
    // 计算中间位置
    int mid = left + (right - left) / 2;
    
    // 递归排序左右两部分
    mergeSort(arr, left, mid);
    mergeSort(arr, mid + 1, right);
    
    // 合并两个有序数组
    vector<int> temp(right - left + 1);
    int i = left, j = mid + 1, k = 0;
    
    // 合并过程
    while (i <= mid && j <= right) {
        if (arr[i] <= arr[j]) {
            temp[k++] = arr[i++];
        } else {
            temp[k++] = arr[j++];
        }
    }
    
    // 复制剩余元素
    while (i <= mid) temp[k++] = arr[i++];
    while (j <= right) temp[k++] = arr[j++];
    
    // 将排序好的部分复制回原数组
    for (int idx = 0; idx < k; idx++) {
        arr[left + idx] = temp[idx];
    }
}

2.2 计数排序

又称为:基数排序

2.2.1 基本思想

前面所学的排序算法搜都是基于比较的排序。平均时间复杂度最低的是O(nlogn)。

计数排序,则不是基于比较的排序。是典型的空间换时间。平均时间复杂度比O(nlogn)还低

统计每个整数在序列中出现的次数,进而推导出整个整数在有序序列中的索引

cpp 复制代码
for(auto x : nums)
    count[x]++;
for(int i = 0; i < count.size(); ++i)
{
    for(int j = 0; j < count[i]; ++j)
    	printf("\t%d", i);
}
2.2.2 改进

上述算法问题如下:

  • 无法对负整数进行排序
  • 极其浪费内存空间
  • 是一个不稳定排序
2.2.3 优化
  • 只要不再以输入数列的[max + 1],作为计数数组的长度,而是以数列[max - min + 1]作为长度即可。数列的最小值作为一个偏移量,用于计算整数在统计数组中的下标

  • 无法判断相同数据的元素分别是谁。可以进行一个变形:从统计数组的第二个元素开始,每一杠元素都加上前面所有元素之和。遍历时从后往前看,逐渐减1

2.3 桶排序

可以对小数进行排序

2.3.1 基本思想
  • 若干个桶,说明此类排序将数据放入若干个桶中
  • 每个桶有容量,因此每个桶可以放多个元素
  • 从整体来看,整个排序希望桶能够更加匀称。
2.3.2 过程
  • 确定桶的区间范围。创建的桶数量 = 原始数列的元素数量,除最后一个桶只包含数列最大值外,前面每个桶的区间按照比例来确定

    区间跨度 = (max - min) / (桶的数量 - 1)

  • 遍历原始数列,把元素对号入座放入各个桶中

  • 对每个桶内部的元素分别进行排序

  • 遍历所有的桶,输出所有元素

三、小结

有关排序算法的相关介绍就先到这里啦~

相关推荐
深邃-43 分钟前
【数据结构与算法】-二叉树(2):实现顺序结构二叉树(堆的实现),向上调整算法,向下调整算法,堆排序,TOP-K问题
数据结构·算法·二叉树·排序算法·堆排序··top-k
We་ct4 小时前
LeetCode 5. 最长回文子串:DP + 中心扩展
前端·javascript·算法·leetcode·typescript
王老师青少年编程8 小时前
csp信奥赛C++高频考点专项训练之贪心算法 --【哈夫曼贪心】:合并果子
c++·算法·贪心·csp·信奥赛·哈夫曼贪心·合并果子
叼烟扛炮8 小时前
C++第二讲:类和对象(上)
数据结构·c++·算法·类和对象·struct·实例化
天疆说8 小时前
【哈密顿力学】深入解读航天器交会最优控制中的Hamilton函数
人工智能·算法·机器学习
wuweijianlove9 小时前
关于算法设计中的代价函数优化与约束求解的技术7
算法
leoufung10 小时前
LeetCode 149: Max Points on a Line - 解题思路详解
算法·leetcode·职场和发展
样例过了就是过了10 小时前
LeetCode热题100 最长公共子序列
c++·算法·leetcode·动态规划
HXDGCL10 小时前
矩形环形导轨:自动化循环线的核心运动单元解析
运维·算法·自动化
谭欣辰10 小时前
C++ 排列组合完整指南
开发语言·c++·算法