排序算法详解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)

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

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

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

三、小结

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

相关推荐
cm6543202 小时前
C++代码切片分析
开发语言·c++·算法
Lhan.zzZ2 小时前
深入浅出 Qt 信号槽连接方式:从 AutoConnection 到 BlockingQueuedConnectionQt
开发语言·c++·qt
重生之我是Java开发战士2 小时前
【递归、搜索与回溯】FloodFill算法:图像渲染,岛屿数量,岛屿的最大面积,被围绕的区域,太平洋大西洋水流问题,扫雷游戏,衣橱整理
算法·leetcode·深度优先
YUANQIANG20242 小时前
PPO算法典型思路
算法·机器学习
twc8292 小时前
大模型评估指标简要说明
算法·大模型·bleu
淀粉肠kk2 小时前
【C++】C++11可变参数模板和emplace系列接口
算法
草莓熊Lotso2 小时前
MySQL 内置函数指南:日期、字符串、数学函数实战
android·java·linux·运维·数据库·c++·mysql
ab1515172 小时前
3.21二刷基础125、122、130,完成进阶65
开发语言·c++·算法
j_xxx404_2 小时前
力扣--分治(快速排序)算法题I:颜色分类,排序数组
数据结构·c++·算法·leetcode·排序算法