冒泡、选择、插入、希尔排序
今天学习了八大算法中的冒泡、选择、插入、希尔排序算法。以数据从小到大排序为例,谈一谈自己的理解。有错误之处,还望指出,我会及时改正。
1. 冒泡排序:
冒泡排序每次将相邻元素进行比较,较大的数放在后面,每轮下来,最大的数都会被交换到最后,并且下一轮不需要进行排序了。通过双重for循环实现,平均和最差时间复杂度为O(n^2)。
package com.sort.bubbleSort;
import java.util.Arrays;
/*
* 冒泡排序
*/
public class BubbleSortDemo2 {
public static void main(String[] args) {
int[] array = { 5, 9, 8, 3, 2, 0, 1 };
System.out.println("排序前:" + Arrays.toString(array));
bubbleSort(array);
System.out.println("排序后:" + Arrays.toString(array));
}
// 冒泡排序
public static void bubbleSort(int[] array) {
int temp = 0;
boolean flag = false;
// 共执行array.length-1
for (int i = 0; i < array.length - 1; i++) {
// 每轮都会将最大的数放到最后,所以每轮只需要执行array.length-1-i次
for (int j = 0; j < array.length - 1 - i; j++) {
if (array[j] > array[j + 1]) {
flag = true;
temp = array[j + 1];
array[j + 1] = array[j];
array[j] = temp;
}
}
// 每轮排序完,检查一下该轮是否有数调整位置了
if (!flag) {// 如果没有数调整位置,说明已经有序了
break;
} else {
flag = false;
}
}
}
}
2. 选择排序
每轮将最小的数放在前面,直至放完。平均和最差时间复杂度为O(n^2)。
package com.sort.selectionSort;
import java.util.Arrays;
/*
* 选择排序
*/
public class SelectSortDemo2 {
public static void main(String[] args) {
int[] array = { 5, 9, 8, 3, 2, 0, 1 };
System.out.println("排序前:" + Arrays.toString(array));
selectSort(array);
System.out.println("排序后:" + Arrays.toString(array));
}
// 选择排序
public static void selectSort(int[] array) {
int min = 0;// 记录最小值
int minIndex = 0;// 记录最小值的下标
// 一共需要排序array.length-1轮
for (int i = 0; i < array.length - 1; i++) {
// 假定每轮的第一个数就是最小值
min = array[i];
minIndex = i;
// 每轮会将最小的值放在最前面,所以每轮从第i+1个数开始
for (int j = i + 1; j < array.length; j++) {
if (min > array[j]) {// 说明假定的最小值并不是真正的最小值
// 将更小的那个值标记为最小值
min = array[j];
minIndex = j;
}
}
// 每轮找到最小值后,将最小值和开始假定的最小值交换位置
if (minIndex != i) {
array[minIndex] = array[i];
array[i] = min;
}
}
}
}
3. 插入排序
插入排序的思想是将一堆数分成有序表和无序表两类,每轮从无序表中取一个数按顺序放到有序表中,直至放完。平均和最差时间复杂度为O(n^2)。
package com.sort.insertSort;
import java.util.Arrays;
/*
* 插入排序
*/
public class InsertSortDemo2 {
public static void main(String[] args) {
int[] array = { 5, 9, 8, 3, 2, 0, 1 };
System.out.println("排序前:" + Arrays.toString(array));
insertSort(array);
System.out.println("排序后:" + Arrays.toString(array));
}
// 插入排序
/*
* 思路分析:
* 1.插入排序的思想是将一堆数分成有序和无序两部分,每次从无序组的中取一个数核有序组进行比较,放入有序组
* 2.将array数组的第一个数看作一个有序组,后面的n-1个数看作无序组;
* 3.从第2个数开始,每取一个数就按顺序插入到有序组中
* 4.记录每次待插入的数,记录每次待插入数前面的那个数的下标
* 5.以从小到大排序为例,那么每次待插入数的前一个数,即有序组的最后一个数,这个数肯定是最大的,
* 如果待插入的数小于这个最大数,说明位置没找到插入位置,就需要跟最大数前面那个数进行比较,以此类推(下标往前移动)
* 直到待插入的数比有序组中某个数大时,插入位置就找到了,就是那个数的位置
* 6.交换位置即可
* 举例:{101, 34, 119, 1,-1,89}==>34 {101, 101, 119, 1,-1,89}==>{34, 101, 119, 1,-1,89}
* {34, 101, 119, 1,-1,89}==>119 {34, 101, 119, 1,-1,89}==>{34, 101, 119, 1,-1,89}
* {34, 101, 119, 1,-1,89}==>1 {34, 101, 119, 119,-1,89}==>{34, 101, 101, 119,-1,89}==>{34, 34, 101, 119,-1,89}==>{1, 34, 101, 119,-1,89}
*/
public static void insertSort(int[] array) {
// 先将array中的第一个元素设为有序表,后面n-1个元素为无序表,接下来每次从无序表中取一个数据插入到有序表中,所以需要array.length-1轮
int insertVal = 0;// 待插入的数
int insertIndex = 0;// 待插入的位置
for (int i = 1; i < array.length; i++) {
// 每次从无序表中取第一个数作为待插入数
insertVal = array[i];
// 将待插入数的前一个位置记录下来,也就是有序表中最大数的位置
insertIndex = i - 1;
// 从有序表中最大数的位置开始找,如果待插入的数小于这个最大数,就找打插入位置了,否则,往前移动(将这个最大的数赋值给后一个数),直到遍历到有序表中的第一个数,也就是最小的那个数为止
while (insertIndex >= 0 && insertVal < array[insertIndex]) {
array[insertIndex + 1] = array[insertIndex];
insertIndex--;
}
// 遍历结束,说明找到待插入数的位置了
if (insertIndex + 1 != i) {// 待插入位置就是自己目前的位置就不用移动(里面就一行代码,对性能几乎没有提升,也可以不做判断)
array[insertIndex + 1] = insertVal;
}
System.out.println("第" + i + "轮排序" + Arrays.toString(array));
}
}
}
4. 希尔排序
希尔排序是一种改进的插入排序。当较小的数比较靠后时,如果使用直接插入排序会移动很多数,这样导致性能很低,希尔排序通过设定步长进行分组排序的方式,提高了较小数靠后时排序的性能。其平均时间复杂度为O(nlogn),最坏时间复杂度为O(n^2)。其方式有两种,一是交换法,二是移动法。
package com.sort.shellSort;
import java.util.Arrays;
/*
* 希尔排序
*/
public class ShellSortDemo2 {
public static void main(String[] args) {
int[] array = { 9, 8, 7, 6, 5, 4, 3, 2, 1, 0 };
System.out.println("排序前:" + Arrays.toString(array));
shellSort2(array);
System.out.println("排序后:" + Arrays.toString(array));
}
// 希尔排序(交换法)---不提倡,交换数据对性能有影响
public static void shellSort1(int[] array) {
int temp = 0;
int count = 0;
// 计算每轮的步长,,每轮的步长也是每轮的组数
for (int gap = array.length / 2; gap > 0; gap /= 2) {
// 每轮有gap组,每组的第一个数作为有序表,不参与排序,所以每轮需要排 array.length-gap次
for (int i = gap; i < array.length; i++) {
// 每组的数间隔为gap
for (int j = i - gap; j >= 0; j -= gap) {
if (array[j] > array[j + gap]) {
temp = array[j + gap];
array[j + gap] = array[j];
array[j] = temp;
}
}
}
System.out.println("第" + (++count) + "轮排序:" + Arrays.toString(array));
}
}
// 希尔排序(移动法)---推荐
public static void shellSort2(int[] array) {
int count = 0;
int insertVal = 0;// 待插入的数
int insertIndex = 0;// 待插入数的前gap个数的下标
// 计算每轮的步长,,每轮的步长也是每轮的组数
for (int gap = array.length / 2; gap > 0; gap /= 2) {
// 每轮有gap组,每组的第一个数作为有序表,不参与排序,所以每轮需要排 array.length-gap次
for (int i = gap; i < array.length; i++) {
// 先记录待插入的数
insertVal = array[i];
// 记录待插入数的前gap个数的下标
insertIndex = i - gap;
// 对比插入排序:如果前gap个数的下标不在数组的最前面,并且待插入的数小于前gap个数,那就将前gap个数的值赋给当前下标往前移动gap个之后,再比较
while (insertIndex >= 0 && insertVal < array[insertIndex]) {
array[insertIndex + gap] = array[insertIndex];
insertIndex -= gap;
}
// 循环结束表示找到插入位置了,就将待插入的数插入到插入位置即可
if (insertIndex + gap != i) {
array[insertIndex + gap] = insertVal;
}
}
System.out.println("第" + (++count) + "轮排序:" + Arrays.toString(array));
}
}
}