基础算法-冒泡排序,插入排序,选择排序

Posted 厦门不渝

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了基础算法-冒泡排序,插入排序,选择排序相关的知识,希望对你有一定的参考价值。

本文图片来在极客时间-王争老师的《数据结构与算法之美》

冒泡排序(Bubble Sort)

算法说明

每次冒泡操作都会对相邻的两个元素进行比较,看是否满足大小关系要求。如果不满足就让它俩互换。一次冒泡会让至少一个元素移动到它应该在的位置,重复 n 次,就完成了 n 个数据的排序工作。

例如:要对一组数据 4,5,6,3,2,1,从小到大进行排序。第一次冒泡操作的详细过程如下: 

步骤说明

假设要对N个元素进行冒泡排序,执行的步骤如下:

  1. 比较相邻的元素。如果前者>后者,则交换两者的位置。

  2. 按顺序对每一组相邻元素做步骤1的工作,直到结束(需要执行了N-1次比较工作)。

  3. 以此类推,执行N-1次的步骤2,即可完成冒泡排序。

JAVA代码

package cn.hgy.data.algorithm;

package cn.hgy.data.algorithm;

/**
* 冒泡排序
* <pre>
* 算法介绍:遍历待排序的数,每次选出最大的数添加到合适的位置
* 时间复杂度:最优-O(n),最坏-O(n^2),
* </pre>
*
* @author guoyu.huang
* @version 1.0.0
*/

public class BubbleSort {

public static void main(String[] args) {
int[] arrays = {4, 5, 6, 3, 2, 1};
int[] sortArrays = sort(arrays);

for (int i = 0; i < sortArrays.length; i++) {
System.out.print(sortArrays[i] + " ");
}
}

/**
* 排序
* <pre>
* 两个优化点:
* 1. 如果进行一轮的比较遍历后,没有发生过替换操作,则表示当前已是有序状态
* 2. 每进行一轮比较遍历后,在进行第i轮之后,数组尾部的n-in的数是有序的,无需再判断
* </pre>
*
* @param content
*/

public static int[] sort(int[] content) {

for (int i = 0; i < content.length; i++) {
// 优化点1:有序性标识符
boolean ordinalFlag = true;

// 优化点2:在进行i轮之后,数组尾部的n-in的数是有序的
for (int j = 0; j < content.length - i - 1; j++) {
if (content[j] > content[j + 1]) {
ordinalFlag = false;
int temp = content[j + 1];
content[j + 1] = content[j];
content[j] = temp;
}
}

// 优化点1:进行一轮的比较遍历后,没有发生过替换操作,则表示当前已是有序状态
if (ordinalFlag) {
break;
}
}
return content;
}

}

插入排序(InsertionSort)

算法说明

我们将待排序的元素分为两个区间,已排序区间和未排序区间。插入算法的核心思想是取未排序区间中的元素,在已排序区间中找到合适的插入位置将其插入,并保证已排序区间的元素一直有序。

例如:要对一组数据 4,5,6,3,2,1,从小到大进行排序。插入排序的详细过程如下: 基础算法-冒泡排序,插入排序,选择排序

步骤说明

假设要对N个元素进行插入排序,执行的步骤如下:

  1. 第一次取第一个元素作为已排序区间,后续的N-1个元素作为未排序区间。

  2. 取未排序区间的第一个元素,在已排序区间找到合适的位置,并保证已排序区间还是有序的。

  3. 以此类推,执行N-1次的步骤2,即可完成插入排序。

JAVA代码

package cn.hgy.data.algorithm;

/**
* 插入排序
* <pre>
* 算法介绍:每次从未排序中选择第一个数添加到已排序的合适位置
* 时间复杂度:最优O(n),最坏O(n^2)
* </pre>
*
* @author guoyu.huang
* @version 1.0.0
*/

public class InsertionSort {

public static void main(String[] args) {
int[] arrays = {4, 5, 6, 3, 2, 1};
int[] sortArrays = sort(arrays);

for (int i = 0; i < sortArrays.length; i++) {
System.out.print(sortArrays[i] + " ");
}
}

/**
* 排序
* <pre>
* 优化点:
* 1. 如果待排序的元素大于有序区的最大元素则直接在尾部插入
* 2. 等已排序区间的元素都移动好位置,再将元素插入指定位置,减少赋值操作
* </pre>
*
* @param content
*/

public static int[] sort(int[] content) {
for (int i = 0; i < content.length; i++) {
int temp = content[i];
// 优化点2:记录待插入的位置
int j = i - 1;
for (; j >= 0; j--) {
if (content[j] > temp) {
content[j + 1] = content[j];
} else {
// 优化点1:如果待排序的元素大于有序区的最大元素则直接在尾部插入
break;
}
}
// 优化点2:将元素插入指定位置,减少赋值操作
content[j + 1] = temp;
}
return content;
}

}

选择排序

算法说明

将待排序的元素分为两个区间,已排序区间和未排序区间。选择排序每次会从未排序区间中找到最小的元素,将其放到已排序区间的末尾。

例如:要对一组数据 4,5,6,3,2,1,从小到大进行排序。选择排序的详细过程如下: 基础算法-冒泡排序,插入排序,选择排序

步骤说明

假设要对N个元素进行选择排序,执行的步骤如下:

  1. 所有元素都在未排序区间

  2. 找出最小的数和未排序区间的第一个元素进行位置替换。

  3. 以此类推,执行N-1次的步骤2,即可完成选择排序。

JAVA代码

package cn.hgy.data.algorithm;

/**
* 选择排序
* <pre>
* 算法介绍:每次会从未排序区间中找到最小的元素,将其放到已排序区间的末尾
* 时间复杂度:最优O(n),最坏O(n^2)
* </pre>
*
* @author guoyu.huang
* @version 1.0.0
*/

public class SelectionSort {

public static void main(String[] args) {
int[] arrays = {4, 5, 6, 3, 2, 1};
int[] sortArrays = sort(arrays);

for (int i = 0; i < sortArrays.length; i++) {
System.out.print(sortArrays[i] + " ");
}
}

/**
* 排序
*
* @param content
*/

public static int[] sort(int[] content) {
for (int i = 0; i < content.length; i++) {
int min = content[i];
int index = i;
// 每次从未排序中找出最小的值,将其插入已排序数组的尾部
for (int j = i; j < content.length; j++) {
if (content[j] < min) {
index = j;
min = content[j];
}
}
content[index] = content[i];
content[i] = min;
}
return content;
}
}

总结

分析维度一:时间复杂度

冒泡排序

  • 最好时间复杂度:O(n)

  • 最坏时间复杂度:O(n^2)

  • 平均时间复杂度:O(n^2)

插入排序

  • 最好时间复杂度:O(n)

  • 最坏时间复杂度:O(n^2)

  • 平均时间复杂度:O(n^2)

选择排序

  • 最好时间复杂度:O(n^2)

  • 最坏时间复杂度:O(n^2)

  • 平均时间复杂度:O(n^2)

分析维度二:空间复杂度

冒泡排序

O(1)

插入排序

O(1)

选择排序

O(1)

分析维度三:是否稳定

稳定排序是指在原有的顺序上,如果满足条件,还能够保留原有的顺序。

例如:订单按金额和时间来排序,如果是稳定的排序算法,先按时间排序之后,再按金额排序。即可实现,如下图: 

冒泡排序

在比较替换的代码中,只处理大于的数据,相等的数据保持位置不变,所以是稳定的。

插入排序

在比较替换的代码中,只处理大于的数据,相等的数据保持位置不变,所以是稳定的。

选择排序

选择排序应该会互换元素之间的位置,所以会导致原有的顺序发生变化,所以不算稳定排序。

例如:5,8,5,2,9 这样一组数据,使用选择排序算法来排序的话,第一次找到最小元素 2,与第一个 5 交换位置,那第一个 5 和中间的 5 顺序就变了,所以就不稳定了。

冒泡排序 vs 插入排序

既然时间复杂度,空间复杂度一样,也都是稳定排序。那选择哪种排序更好呢?查看代码可知,一次交换中,插入排序的赋值操作为1次,冒泡排序的赋值操作为3次。插入排序更好一些。

 


以上是关于基础算法-冒泡排序,插入排序,选择排序的主要内容,如果未能解决你的问题,请参考以下文章

算法基础——1.排序

排序算法之冒泡选择插入排序(Java)

排序算法之冒泡选择插入排序(Java)

基础排序算法,java实现(快速,冒泡,选择,堆排序,插入)

php四种基础算法:冒泡,选择,插入和快速排序法

python实现时间复杂度平均为N^2的冒泡选择插入排序