【数据结构】八大排序(上)

简介: 【数据结构】八大排序(上)

排序的基础知识


1.排序的相关概念

排序:所谓排序,就是使一串记录,按照其中的某个或某些关键字的大小,递增或递减的排列起来的操作。


稳定性:假定在待排序的记录序列中,存在多个具有相同的关键字的记录,若经过排序,这些记录的相对次序保持不变,即在原序列中,r[i]=r[j],且r[i]在r[j]之前,而在排序后的序列中,r[i]仍在r[j]之前,则称这种排序算法是稳定的;否则称为不稳定的。


内部排序:数据元素全部放在内存中的排序。


外部排序:数据元素太多不能同时放在内存中,根据排序过程的要求不能在内外存之间移动数据的排序。


比较排序:通过比较两个元素的大小来确定元素在内存中的次序,我们常见的入选择排序、插入排序、比较排序与归并排序都属于比较排序。


非比较排序:通过确定每个元素之前,应该有多少个元素来排序,常见的非比较排序有基数排序、计数排序与桶排序。


2.常见的排序种类

b14e6a025511bb4417b60161f8f05cbc.png


3.排序的应用

排序在我们的日常生活中是非常常见的,比如我们在京东/淘宝上购物商品时,可以选择按销量排序、按价格排序、按评论排序,又比如世界500强企业,中国前50所高校等等,这些地方都需要用到排序。

3c3559dadb8e181fa632f56bff34ccb6.png


dc0d5bba1d201939f742e17cd7ff71f4.png


常见排序的算法实现

注:以下排序全部基于升序举例


1.直接插入排序


1.排序思想

直接插入排序是一种简单的插入排序法,其基本思想是:把待排序的记录按其关键码值的大小逐个插入到一个已经排好序的有序序列中,直到所有的记录插入完为止,得到一个新的有序序列

实际中我们玩扑克牌时,就用了插入排序的思想

54c3700e3985c36c5b431d37660f0281.png

动图演示

fcdcc476f5418d0938ebdd7181f482e0.gif

2.代码实现

void InsertSort(int* a, int n)
{
  //假设[0,end]有序,要排序的是[0,n]
  for (int i = 0; i < n - 1; i++)
  {
    int end = i;
    int tmp = a[end + 1];
    while (end >= 0)
    {
      if (a[end] > tmp)
      {
        a[end + 1] = a[end];
        --end;
      }
      else
      {
        break;
      }
    }
    a[end + 1] = tmp;
  }
}


注意:我们实现代码的时候假设[0,end]是有序的,然后将end+1的值插入到其中,使[0,end+1]有序,上述代码中,for循环里面我们让tmp保存的是数组中下标为end+1的值,end是i,所以当end=n-1的时候,tmp保存的值就已经越界了,所以我们的循环条件设置为i < n - 1。


3.复杂度

时间复杂度

最坏情况:每次插入的新数据最后都需要插入在下标为0的位置上,此时每个单趟排序的时间复杂度为end,所以一共要挪动1+2+3+…+n-1次,所以最坏时间复杂度为O(N2).

最好情况:每次插入新的数据的时候,都不需要挪动,此时的时间复杂度为O(N).

结论:直接插入排序的时间复杂度为O(N)


空间复杂度

由于没有开辟新的空间,因此空间复杂度为O(1).


4.特性总结

  1. 元素集合越接近有序,直接插入排序算法的时间效率越高
  2. 时间复杂度:O(N^2)
  3. 空间复杂度:O(1)
  4. 稳定性:稳定


2.希尔排序(缩小增量排序)


1.排序思想

希尔排序是直接插入排序的优化。它分为两个步骤

  1. 预排序 目的:接近有序
  2. 直接插入排序


预排序的时候,需要选定一个gap,然后把相隔gap距离的元素分为一组,一共分为gap组,对每一组进行排序,然后重复操作,当gap为1的时候就是直接插入排序。


希尔排序相较于直接插入排序优化的点:对于需要排序数据量特别大,数据特别乱的数据,预排序会让数据更快趋近于有序,当gap==1的时候就是直接插入排序。

9cc52e9ae9eed82dc0e32c73c993951a.png

2.代码实现

void ShellSort(int* a, int n)
{
  int gap = 3;
  while (gap > 1)
  {
    gap = gap / 3 + 1;
    //假设[0,end]有序,插入a[end+gap]保持有序
    for (int i = 0; i < n - gap; i++)
    {
      int end = i;
      int tmp = a[end + gap];
      while (end >= 0)
      {
        if (a[end] > tmp)
        {
          a[end + gap] = a[end];
          end -= gap;
        }
        else
        {
          break;
        }
        a[end + gap] = tmp;
      }
    }
  }
}


注意:希尔排序中gap的取值时不确定的,我们这里对gap的处理是初始化为n,然后除3加1,这就意味着如果按照最坏的情况来看,第一次预排序的时候一个元素只需要3次就可以达到它应该在的位置附近,加1是为了保证最后一次gap的值为1,当gap==1时,就是直接插入排序。


3.复杂度

时间复杂度

希尔排序的时间复杂度不好计算,因为gap的取值方法很多,导致很难去计算,因此在很多书中给出的希尔排序的时间复杂度都不固定:

《数据结构(C语言版)》— 严蔚敏

37aa8861e44c013c209ac9e8dcdc367e.png

《数据结构-用面相对象方法与C++描述》— 殷人昆

5ecacebe7052ec4d78380c40a9d73a2a.png

由于我们这里对gap的处理是按照Knuth提出的方式取值的,而且Knuth进行了大量的试验统计,我们暂时就按照:O(N1.25)

到O(1.6*N1.25)来算。


空间复杂度

由于没有开辟新的空间,因此空间复杂度为O(1).


4.特性总结

  1. 希尔排序是对直接插入排序的优化。
  2. 当gap > 1时都是预排序,目的是让数组更接近于有序。当gap == 1时,数组已经接近有序的了,这样就会很快。这样整体而言,可以达到优化的效果。
  3. 时间复杂度O(N1.25)到O(1.6*N1.25)
  4. 稳定性:不稳定
相关文章
|
11天前
|
存储 人工智能 算法
【C++数据结构——内排序】二路归并排序(头歌实践教学平台习题)【合集】
本关任务是实现二路归并算法,即将两个有序数组合并为一个有序数组。主要内容包括: - **任务描述**:实现二路归并算法。 - **相关知识**: - 二路归并算法的基本概念。 - 算法步骤:通过比较两个有序数组的元素,依次将较小的元素放入新数组中。 - 代码示例(以 C++ 为例)。 - 时间复杂度为 O(m+n),空间复杂度为 O(m+n)。 - **测试说明**:平台会对你编写的代码进行测试,提供输入和输出示例。 - **通关代码**:提供了完整的 C++ 实现代码。 - **测试结果**:展示代码运行后的排序结果。 开始你的任务吧,祝你成功!
30 10
|
11天前
|
搜索推荐 算法 数据处理
【C++数据结构——内排序】希尔排序(头歌实践教学平台习题)【合集】
本文介绍了希尔排序算法的实现及相关知识。主要内容包括: - **任务描述**:实现希尔排序算法。 - **相关知识**: - 排序算法基础概念,如稳定性。 - 插入排序的基本思想和步骤。 - 间隔序列(增量序列)的概念及其在希尔排序中的应用。 - 算法的时间复杂度和空间复杂度分析。 - 代码实现技巧,如循环嵌套和索引计算。 - **测试说明**:提供了测试输入和输出示例,帮助验证代码正确性。 - **我的通关代码**:给出了完整的C++代码实现。 - **测试结果**:展示了代码运行的测试结果。 通过这些内容,读者可以全面了解希尔排序的原理和实现方法。
42 10
|
11天前
|
搜索推荐 C++
【C++数据结构——内排序】快速排序(头歌实践教学平台习题)【合集】
快速排序是一种高效的排序算法,基于分治策略。它的主要思想是通过选择一个基准元素(pivot),将数组划分成两部分。一部分的元素都小于等于基准元素,另一部分的元素都大于等于基准元素。然后对这两部分分别进行排序,最终使整个数组有序。(第一行是元素个数,第二行是待排序的原始关键字数据。本关任务:实现快速排序算法。开始你的任务吧,祝你成功!
30 7
|
3月前
|
算法 搜索推荐 Java
数据结构与算法学习十三:基数排序,以空间换时间的稳定式排序,速度很快。
基数排序是一种稳定的排序算法,通过将数字按位数切割并分配到不同的桶中,以空间换时间的方式实现快速排序,但占用内存较大,不适合含有负数的数组。
50 0
数据结构与算法学习十三:基数排序,以空间换时间的稳定式排序,速度很快。
|
3月前
|
存储 搜索推荐 算法
【用Java学习数据结构系列】七大排序要悄咪咪的学(直接插入,希尔,归并,选择,堆排,冒泡,快排)以及计数排序(非比较排序)
【用Java学习数据结构系列】七大排序要悄咪咪的学(直接插入,希尔,归并,选择,堆排,冒泡,快排)以及计数排序(非比较排序)
38 1
|
3月前
|
搜索推荐 索引
【初阶数据结构】深度解析七大常见排序|掌握底层逻辑与原理(二)
【初阶数据结构】深度解析七大常见排序|掌握底层逻辑与原理
|
3月前
|
搜索推荐 C++
【初阶数据结构】深度解析七大常见排序|掌握底层逻辑与原理(一)
【初阶数据结构】深度解析七大常见排序|掌握底层逻辑与原理
|
3月前
|
算法
蓝桥杯宝藏排序 | 数据结构 | 快速排序 归并排序
蓝桥杯宝藏排序 | 数据结构 | 快速排序 归并排序
05_用一个栈实现另一个栈的排序
05_用一个栈实现另一个栈的排序
|
3月前
|
人工智能 搜索推荐 算法
【初阶数据结构】深度解析七大常见排序|掌握底层逻辑与原理(三)
【初阶数据结构】深度解析七大常见排序|掌握底层逻辑与原理

热门文章

最新文章