直接插入排序
基本思路:
- 定义两个指针 i 和 j , 还得有一个临时变量 temp
- i 从 1 下标开始遍历数组, j 每次从 i - 1 的下标开始往前遍历, temp 每次一开始存储的是 i 下标的元素
- j 每次往前走都判断 temp 的值和 j 下标的值, 如果 j 下标的值比 temp 的值大就将 j 下标的值放入 j+1 下标
- j 遍历结束或者 j 下标的值比 temp 的值小 , 当前的插入排序就结束
图解
插入第一个元素
插入第二个元素
插入第三个元素
插入第四个元素
代码
public void insertSort(int[] arr) {
if (arr == null || arr.length < 1) {
return;
}
for (int i = 1; i < arr.length; i++) {
int temp = arr[i];
int j = i - 1;
for (; j >= 0; j--) {
if (arr[j] > temp) {
arr[j + 1] = arr[j];
} else {
// arr[j + 1] = temp;
// 只要j回退的时候,遇到了 比temp小的元素就可以结束这次的比较
// 因为经过前面的比较, 前面的元素已经保持有序的状态, 不需要在进行比较了
break;
}
}
// j回退到了 < 0 的地方
arr[j + 1] = temp;
}
}
分析
空间复杂度: O(1)
时间复杂度: O(N ^ 2)
- 对于直接插入排序来说最好的情况就是数据有序的时候, 数组越有序就越快, 这是直接插入排序的一大特点
- 因此插入排序的最好的情况下的时间复杂度为: O(N)
稳定性: 稳定的
- 一个稳定的排序,可以实现为不稳定的排序
- 但是一个本身就不稳定的排序,是不可以变成稳定的排序的
- 因此直接插入排序经常使用在 数据量不多 且 整体数据 趋于有序了的场景下
选择排序
基本思路:
每一次从待排序的数据元素中选出最小(或最大)的一个元素,存放在序列的起始位置,直到全部待排序的数据元素排完
- 定义两个指针 i 和 j
- i 遍历数组, j 就遍历 i + 1 后面的所有元素, 找到最小值
- 然后让这个最小值和 i 下标的元素交换 , i++ 继续在剩余元素找到最小值, 再交换, 直到 i 遍历完数组~
总结:
在元素集合array[i]--array[n-1]中选择关键码最大(小)的数据元素若它不是这组元素中的最后一个(第一个)元素,则将它与这组元素中的最后一个(第一个)元素交换在剩余的array[i]--array[n-2](array[i+1]--array[n-1])集合中,重复上述步骤,直到集合剩余1个元素
代码
public void selectSort(int[] arr) {
if (arr == null || arr.length < 1) {
return;
}
for (int end = 0; end < arr.length; end++) {
// 定义一个遍历来维护最小值下标
int minIndex = end;
for (int second = end + 1; second < arr.length; second++) {
// 每次从当前的 end 下标的第二给元素开始往后遍历, 寻找最小值并更新 minIndex 下标
// 这样一来每次的 0 ~ end 区间的元素就确定是有序的了
minIndex = arr[second] < arr[minIndex] ? second : minIndex;
}
// 让当前的 end 下标和 最小值下标交换
swap(arr, end, minIndex);
}
}
private void swap(int[] arr, int j, int i) {
int temp = arr[j];
arr[j] = arr[i];
arr[i] = temp;
}
分析
空间复杂度: O(N^2)
时间复杂度: O(1)
稳定性: 不稳定的排序
冒泡排序
思路: 让 j 这个指针遍历从前往后遍历, 相邻的两个元素两两比较, 满足条件就交换~
代码1
// 未优化版本
public void bubbleSort(int[] arr) {
if (arr == null || arr.length < 1) {
return;
}
for (int i = 0; i < arr.length; i++) {
// 冒泡的趟数
for (int j = 0; j < arr.length - 1; j++) {
// 每一趟所要比较的次数
// 每次的比较都让 j 和 j+1 比较, 满足条件就交换, 然后 j 继续往后走
if (arr[j] > arr[j + 1]) {
swap(arr, j, j+1);
}
}
}
}
private void swap(int[] arr, int j, int i) {
int temp = arr[j];
arr[j] = arr[i];
arr[i] = temp;
}
代码2
使用 标志位 优化~
// 优化版本
public void bubbleSort2(int[] arr) {
if (arr == null || arr.length < 1) {
return;
}
for (int i = 0; i < arr.length; i++) {
boolean flag = false;
// 冒泡的趟数 i
for (int j = 0; j < arr.length - 1 - i; j++) {
// 每一趟所要比较的次数 j
// 每次的比较都让 j 和 j+1 比较, 满足条件就交换, 然后 j 继续往后走
// 每一趟都确定了一个最大值, 因此 j 就可以不用遍历数组的全部元素
// 只需在上一趟的基础上减少一次比较
if (arr[j] > arr[j + 1]) {
swap(arr, j, j+1);
flag = true;
}
}
if (flag == false) {
// 如果 flag 没变说明没有进入 if 语句, 整体有序不需要排序
break;
}
}
}
private void swap(int[] arr, int j, int i) {
int temp = arr[j];
arr[j] = arr[i];
arr[i] = temp;
}
分析
空间复杂度: O(1)
时间复杂度: O(N ^ 2)
- 对于 代码1 来说, 无论是最好情况下还是最坏情况下, 它的时间复杂度都是 O(N ^ 2)
- 对于 代码2 来说, 最好情况下是 O(N) , 最坏情况下 O(N ^ 2)
稳定性: 稳定的排序
结
堆排序算法有兴趣的童鞋还可以看看下面这篇文章~