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

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

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

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

三、小结

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

相关推荐
杜子不疼.1 天前
【C++ AI 大模型接入 SDK】 - DeepSeek 模型接入(上)
开发语言·c++·chatgpt
石山代码1 天前
C++ 内存分区 堆区
java·开发语言·c++
心中有国也有家1 天前
cann-recipes-infer:昇腾 NPU 推理的“菜谱集合”
经验分享·笔记·学习·算法
绝知此事1 天前
【算法突围 01】线性结构与哈希表:后端开发的收纳术
java·数据结构·算法·面试·jdk·散列表
碧海银沙音频科技研究院1 天前
通话AEC与语音识别AEC的软硬回采链路
深度学习·算法·语音识别
csdn_aspnet1 天前
Python 算法快闪 LeetCode 编号 70 - 爬楼梯
python·算法·leetcode·职场和发展
LuminousCPP1 天前
数据结构 - 线性表第四篇:C 语言通讯录优化升级全记录(踩坑 + 思考)
c语言·开发语言·数据结构·经验分享·笔记·学习
张小姐的猫1 天前
【Linux】多线程 —— 线程互斥
linux·运维·服务器·c++
AI算法沐枫1 天前
深度学习python代码处理科研测序数据
数据结构·人工智能·python·深度学习·决策树·机器学习·线性回归