1. LowB 三人组介绍
LowB 三人组的时间复杂度都是 O(n^2)
1.1 冒泡排序(Bubble Sort)
- 列表每2个相邻的数,如果前面比后面大,则交换这两个数。
- 一趟排序完成后,则无序区减少一个数,有序区增加一个数;
时间复杂度 O(n^2)
案例:
如上图所示,因为冒泡排序是向上冒泡,所以这里采用竖着的,看起来更直观
- 第一次: 7和5比,7大于5,交换7和5,指针向上挪动;
- 第二次: 7和4比,7大于4,交换7和4,指针向上挪动;
- 第三次: 7和6比,7大于6,交换7和6,指针向上挪动;
- 第四次: 7和3比,7大于3,交换7和3,指针向上挪动;
- 第五次: 7和8比,7小于8,不交换,指针向上挪动;
- 第六次: 8和2比,8大于2,交换8和2,指针向上挪动;
- 第七次: 8和9比,8小于9,不交换,指针向上挪动;
- 第八次: 9和1比,9大于1,交换9和1,指针向上挪动,此时指针指向1,倒数第二个位置,由于后面没有数据可以比对了;
这上面的指针从底部挪到顶部倒数第二个位置,称之为一趟; 从此列表分为2部分:有序区和无序区,有序区不用管,无序区再进行上面冒泡操作;一趟会出来一个数,所以当列表长度为n时,会走n-1趟,为什么是n-1趟,因为最后一趟只剩下一个数了;也就不需要冒泡了;所以是n-1趟;
总结: 冒泡排序排了 n-1 趟;
那么怎么使用代码表达? 关键点:趟
,无序区范围
分析:
- 第0趟的时候,有序区没有数;
- 第1趟的时候,有序区只有一个数;
- 第2趟的时候,有序区有2个数;
- 那么无序区的范围就是 (n-i-1) n是列表长度,i 是趟数,减去1是因为指针会走到倒数第二个位置
代码演示:
import java.util.Arrays;
/**
* 冒泡排序
*
* @author wql
* @date 2022/12/8 0:05
*/
public class BubbleSort {
public static void main(String[] args) {
int[] asList = {6, 5, 4, 1, 2, 3};
handBubbleSort(asList);
System.out.println("asList = " + Arrays.toString(asList));
}
public static void handBubbleSort(int[] arr) {
//表示第i趟
for (int i = 0; i < arr.length - 1; i++) {
//j 表示指针
for (int j = 0; j < arr.length - i - 1; j++) {
//如果指针j 指向的那个数比后面一个大,则交换顺序
if (arr[j] > arr[j + 1]) {
int temp = arr[j + 1];
arr[j + 1] = arr[j];
arr[j] = temp;
}
}
}
}
}
1.2 选择排序(Select Sort)
- 一趟排序记录最小的数,放到第一个位置;
- 再一趟排序记录列表无序区最小的数,放到第二个位置
- …
- 算法关键点:有序区和无序区,无序区最小数的位置
时间复杂度 O(n^2)
代码如下:
第一种方式:
import java.util.*;
/**
* 选择排序
*
* @author wql
* @date 2022/12/8 20:34
*/
public class SelectSort {
public static void main(String[] args) {
List<Integer> asList = new ArrayList<>();
asList.add(1);
asList.add(10);
asList.add(3);
asList.add(7);
asList.add(2);
asList.add(4);
asList.add(9);
List<Integer> integers = handSelectSort(asList);
System.out.println("integers = " + integers);
}
/**
* 选择排序的思路是: 每一趟将最小的数选出来,放到一个新的集合内
*
* @param asList 原始列表
*/
public static List<Integer> handSelectSort(List<Integer> asList) {
//创建一个新集合,将每趟挑出来的最小数放入
List<Integer> arrayList = new ArrayList<>();
Iterator<Integer> iterator = asList.iterator();
while (iterator.hasNext()) {
Integer min = Collections.min(asList);
arrayList.add(min);
asList.remove(min);
}
return arrayList;
}
}
- 不推荐,因为还会占用一份内存;
- 选择最小数时,是O(n) 的一个操作;
- remove时,也是一个O(n)的一个操作;
- 所以
时间复杂度 O(n^2)
第二种方式:
不开辟新的列表
思路: 每次排序将最小数选出来,与最左边的第一个数做交换,这样列表会分为两个区域,最左边是有序区,最右边是无序区,依次排序,排序n-1次,即全部变成有序,最后一个数一定是最大的
public static void handSelectSortTwo(int[] arr) {
//i 表示第几趟
for (int i = 0; i < arr.length - 1; i++) {
//暂存无序区最小值的位置下标,为了方便交换
int minLoc = i;
// j 表示 无序列表的范围
for (int j = i + 1; j < arr.length; j++) {
//如果无序区的数,比min_loc 还小,那么重新赋值
if (arr[j] < arr[minLoc]) {
minLoc = j;
}
}
//当循环结束后,则最小值已经找出来了,此时要将无序区第一个值 i和最小值min_loc做交换
int temp = arr[minLoc];
arr[minLoc] = arr[i];
arr[i] = temp;
System.out.println("min_loc = " + Arrays.toString(arr));
}
}
1.3 插入排序
时间复杂度 : O(n^2)
import java.util.*;
/**
* 插入排序
*
* @author wql
* @date 2022/12/8 21:49
*/
public class InsertSort {
public static void main(String[] args) {
int[] arr = {1, 4, 2, 5, 3, 9, 7, 8, 6};
handInsertSort(arr);
}
/**
* 插入排序
* 思路: 就好比摸牌,每次摸一张牌,就跟手里的牌比一下,然后按照正确的位置插入
*
* @param arr 原始列表
*/
public static void handInsertSort(int[] arr) {
//i 表示 摸到的牌的下标
for (int i = 1; i < arr.length; i++) {
//将摸到的牌暂存
int tmp = arr[i];
//j 表示手里的牌的下标
int j = i - 1;
//如果牌的下标小于0或者 手机的牌小于摸到的牌,就暂停循环
while (j >= 0 && arr[j] > tmp) {
//如果手里的牌大于摸到的牌,手里的牌往右边移动
int temp = arr[j + 1];
arr[j + 1] = arr[j];
arr[j] = temp;
j -= 1;
System.out.println("arr = " + Arrays.toString(arr));
}
}
}
}
1.4 小总结
- 冒泡,选择,插入 排序的时间复杂度都是
O(n^2)
; - 都是原地排序