排序算法总结
选择排序
public static void selectSort(int[] arr) {
if (arr == null || arr.length < 2) {
return;
}
// 0 ~ N-1 找到最小值,在哪,放到0位置上
// 1 ~ n-1 找到最小值,在哪,放到1 位置上
// 2 ~ n-1 找到最小值,在哪,放到2 位置上
for (int i = 0; i < arr.length - 1; i++) {
int minIndex = i;
for (int j = i + 1; j < arr.length; j++) { // i ~ N-1 上找最小值的下标
minIndex = arr[j] < arr[minIndex] ? j : minIndex;
}
int tmp = arr[i];
arr[i] = arr[minIndex];
arr[minIndex] = tmp;
}
}
冒泡排序
冒泡排序的基本思想是通过重复地交换相邻的未按次序排列的元素,每一轮将未排序部分中最大的元素向右移动。
public static void bubbleSort(int[] arr){
if (arr == null || arr.length < 2) {
return;
}
for (int end = arr.length - 1; end >= 0; end--) {
for (int i = 0; i < end; i++) {
if (arr[i + 1] < arr[i]) {
int temp = arr[i];
arr[i] = arr[i + 1];
arr[i + 1] = temp;
}
}
}
}
插入排序
插入排序的基本思想是将一个元素插入到已经有序的部分中,直到所有元素都有序为止。
public static void insertSort(int[] arr) {
if (arr == null || arr.length < 2) {
return;
}
for (int i = 1; i < arr.length; i++) {
int tmp = arr[i];
int j = i - 1;
while (j >= 0 && arr[j] > tmp) {
arr[j + 1] = arr[j];
j--;
}
arr[j + 1] = tmp;
}
}
归并排序
public static void mergeSort(int[] arr) {
if (arr == null || arr.length < 2) {
return;
}
process(arr, 0, arr.length);
}
public static void process(int[] arr, int L, int R){
if (L >= R) {
return;
}
//int mid = L + (R - L) / 2;
//取中点
int mid = L + ((R - L) >> 1);
process(arr, L, mid);
process(arr, mid + 1, R);
merge(arr, L, mid, R);
}
public static void merge(int[] arr, int L, int mid, int R) {
int[] temp = new int[R - L + 1];
int i = L, j = mid + 1, k = 0;
while (i <= mid && j <= R) {
temp[k++] = arr[i] <= arr[j] ? arr[i++] : arr[j++];
}
while (i <= mid) {
temp[k++] = arr[i++];
}
while (j <= R) {
temp[k++] = arr[j++];
}
for (int m = 0; m < temp.length; m++) {
arr[L + m] = temp[m];
}
}
随机快速排序
递归实现
public static void QuickSort1(int[] arr) {
if (arr == null || arr.length < 2) return;
process(arr, 0, arr.length - 1);
}
public static void process(int[] arr, int L, int R) {
if (L >= R) return;
swap(arr, (int) Math.random() * (R - L + 1), R);
int[] equlArea = netherlandsFlag(arr, L, R);
process(arr, L, equlArea[0] - 1);
process(arr, equlArea[1] + 1, R);
}
//返回等于 R 的区间下标
public static int[] netherlandsFlag(int[] arr, int L, int R) {
if (L > R) return new int[]{-1, -1};
if (L == R) return new int[]{L, L};
int less = L - 1;
int more = R;
int index = L;
while (index < more) {
if (arr[index] < arr[R]) {
swap(arr, index++, ++less);
} else if (arr[index] > arr[R]) {
swap(arr, index, --more);
} else {
index++;
}
}
swap(arr, more, R);
return new int[]{less + 1, more};
}
public static void swap(int[] arr, int i, int j) {
int temp = arr[i];
arr[i] = arr[j];
arr[j] = temp;
}
非递归- 栈实现
public static class Op{
public int l;
public int r;
public Op(int left, int right) {
this.l = left;
this.r = right;
}
}
public static void QuickSort2(int[] arr) {
if (arr == null || arr.length < 2) return;
int N = arr.length;
swap(arr, N - 1, (int) (Math.random() * N));
int[] equalArea = netherlandsFlag(arr, 0, N - 1);
int el =equalArea[0];
int er = equalArea[1];
Stack<Op> stack = new Stack<>();
stack.push(new Op(0, el - 1));
stack.push(new Op(er + 1, N - 1));
while (!stack.isEmpty()) {
Op op = stack.pop();
if (op.l < op.r) {
swap(arr, op.l + (int) (Math.random() * (op.r - op.l + 1)), op.r);
equalArea = netherlandsFlag(arr, op.l, op.r);
el = equalArea[0];
er = equalArea[1];
stack.push(new Op(op.l, el - 1));
stack.push(new Op(er + 1, op.r));
}
}
}
//返回等于 R 的区间下标
public static int[] netherlandsFlag(int[] arr, int L, int R) {
if (L > R) return new int[]{-1, -1};
if (L == R) return new int[]{L, L};
int less = L - 1;
int more = R;
int index = L;
while (index < more) {
if (arr[index] < arr[R]) {
swap(arr, index++, ++less);
} else if (arr[index] > arr[R]) {
swap(arr, index, --more);
} else {
index++;
}
}
swap(arr, more, R);
return new int[]{less + 1, more};
}
public static void swap(int[] arr, int i, int j) {
int temp = arr[i];
arr[i] = arr[j];
arr[j] = temp;
}
非递归- 队列实现
public static void QuickSort3(int[] arr){
if (arr == null || arr.length < 2) return;
int N = arr.length;
swap(arr, N - 1, (int) (Math.random() * N));
int[] equalArea = netherlandsFlag(arr, 0, N - 1);
int el = equalArea[0];
int er = equalArea[1];
Queue<Op> queue = new LinkedList<Op>();
queue.offer(new Op(0, el - 1));
queue.offer(new Op(er + 1, N - 1));
while (!queue.isEmpty()) {
Op op = queue.poll();
if (op.l < op.r) {
swap(arr, op.l + (int) (Math.random() * (op.r - op.l + 1)), op.r);
equalArea = netherlandsFlag(arr, op.l, op.r);
el = equalArea[0];
er = equalArea[1];
queue.offer(new Op(op.l, el - 1));
queue.offer(new Op(er + 1, op.r));
}
}
}
public static class Op{
public int l;
public int r;
public Op(int left, int right) {
this.l = left;
this.r = right;
}
}
//返回等于 R 的区间下标
public static int[] netherlandsFlag(int[] arr, int L, int R) {
if (L > R) return new int[]{-1, -1};
if (L == R) return new int[]{L, L};
int less = L - 1;
int more = R;
int index = L;
while (index < more) {
if (arr[index] < arr[R]) {
swap(arr, index++, ++less);
} else if (arr[index] > arr[R]) {
swap(arr, index, --more);
} else {
index++;
}
}
swap(arr, more, R);
return new int[]{less + 1, more};
}
public static void swap(int[] arr, int i, int j) {
int temp = arr[i];
arr[i] = arr[j];
arr[j] = temp;
}
堆排序
大根堆实现堆排序
public static void heapSort(int[] arr) {
if (arr == null || arr.length < 2) return;
int N = arr.length;
// 建堆(构造大根堆)
for (int i = N / 2 - 1; i >= 0; i--) {
heapify(arr, N, i);
}
// 堆排序(依次交换堆顶和最后一个元素,并进行堆调整)
for (int i = N - 1; i >= 0; i--) {
swap(arr, 0, i);
heapify(arr, 0, i);
}
}
public static void heapify(int[] arr, int N, int i) {
int left = 2 * i + 1;
int right = 2 * i + 2;
int largest = i; // 假设当前节点的值最大
// 如果左子节点的值大于当前节点的值,则更新最大节点
if (left < N && arr[left] > arr[largest]) {
largest = left;
}
// 如果右子节点的值大于当前节点的值,则更新最大节点
if (right < N && arr[right] > arr[largest]) {
largest = right;
}
// 如果最大节点不是当前节点,则进行交换,并对交换后的节点进行堆调整
if (largest!= i) {
swap(arr, i, largest);
heapify(arr, N, largest);
}
}
桶排序
计数排序
public static void countSort(int[] arr) {
if (arr == null || arr.length < 2) return;
int max = Integer.MIN_VALUE;
for (int i = 0; i < arr.length; i++) {
max = Math.max(max, arr[i]);
}
//按最大值创建辅助数组
int[] bucket = new int[max + 1];
//按值进桶记录
for (int i = 0; i < arr.length; i++) {
bucket[arr[i]]++;
}
int i = 0;
//出桶,bucket是几就说明这个数出现了几次
for (int j = 0; j < bucket.length; j++) {
while (bucket[j]-- >0) {
arr[i] = j;
}
}
}
基数排序
public static void radixSort(int[] arr) {
if (arr == null || arr.length < 2) return;
radixSort(arr, 0, arr.length - 1, maxbits(arr));
}
//返回数组中最大的数是几位数
public static int maxbits(int[] arr) {
int max = Integer.MIN_VALUE;
for (int i = 0; i < arr.length; i++) {
max = Math.max(max, arr[i]);
}
int res = 0;
while (max != 0) {
res++;
max /= 10;
}
return res;
}
public static void radixSort(int[] arr, int L, int R, int maxbit) {
final int redix = 10;
int i = 0, j = 0;
int[] help = new int[R - L + 1];
for (int x = 1; x <= maxbit; x++) {
//创建辅助数组
int[] count = new int[redix];
//进桶,根据位上的数在辅助数组中记录
for (i = L; i <= R; i++) {
j = getDigit(arr[i], x);
count[j]++;
}
//求前缀和
for (i = 1; i < redix; i++) {
count[i] += count[i - 1];
}
//按顺序出桶,
for (i = L; i <= R; i++) {
j = getDigit(arr[i], x);
help[count[j] - 1] = arr[i];
count[j]--;
}
//给原数组赋值
for (i = L; i <= R; i++) {
arr[i] = help[i - L];
}
}
}
//返回数x在d位上是多少
public static int getDigit(int x, int d) {
return (x / (int) Math.pow(10, d - 1)) % 10;
}
算法 | 时间复杂度 | 额外空间复杂度 | 稳定性 |
---|---|---|---|
选择排序 | O(N^2) | O(1) | 无 |
冒泡排序 | O(N^2) | O(1) | 有 |
插入排序 | O(N^2) | O(1) | 有 |
归并排序 | O(N*logN) | O(N) | 有 |
随机排序 | O(N*logN) | O(logN) | 无 |
堆排序 | O(N*logN) | O(1) | 无 |
计数排序 | O(N) | O(N) | 有 |
基数排序 | O(N) | O(N) | 有 |