【基础篇】9 # 排序:冒泡排序(Bubble Sort)、插入排序(Insertion Sort)、选择排序(Selection Sort)

简介: 【基础篇】9 # 排序:冒泡排序(Bubble Sort)、插入排序(Insertion Sort)、选择排序(Selection Sort)

说明

【数据结构与算法之美】专栏学习笔记




如何分析一个排序算法


1、排序算法的执行效率

   最好情况、最坏情况、平均情况时间复杂度

   时间复杂度的系数、常数 、低阶

   比较次数和交换(或移动)次数


2、排序算法的内存消耗


3、排序算法的稳定性

原地排序(Sorted in place):就是特指空间复杂度是 O(1) 的排序算法。


稳定性度量指标:如果待排序的序列中存在值相等的元素,经过排序之后,相等元素之间原有的先后顺序不变,这种排序算法叫作稳定的排序算法;如果前后顺序发生变化,那对应的排序算法就叫作不稳定的排序算法。



冒泡排序(Bubble Sort)

冒泡排序是一种简单的排序算法,它重复地走访过要排序的数列,每次冒泡操作都会对相邻的两个元素进行比较,看是否满足大小关系要求。如果不满足就让它俩互换。

// 冒泡排序,a表示数组,n表示数组大小
public void bubbleSort(int[] a, int n) {
  if (n <= 1) return;
 for (int i = 0; i < n; ++i) {
    // 提前退出冒泡循环的标志位
    boolean flag = false;
    for (int j = 0; j < n - i - 1; ++j) {
      if (a[j] > a[j+1]) { // 交换
        int tmp = a[j];
        a[j] = a[j+1];
        a[j+1] = tmp;
        flag = true;  // 表示有数据交换      
      }
    }
    if (!flag) break;  // 没有数据交换,提前退出
  }
}


可以查看排序的可视化演示


e83767f92ef7446282684a21edb158f6.gif




有序度


有序度是数组中具有有序关系的元素对的个数。

有序元素数学表达式表示如下:

有序元素对:a[i] <= a[j], 如果 i < j。


比如:2,4,3,1,5,6,这组数据

有序元素有:

(2, 4) (2, 3) (2, 5) (2, 6)
(4, 5) (4, 6)
(3, 5) (3, 6)
(1, 5) (1, 6)
(5, 6)


所以上面数据组的有序度就是11。

比如 6,5,4,3,2,1,有序度是 0;对于一个完全有序的数组,有序度就是 n(n-1)/2*。全有序的数组的有序度叫作满有序度



逆序度


逆序元素对:a[i] > a[j], 如果i < j。


逆序度 = 满有序度 - 有序度

下面用 4,5,6,3,2,1 这组数据来对比一下冒泡过程的有序度跟逆序度:

image.png


冒泡排序包含两个操作原子,比较和交换。每交换一次,有序度就加 1。对于包含 n 个数据的数组进行冒泡排序,平均情况下,需要 n*(n-1)/4 次交换操作,平均情况下的时间复杂度就是 O(  n^2 )。



插入排序(Insertion Sort)


插入排序是一种简单的排序算法,它的基本思想是:将一个记录插入到已排好序的有序表中,从而得到一个新的记录数增1的有序表。


插入算法的核心思想:将数组中的数据分为已排序区间和未排序区间两个区间,然后初始已排序区间只有一个元素,就是数组的第一个元素。再取未排序区间中的元素,在已排序区间中找到合适的插入位置将其插入,并保证已排序区间数据一直有序。重复这个过程,直到未排序区间中元素为空,算法结束。


例子:4,5,6,1,3,2 数据进行插入排序,逆序数为10。


4b67d5c29761431fa64c67ff53212b4d.png


插入排序包含两种操作,一种是元素的比较,一种是元素的移动,移动操作的次数总是固定的,就等于逆序度。

// 插入排序,a表示数组,n表示数组大小
public void insertionSort(int[] a, int n) {
  if (n <= 1) return;
  for (int i = 1; i < n; ++i) {
    int value = a[i];
    int j = i - 1;
    // 查找插入的位置
    for (; j >= 0; --j) {
      if (a[j] > value) {
        a[j+1] = a[j];  // 数据移动
      } else {
        break;
      }
    }
    a[j+1] = value; // 插入数据
  }
}


有点晕的同学可以查看排序的可视化演示,选择插入排序,就能清晰的看到过程效果


c58300e59f12471e8ac4e3f8dd47f20f.gif



现实生活里就有这样的例子,打扑克的时候,整理牌用到的思路其实就是插入排序,牌堆就是未排序区间,手中已摸的牌就是已排序区间。每一次抽牌,就根据摸到的牌的大小从左往右,或者从右往左对比,放到恰当的位置。



选择排序(Selection Sort)


选择排序算法的实现思路有点类似插入排序,但选择排序每次会从未排序区间中找到最小的元素,将其放到已排序区间的末尾。


选择排序原理示意图:可以查看排序的可视化演示,选择选择排序,就能清晰的看到过程效果


337ab02ecb48477e923242c8fba5ef3e.gif



总结

总的来说,冒泡排序的数据交换要比插入排序的数据移动要复杂,相对于冒泡排序和插入排序,选择排序不稳定,就稍微逊色一点。

image.png









目录
相关文章
|
13天前
|
搜索推荐 算法 Java
sort-01-bubble sort 冒泡排序算法详解
这是一个关于排序算法的系列文章摘要。作者整理了10种不同的排序算法,包括冒泡排序、快速排序、选择排序、堆排序、插入排序、希尔排序、归并排序、计数排序、桶排序和大文件外部排序。文章详细介绍了冒泡排序的工作原理、流程,并提供了代码实现,强调了在实现中考虑的改进点,如统一接口、实用性增强和日志输出。此外,还提供了一个排序接口和工具类以方便使用,并通过测试代码和日志展示了排序过程。整个系列旨在帮助读者理解和掌握排序算法。相关代码已开源在GitHub。
|
13天前
|
搜索推荐 算法 Java
sort-03-SelectSort 选择排序算法详解
这是一个关于排序算法的系列文章摘要,包括了各种排序算法的介绍和Java实现。文章列举了冒泡排序、快速排序、选择排序、堆排序、插入排序、希尔排序、归并排序、计数排序、桶排序以及大文件外部排序的链接和简要说明。其中,选择排序算法被详细解释,它是通过找到未排序序列中的最小元素并将其放到正确位置来逐步构建有序序列的。Java实现中,选择排序的`doSort`方法通过两层循环实现,时间复杂度为`O(N^2)`,是稳定的排序算法。整个排序项目已开源在GitHub。
|
13天前
|
搜索推荐 算法 Java
sort-07-merge sort 归并排序
这是一个关于排序算法的系列文章摘要。文章涵盖了多种排序算法的详细解释,包括冒泡排序、快速排序、选择排序、堆排序、插入排序、希尔排序、归并排序、计数排序、桶排序以及大文件外部排序。归并排序是一种效率为O(nlogn)的排序算法,基于分治法,将序列分成两半,分别排序后再合并。文章提供了Java实现的递归和迭代版本。在归并排序的递归实现中,代码通过不断拆分和合并子序列完成排序,而迭代实现则是通过逐步增大子序列长度并进行两两归并来排序。整个系列可在GitHub找到相关源码。
|
13天前
|
搜索推荐 算法 Java
sort-08-counting sort 计数排序
这是一个关于排序算法的系列文章摘要。文章详细介绍了多种排序算法,包括冒泡排序、快速排序、选择排序、堆排序、插入排序、希尔排序、归并排序、计数排序、桶排序以及大文件外部排序。计数排序是一种线性时间复杂度的稳定排序算法,由 Harold H. Seward 在1954年提出。基础版计数排序通过创建一个与最大元素等长的新数组来统计每个元素出现的次数,然后填充排序结果。改良版则考虑了空间浪费问题,通过找出最小值来减少数组长度。此外,还提出了使用 TreeMap 来扩展排序算法以适应非数字元素的情况。
|
10月前
|
搜索推荐 算法 C#
C#选择排序(Selection Sort)算法
C#选择排序(Selection Sort)算法
|
10月前
|
存储 算法 搜索推荐
|
10月前
|
人工智能 算法 搜索推荐
|
11月前
|
存储 搜索推荐 算法
十大排序之Selection Sort 选择排序
十大排序之Selection Sort 选择排序
2-路插入排序(Two-Way Insertion Sort)
算法介绍 算法描述 算法分析 代码实现 参考
|
机器学习/深度学习 算法 搜索推荐
经典算法之冒泡排序(Bubble Sort)
经典算法之冒泡排序(Bubble Sort)
125 0
经典算法之冒泡排序(Bubble Sort)