常见的排序算法:
一、冒泡排序:演示冒泡过程的例子
冒泡排序(Bubble Sorting)的基本思想是:通过对待排序序列从前向后(从下标较小的元素开始),依次比较相邻元素的值,若发现逆序则交换,使值较大的元素逐渐从前移向后部,就象水底下的气泡一样逐渐向上冒。
优化:
因为排序的过程中,各元素不断接近自己的位置,如果一趟比较下来没有进行过交换,就说明序列有序,因此要在排序过程中设置一个标志 flag 判断元素是否进行过交换。从而减少不必要的比较。(这里说的优化,可以在冒泡排序写好后,在进行)
图解:
总结:
(1) 一共进行 数组的大小-1 次 大的循环
(2)每一趟排序的次数在逐渐的减少
(3) 如果我们发现在某趟排序中,没有发生一次交换, 可以提前结束冒泡排序。这个就是优化
/**
* 推倒思路:
*/
int[] arr = {5,4,3,2,1};
// 第1次排序
int index = 0;
for(int i = 0; i < arr.length - 1 - index; i++) {
// 找到逆序的,交换位置
if(arr[i] > arr[i + 1]) {
int cur = arr[i];
arr[i] = arr[i + 1];
arr[i + 1] = cur;
}
}
// [4, 3, 2, 1, 5] 把5排序到最后面
System.out.println("第" + (index + 1) + "次排序: " + Arrays.toString(arr));
// 第2次排序:
index = 1;
for(int i = 0; i < arr.length - 1 - index; i++) {
// 找到逆序的,交换位置
if(arr[i] > arr[i + 1]) {
int cur = arr[i];
arr[i] = arr[i + 1];
arr[i + 1] = cur;
}
}
// [3, 2, 1, 4, 5]把4排序到最后面
System.out.println("第" + (index + 1) + "次排序: " + Arrays.toString(arr));
// 第3次排序:
index = 2;
for(int i = 0; i < arr.length - 1 - index; i++) {
// 找到逆序的,交换位置
if(arr[i] > arr[i + 1]) {
int cur = arr[i];
arr[i] = arr[i + 1];
arr[i + 1] = cur;
}
}
// [2, 1, 3, 4, 5] 把3排序到最后面
System.out.println("第" + (index + 1) + "次排序: " + Arrays.toString(arr));
/** 华丽分割线=====================================================*/
/**
* 冒泡排序 8万数据 20 - 30 s
*/
private static void BubbleSort() {
// 思路: 由上面推倒我们可以用双重循环代替
// 内层循环:遍历的是每一次的从0位置到最后位置的,然后比较大小排序
// 外层循环:一共循环几次能排序出来
int[] arr = {2,1,5,4,11,6,22,7,9};
// 优化 一旦发现没有变化位置,直接退出即可
boolean isSort = false;
for(int i = 0; i < arr.length - 1; i++) {
for(int j = 0; j < arr.length - 1 - i; j++) {
if(arr[j] > arr[j + 1]) {
int cur =arr[j];
arr[j] = arr[j + 1];
arr[j + 1] = cur;
isSort=true;
}
}
System.out.println("第" + (i + 1) + "排序: " + Arrays.toString(arr));
if(!isSort) {
break;
}else {
// 重置状态
isSort = false;
}
}
System.out.print(Arrays.toString(arr));
/**
* 结果:
* 第1排序: [1, 2, 4, 5, 6, 11, 7, 9, 22]
* 第2排序: [1, 2, 4, 5, 6, 7, 9, 11, 22]
* 第3排序: [1, 2, 4, 5, 6, 7, 9, 11, 22] // 排了三次就不用拍了,这就是优化。
* [1, 2, 4, 5, 6, 7, 9, 11, 22]
*/
}
二、选择排序:
总结: 默认每次比较的第一位置为最小值,然后遍历比较,如果有比此数还小的值,交换位置。
代码实现:
/** 选择排序*/
private static void selecterSort() {
int[] arr = {6,5,4,3,2,1};
/** ====== 推倒过程 =========*/
// 第1次排序
int index = 0;
// 默认最小值为数组第一个
int min = arr[index];
// 最小值位置
int minPos = index;
for(int i = 0 + 1; i < arr.length; i++) {
// 找到最小值的最小位置
if(min > arr[i]) {
min = arr[i];
minPos = i;
}
}
// 保证不是原位置
if(minPos != index) {
// 交换位置
arr[minPos] = arr[index];
arr[index] = min;
}
// [1, 5, 4, 3, 2, 6] 把1放到第一个位置
System.out.println("排序第" + (index + 1) + "次" + Arrays.toString(arr));
// 第2次排序
index = 1;
// 默认最小值为数组第一个
min = arr[index];
// 最小值位置
minPos = index;
for(int i = 1 + 1; i < arr.length; i++) {
// 找到最小值的最小位置
if(min > arr[i]) {
min = arr[i];
minPos = i;
}
}
// 保证不是原位置
if(minPos != index) {
// 交换位置
arr[minPos] = arr[index];
arr[index] = min;
}
// [1, 5, 4, 3, 2, 6] =======>[1, 2, 4, 3, 5, 6] 把2 和5掉换位置
System.out.println("排序第" + (index + 1) + "次" + Arrays.toString(arr));
// 第3次排序
index = 2;
// 默认最小值为数组第一个
min = arr[index];
// 最小值位置
minPos = index;
for(int i = 2 + 1; i < arr.length; i++) {
// 找到最小值的最小位置
if(min > arr[i]) {
min = arr[i];
minPos = i;
}
}
// 保证不是原位置
if(minPos != index) {
// 交换位置
arr[minPos] = arr[index];
arr[index] = min;
}
// [1, 2, 4, 3, 5, 6] =======> [1, 2, 3, 4, 5, 6] 把3 和4掉换位置
System.out.println("排序第" + (index + 1) + "次" + Arrays.toString(arr));
/**============================ 华丽分割线===============================*/
/**
* 根据上面推理:总结两个循环搞定
* 8万数据 时间 2 - 3 s
* 时间复杂度 0n²
* 思路:1、内存循环找到每次循环最小值,和最小值位置
* 2、外层循环排序一共循环多少次比较。
*/
// 一共比较几次? n-1次
for(int i = 0; i < arr.length - 1; i++) {
// 假定第一个数是最小值
min = arr[i];
// 最小值位置
index = i;
// 每一次比较,逻辑
for(int j = i + 1; j < arr.length; j++) {
if(min > arr[j]) {
// 找到最小的值 和 位置
min = arr[j];
index = j;
}
}
if(i != index) {
// 交换位置
int cur = arr[i];
arr[i] = min;
arr[index] = cur;
System.out.println("排序第" + (i + 1) + "次" + Arrays.toString(arr));
}else {
System.out.println("排序第" + (i + 1) + "次" + "没有发生交换");
}
}
System.out.println("结果:" + Arrays.toString(arr));
/**
* 排序第1次[1, 5, 4, 3, 2, 6]
* 排序第2次[1, 2, 4, 3, 5, 6]
* 排序第3次[1, 2, 3, 4, 5, 6]
* 排序第4次没有发生交换
* 排序第5次没有发生交换
* 结果:[1, 2, 3, 4, 5, 6]
*/
}
三、插入排序:
插入排序法思想:
插入排序(Insertion Sorting)的基本思想是:把 n 个待排序的元素看成为一个有序表和一个无序表,开始时有序表中只包含一个元素,无序表中包含有 n-1 个元素,排序过程中每次从无序表中取出第一个元素,把它的排序码依次与有序表元素的排序码进行比较,将它插入到有序表中的适当位置,使之成为新的有序表。
private static void insertSort() {
int[] arr = {101,34,119,1};
/**
* 推倒过程
* 原 [101, 34, 119, 1]
* 1、看成两个数组,一个有序一个无序。 第一次排序[101]为有序 [34,119,1]为无序
* 2、比较有序数据小的放在前面
*/
//
// /** 第一次插入比较*/
// // 要插入的数
// int insertNum = arr[1];
// // 要插入的数前一个位置(比较)
// int insertPre = 1 - 1;
//
// /**
// * insertPre >= 0 ===========》 比较位置不能越界
// * insertNum < arr[insertPre] ==========》 插入的数小于有序数组的数, 数有序的数往后措一位
// */
// while (insertPre >= 0 && insertNum < arr[insertPre]){
// arr[insertPre + 1] = arr[insertPre];
// insertPre--;
// }
//
// // 插入的位置替换
// arr[insertPre + 1] = insertNum;
//
// System.out.println("第1次排序结果: " + Arrays.toString(arr));
//
// /** 第二次插入比较*/
// insertNum = arr[2];
// insertPre = 2 - 1;
//
// while (insertPre >= 0 && insertNum < arr[insertPre]){
// arr[insertPre + 1] = arr[insertPre];
// insertPre--;
// }
//
// arr[insertPre + 1] = insertNum;
// System.out.println("第2次排序结果: " + Arrays.toString(arr));
//
// /** 第二次插入比较*/
// insertNum = arr[3];
// insertPre = 3 - 1;
//
// while (insertPre >= 0 && insertNum < arr[insertPre]){
// arr[insertPre + 1] = arr[insertPre];
// insertPre--;
// }
//
// arr[insertPre + 1] = insertNum;
// System.out.println("第3次排序结果: " + Arrays.toString(arr));
/**
* 1、一共排序数组长度 - 1 次
* 第一层循环: i == 》 第几次循环
*/
for(int i = 1; i < arr.length; i++) {
// 要插入的数
int insertNum = arr[i];
// 要插入的数的前一个位置
int insertPre = i - 1;
while (insertPre >= 0 && insertNum < arr[insertPre]){
arr[insertPre + 1] = arr[insertPre];
insertPre--;
}
arr[insertPre + 1] = insertNum;
}
// 排序结果: [1, 34, 101, 119]
System.out.println("排序结果: " + Arrays.toString(arr));
}
四、希尔排序:插入算法的优化
希尔排序法的示意图:
有一群小牛, 考试成绩分别是 {8,9,1,7,2,3,5,4,6,0} 请从小到大排序.请分别使用
- 希尔排序时, 对有序序列在插入时采用交换法, 并测试排序速度.
- 希尔排序时, 对有序序列在插入时采用移动法, 并测试排序速度
- 代码实现
交换法: 8万数据17s
/**
* 希尔排序: 插入算法优化。
* // 交换算法 8万数据处理 17s
*/
private static void shellSort() {
int[] arr = {8,9,1,7,2,3,5,4,6,0};
/**
* 推导过程
* 思路: 数组的长度开始 /= 2; 直到长度为1
* 每次循环: 比较小的交换到前面
*/
// 第一次 arr.length / 2
for(int i = 5; i < arr.length; i++) {
// 跟前五步比较交换位置
for(int j = i - 5; j >= 0 ; j -= 5) {
if(arr[j + 5] < arr[j]) {
int cur = arr[j];
arr[j] = arr[j + 5];
arr[j + 5]= cur;
}
}
}
// [3, 5, 1, 6, 0, 8, 9, 4, 7, 2]
System.out.println(Arrays.toString(arr));
// 第二次 (arr.length / 2 ) / 2 ======》 5 / 2 = 2
//
for(int i = 2; i < arr.length; i++) {
// 跟前2步比较交换位置
for(int j = i - 2; j >= 0 ; j -= 2) {
if(arr[j + 2] < arr[j]) {
int cur = arr[j];
arr[j] = arr[j + 2];
arr[j + 2]= cur;
}
}
}
// [0, 2, 1, 4, 3, 5, 7, 6, 9, 8]
System.out.println(Arrays.toString(arr));
// 第三次 ((arr.length / 2 ) / 2) / 2 ======》 5 / 2 = 2 / 2 = 1
// 最后一次比较
for(int i = 1; i < arr.length; i++) {
// 跟前1步比较交换位置
for(int j = i - 1; j >= 0 ; j -= 1) {
if(arr[j + 1] < arr[j]) {
int cur = arr[j];
arr[j] = arr[j + 1];
arr[j + 1]= cur;
}
}
}
// [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
System.out.println(Arrays.toString(arr));
/** ===========================华丽的分割线==========================*/
// 每次均分,直到为1。 (一共分这么次)
for(int gap = arr.length / 2; gap > 0; gap /= 2) {
// 从均分位置开始遍历比较
for(int i = gap; i < arr.length; i++){
// 每一次怎么比
for(int j = i - gap; j >= 0; j -= gap) {
if(arr[j + gap] < arr[j]) {
int cur = arr[j];
arr[j] = arr[j + gap];
arr[j + gap]= cur;
}
}
}
}
System.out.println(Arrays.toString(arr));
}
移动法:8万数据不到1s(分组 + 插入算法结合)
/**
* 希尔排序 移动: 分组 + 插入算法结合
* 8万数据 不到1s
*/
private static void shellMove() {
int[] arr = {8,9,1,7,2,3,5,4,6,0};
//希尔交换排序 8万数据 17s 因为每次都要交换数据
//在做一步优化,我们直接结合插入,移动位置
for(int gap = arr.length / 2; gap > 0; gap /= 2) {
// 从第 gap 个元素,逐个对其所在的组进行直接插入排序
// 其实就是比较分组的大小,跟插入排序一样
for(int i = gap; i < arr.length; i++) {
int position = i; //要插入的位置
int insterNum = arr[position];//要插入的数
if(arr[position] < arr[position - gap]) {
while ((position - gap) >= 0 && insterNum < arr[position - gap]) {
arr[position] = arr[position - gap];
position -= gap;
}
//当退出 while 后,就给 temp 找到插入的位置
arr[position] = insterNum;
}
}
}
System.out.println(Arrays.toString(arr));
// for(int gap = arr.length / 2; gap > 0 ; gap /= 2) {
//
// for(int i = gap; i < arr.length; i++) {
//
// int j = i - gap;
// int tmp = arr[i];
//
// if(tmp < arr[j]) {
// while (j >= 0 && tmp < arr[j]){
// arr[j + gap] = arr[j];
// j -= gap;
// }
//
// arr[j + gap] = tmp;
// }
// }
// }
}