(极客时间专栏 “数据结构与算法之美” 的学习笔记)
冒泡排序
冒泡排序只会操作相邻的两个数据。比较,交换之后,每一次冒泡之后会让至少一个元素移动到正确的位置。
public static void bubbleSort(int[] arr) {
if (arr == null || arr.length < 2) {
return;
}
for (int i = 0; i < arr.length; i++) {
boolean flag = false;
for (int j = 0; j < arr.length-i-1; j++) {
if (arr[j] > arr[j + 1]) {
swap(arr, j, j + 1);
flag = true;
}
}
if (!flag) {
return;
}
}
}
插入排序
取未排序区间中的首位元素,移动数据的同时,在已排序区间中找到合适的位置插入,并保证已排区间的数据一直有序,重复直到未排序部分为空。
简单总结过程
- 外层循环遍历 1 to n
- 内层循环后移数据, 插入 arr[i]
public static void insertSort(int[] arr) {
if (arr == null || arr.length < 2) {
return;
}
for (int i = 1; i < arr.length; i++) {
int j = i - 1;
int value = arr[i];
for (; j >= 0; j--) {
if (arr[j] > value) {
arr[j + 1] = arr[j];
} else {
break;
}
}
//已找到插入位置
arr[j + 1] = value;
}
}
选择排序
每次从未排序区间中找到最小值,放到已排序区间的末尾
public static void selectSort(int[] arr) {
if (arr == null || arr.length < 2) {
return;
}
for (int i = 0; i < arr.length; i++) {
int min = i;
for (int j = i + 1; j < arr.length; j++) {
min = arr[j] < arr[min] ? j : min;
}
//将最小值放到已排区间的末尾
if (min != i) {
swap(arr, i, min);
}
}
}
总结
空间O(1) | 是否稳定 | 最好 | 最坏 | 平均 | |
---|---|---|---|---|---|
冒泡 | Y | Y | O(n) | O(n^2) | O(n^2) |
插入 | Y | Y | O(n) | O(n^2) | O(n^2) |
选择 | Y | N | O(n^2) | O(n^2) | O(n^2) |
为什么插入排序比冒泡排序更好?
应用下面的程序测试,随机生成20个1w大小的数组来排序:
冒泡排序的运行时间是 2714 毫秒,插入排序的运行时间是 286 毫秒
冒泡比选择多了 3 个赋值语句,冒泡要交换数据,而插入排序只有一行的数据后移。
long time = 0;
for (int j = 0; j < 20; j++) {
int[] arr = new int[10000];
for (int i = 0; i < 10000; i++) {
arr[i] = (int) (Math.random() * 10000);
}
long begin = System.currentTimeMillis();
bubbleSort(arr, arr.length);
time += System.currentTimeMillis() - begin;
}
System.out.println(time);
插入排序的链表实现
public static Node insertSort(Node head) {
if (head == null || head.next == null) return head;
Node pOrder = head; //有序链表中的游动指针
Node pWait = head.next; //无序链表中的第一个待排序节点
Node insertNode; //要插入的节点
Node ppOrder;//指向pOrder的前一个节点
//将有序和无序部分断开
pOrder.next = null;
while (pWait != null) {
ppOrder = pOrder = head;
insertNode = pWait;
pWait = pWait.next;
insertNode.next = null;
//插入过程
while (pOrder != null) {
if (insertNode.data < pOrder.data) {
if (pOrder == head) { //插在首位
insertNode.next = head;
head = insertNode;
break;
} else { //在中间
insertNode.next = pOrder;
ppOrder.next = insertNode;
break;
}
}
//向后寻找比insert大的节点
ppOrder = pOrder;
pOrder = pOrder.next;
}
//插入结尾
if (pOrder == null) ppOrder.next = insertNode;
}
return head;
}