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

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

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

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

三、小结

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

相关推荐
哥不想学算法2 小时前
【C++】字符串字面量拼接
开发语言·c++
海石2 小时前
1500分的题目,确实有实力,不过还是我略胜一筹
算法·leetcode
海石2 小时前
【记忆化搜索】条条大路通AC,走好适合你的那一条,走到后再考虑走得快
算法·leetcode
Jerry4 小时前
LeetCode 151. 反转字符串中的单词
算法
gugucoding5 小时前
31. 【C语言】堆栈与队列的实现
c语言·开发语言·数据结构·链表
ChaoZiLL6 小时前
我的数据结构3——链表(link list)
数据结构·链表
a1117767 小时前
LM 算法迭代过程动画演示(SLAM)
算法
头茬韭菜7 小时前
Context 的生死抉择:四层压缩、截断算法与 Session Memory
算法·ai
Jerry7 小时前
LeetCode 541. 反转字符串 II
算法
Jerry7 小时前
LeetCode 344. 反转字符串
算法