首先看这张图:
图中有各种排序算法的时间复杂度、空间复杂度、排序方式和稳定性。
稳定性是指在排序的过程中不会改变元素彼此的位置的相对次序,不稳定的排序算法有:快速、希尔、选择、堆排序。
需要使用辅助空间的有归并、桶、计数和基数排序。
基础
算法可视化,这个网站有数据结构和算法的可视化学习界面,对算法学习入门阶段非常友好。
3y的排序算法文章 可以参考一下还是很详细的
冒泡排序
两层for循环,外层遍历、内层比较,每一次比较将最大的推到最后,就像冒泡一样,然后每一趟都逐渐减小比较的个数,因为每一趟中都找到一个最大的放到最后。
可以进行优化的是,如果数组已经有序了就直接结束排序。用一个flag变量判断是否已经有序。
public static void BubbleSort(int[] arr) {
boolean flag = true;
for (int i = 0; i < arr.length - 1 && flag; i++) {
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;
}
}
}
}
选择排序
两层for循环,外层控制次数、内层遍历每次寻找最小的值,将其与已经排序的尾部进行交换。
public static void SelectSort(int[] arr) {
int min;
for (int i = 0; i < arr.length; i++) {
min = i;
int j;
for (j = i + 1; j < arr.length; j++) {
if (arr[j] < arr[min])
min = j;
}
swap(arr, min, i);
}
}
插入排序
对于一个有序的数组,每次对一个数选择一个合适的位置进行插入,外层for循环控制循环次数、内层while循环寻找合适的位置进行插入。类似于扑克牌洗完牌拿到手上,手上已有的是已经排序的,然后每次摸一张牌起来从最后开始比较,找到合适的位置插入数组。
public static void InsertSort(int[] arr) {
for (int i = 1; i < arr.length; i++) {
int temp = arr[i];
int j = i - 1;
while (j >= 0 && temp < arr[j]) {
arr[j + 1] = arr[j];
j--;
}
arr[j + 1] = temp;
}
}
希尔排序
希尔排序实际上就是插入排序的增强版,将数组分组进行插入排序。把数组按下标的一定增量分组,对每组使用直接插入排序算法排序;当增量为1的时候便是一次简单插入排序了。
public static void shellSort(int[] arr) {
for (int step = arr.length / 2; step > 0; step /= 2) {
for (int i = step; i < arr.length; i++) {
int j = i;
int temp = arr[j];
while (j - step >= 0 && arr[j - step] > temp) {
arr[j] = arr[j - step];
j -= step;
}
arr[j] = temp;
}
}
}
归并排序
归并排序体现了分而治之的思想,将两个元素合并成一个有序数组,然后再将合并的数组进行合并,自顶向下来看是将待排序的数组拆分成两个小数组,知道拆分成元素,然后再将他们归并起来。以下是基于递归的实现。
public static void mergeSort(int[] arr, int[] temp, int L, int R) {
if (L == R) return;
int M = (L + R) / 2;
mergeSort(arr, temp, L, M);
mergeSort(arr, temp, M + 1, R);
merge(arr, temp, L, M, M + 1, R);
}
private static void merge(int[] arr, int[] tmp, int s1, int e1, int s2, int e2) {
int i = s1, j = s2, k = 0;
while (i <= e1 && j <= e2) {
if (arr[i] <= arr[j]) tmp[k++] = arr[i++];
else tmp[k++] = arr[j++];
}
while (i <= e1) tmp[k++] = arr[i++];
while (j <= e2) tmp[k++] = arr[j++];
for (int l = 0; l < k; l++) {
arr[s1 + l] = tmp[l];
}
}
快速排序
面试常客快排,找到一个轴,从它的左边开始找比它大的,从右边开始找比他小的,交换位置,直到左右指针相交,然后再递归调用自身,将分隔开的左右两个数组再次进行快速排序。
找到一个合适的中轴值对算法的优化起到重要的作用。
public static void quickSort(int[] arr, int L, int R) {
int i = L, j = R;
int pivot = getPivot(arr, L, R);
while (i <= j) {
while (arr[i] < pivot) i++;
while (arr[j] > pivot) j--;
if (i <= j) {
swap(arr, i, j);
i++;
j--;
}
}
if (L < j) quickSort(arr, L, j);
if (R > i) quickSort(arr, i, R);
}
private static int getPivot(int[] arr, int L, int R) {
return (arr[L] + arr[(L + R) / 2] + arr[R]) / 3;
}
堆排序
堆排序的思想是将待排序的数组构建成一个大顶堆,然后将最后一个元素和顶与顶元素进行交换,再进行一遍调整的过程,每一次调整的过程都找到最大的放到待排序数组的尾部。
public static void heapSort(int[] arr) {
int n = arr.length;
for (int i = n / 2 - 1; i >= 0; i--) {
siftDown(arr, i, n);
}
for (int i = n - 1; i >= 0; i--) {
swap(arr, 0, i);
siftDown(arr, 0, i);
}
}
private static void siftDown(int[] arr, int k, int n) {
while (2 * k + 1 < n) {
int largest = k;
int left = 2 * k + 1;
int right = 2 * k + 2;
if (left < n && arr[left] > arr[largest]) largest = left;
if (right < n && arr[right] > arr[largest]) largest = right;
if (largest != k) {
swap(arr, k, largest);
k = largest;
} else break;
}
}
基本上常见的排序算法都在这了,其他的还有计数排序、桶排序、基数排序不大可能被问到所以就先不写了等以后有需要再补充,能做到手撕或者白板写出来差不多就能算掌握的程度了。
参考
《大话数据结构》
java3y博客系列文章