目录
1 基础算法
1.1 排序
排序主要分为以下几种,在做算法题的时候主要使用到快速排序和归并排序。快速排序可以直接使用语言自带的排序函数,但是需要掌握手写实现,归并排序需要重点掌握实现,其他排序只需掌握原理即可。另外需要熟记各种排序的时间复杂度、空间复杂度、稳定性。
算法 | 时间复杂度(平均) | 空间复杂度 | 是否稳定 |
直接插入排序 | O(n^2) | O(1) | 是 |
希尔排序 | O(n^1.3) | O(1) | 否 |
冒泡排序 | O(n^2) | O(1) | 是 |
快速排序 | O(nlogn) | O(logn) | 否 |
选择排序 | O(n^2) | O(1) | 否 |
堆排序 | O(nlogn) | O(1) | 否 |
归并排序 | O(nlogn) | O(n) | 是 |
基数排序 | O(d(n+r)) | O(r) | 是 |
需要知道的一些结论:
一趟结束之后能确定一个元素的最终位置的排序方法有交换排序和选择排序。其他的选择排序、归并排序、基数排序都不能确定。
排序趟数与序列的原始状态有关的排序方法有冒泡排序和快速排序。在序列已经有序的情况下冒泡排序的时间复杂度降为O(n),快速排序的时间复杂度升为O(n^2)。
直接插入排序:
对序列A的元素A[0] ~ A[n-1],令i从1到n-1枚举,进行n-1趟操作。第i趟时,序列A的前i个元素A[0] ~ A[i-1]已经有序,序列A范围[i,n-1]的元素还未有序,那么该趟从[0,i-1]中寻找某个位置j,使得A[i]插入位置j后(此时A[j] ~ A[i-1]会后移一位至A[j+1] ~ A[i]),范围[0,i]有序。
void insertSort(int[] arr) {
for (int i = 1; i <= arr.length - 1; i++) {//n-1轮
int temp = arr[i], j = i;
while (j >= 1 && temp < arr[j - 1]) {
arr[j] = arr[j - 1];
j--;
}
arr[j] = temp;
}
}
希尔排序:
将序列分成若干组,每组元素之间间隔距离为d,例如某一组元素的位置为i, i + d, i + 2d, ...,对每一组的元素进行插入排序之后,再对全体进行插入排序。
冒泡排序:
从前往后两两相邻的数进行交换,只要左边的数比右边的数大,就交换两个数的位置,一共进行n-1趟。第一趟的时候把最大的数交换到n-1的位置,第i趟的时候的时候把第i大的数交换到n-i的位置。
void bubbleSort(int[] arr) {
for (int i = 1; i <= arr.length - 1; i++) {//比较n-1轮
for (int j = 0; j < arr.length - i; j++) {
if (arr[j] > arr[j + 1]) {
int temp = arr[j];
arr[j] = arr[j + 1];
arr[j + 1] = temp;
}
}
}
}
快速排序:
在一趟排序的过程中,选择某一个数为基准,先从右往左找第一个比它小的数,与之交换。在从左往右找第一个比它大的数,与之交换。直到这个基准数左边的数都比它小,右边的数都比它大为止。然后再递归的对这个基准数左右两边的两组数进行快速排序。
int partition(int lo, int hi, int[] arr) {
int temp = arr[lo];
while (lo < hi) {
while (lo < hi && arr[hi] > temp) {
hi--;
}
arr[lo] = arr[hi];
while (lo < hi && arr[lo] < temp) {
lo++;
}
arr[hi] = arr[lo];
}
arr[lo] = temp;
return lo;
}
void quickSortHelper(int lo, int hi, int[] arr) {
if (lo < hi) {
int pivot = partition(lo, hi, arr);
quickSortHelper(lo, pivot - 1, arr);
quickSortHelper(pivot + 1, hi, arr);
}
}
//快速排序
void quickSort(int[] arr) {
quickSortHelper(0, arr.length - 1, arr);
}
更优雅的模板:
void quickSort(int[] q, int l, int r) {
if (l >= r) return;
int i = l - 1, j = r + 1, x = q[l + r >> 1];
while (i < j) {
do i++; while (q[i] < x);
do j--; while (q[j] > x);
if (i < j) {
int temp = q[i];
q[i] = q[j];
q[j] = temp;
}
}
quickSort(q, l, j);
quickSort(q, j + 1, r);
}
选择排序:
对一个序列A中的元素A[0]~A[n-1],令i从0到n-1枚举,进行n趟操作,每趟从待排序部分[i,n-1]中选择最小的元素,令其与待排序部分第一个元素A[i]交换,这样A[i]就会与当前有序序列[0,i-1]形成新的有序序列[0,i],n趟操作后,所有元素有序。
void selectSort(int[] arr) {
for (int i = 0; i < arr.length; i++) {
int min = i;
for (int j = i + 1; j < arr.length; j++) {
if (arr[j] < arr[min]) {
min = j;
}
}
int temp = arr[i];
arr[i] = arr[min];
arr[min] = temp;
}
}
堆排序:
将序列视为完全二叉树顺序结构,从⌊n/2⌋ ~ 1的顺序依次筛选构建小根堆或者大根堆。
归并排序:
先从左到右两两一组,再进行组内排序。第二趟的时候从左到右每4个一组,再进行组内排序。最终只剩下一组,再进行组内排序。
void mergeSort(int[] q, int l, int r) {
if (l >= r) {
return;
}
int mid = l + r >> 1;
mergeSort(q, l, mid);
mergeSort(q, mid + 1, r);
int[] temp = new int[r - l + 1];