冒泡排序
核心思想
相邻元素两两相比,每一次冒泡都可以找出最大/最小的元素
代码(附详细注释)
代码是普通冒泡的升级版:如果某一次冒泡循环,没有元素移动,就证明已经是有序数组了,后面就无需再循环了
public void bubbleSort(int[] nums){
//冒泡的次数(大循环)
for (int i = 0; i < nums.length-1; i++) {
// 提前退出冒泡循环的标志位(每一次循环后重置为false)
boolean flag = false;
//两两依次比较(小循环)
for (int j = 0; j < nums.length - 1 - i; j++) {
if (nums[j] > nums[j + 1]) {
int temp = nums[j];
nums[j] = nums[j + 1];
nums[j + 1] = temp;
//有数据交换 则置为true
flag = true;
}
}
//无数据交换 提前退出
if(!flag) break;
}
}
性能分析
-
冒泡排序是原地排序
-
冒泡排序是稳定的排序
-
时间复杂度最好O(n),最坏O(n^2)
插入排序
核心思想
每次从未排序的区间拿出一个元素,在已排序区间找到合适的地方插入
初始的已排序区间是第一个元素
我认为插入排序的关键点就是区分开已排序空间和未排序空间
插入比冒泡强在哪(效率)
冒泡排序的数据交换要比插入排序的数据移动要复杂,冒泡排序需要 3 个赋值操作,而插入排序只需要 1 个
代码(附详细注释)
public void insertSort(int[] nums) {
//默认第一个元素是已排序的,所以从索引为1的位置开始循环
for (int i = 1; i < nums.length; i++) {
//temp用来存储:未排序区间的第一个值(需要找地方插入的值)
//因为后续移动过程 这个值会被覆盖
int temp = nums[i];
//j是已排序区间末尾的指针,为了寻找插入点
int j = i-1;
//已排序区间从后至前,依次比较
while (j >= 0){
//如果 j处元素大于需要插入的值
if (nums[j] > temp) {
//j处元素,向后移动一格
nums[j+1] = nums[j];
} else {
//j处元素 不大于需要插入的值 就证明需要插入的值(temp)找到了归宿
break;
}
//j回退一格
j--;
}
//temp找到了归宿,插入j的后面,j+1的位置
nums[j+1] = temp;
}
}
性能分析
-
插入排序是原地排序
-
插入排序是稳定的排序
-
时间复杂度最好O(n),最坏O(n^2)
选择排序
核心思想
选择排序是每次会从未排序的区间中找到最小的元素,将其放到头部
选择与插入的对比
- 插入排序是拿未排序区间的值,去已排序区间遍历,直到找到属于他的位置(初始情况第一个元素是已排序区间,作为参照物)
- 选择排序是在未排序区间里做文章,先找到最小元素的位置,然后放在头部,每次确定一个最小值
代码(附详细注释)
public void selectSort(int[] nums) {
for (int i = 0; i < nums.length - 1; i++) {
//用来记录最小值的索引位置,默认值为i
int minIndex = i;
//遍历 i+1 ~ length 的值,找到其中最小值的位置
for (int j = i + 1; j < nums.length; j++) {
if (nums[j] < nums[minIndex]) {
minIndex = j;
}
}
// 交换当前索引 i 和最小值索引 minIndex 两处的值
if (i != minIndex) {
int temp = nums[i];
nums[i] = nums[minIndex];
nums[minIndex] = temp;
}
// 执行完一次循环,当前索引 i 处的值为最小值,直到循环结束即可完成排序
}
}
性能分析
-
选择排序是原地排序
-
选择排序不是稳定的排序
-
时间复杂度最好最坏都是O(n^2)