排序算法
算法基础
1)时间复杂度
一个算法执行所消耗的时间。
2)空间复杂度
运行完一个算法所需的内存大小。
3)算法的稳定行
稳定排序:如果 a 原本在 b 的前面,且 a == b,排序之后 a 仍然在 b 的前面,则为稳定排序。
非稳定排序:如果 a 原本在 b 的前面,且 a == b,排序之后 a 可能不在 b 的前面,则为非稳定排序。
2、经典排序算法
2.1、插入排序-insertion
从数组第一个开始往后挑出一个数字,然后网这个数字位置前面去找,直到找到比它小的停止
package sort;
/**
* @author zll
* @description 插入排序
*/
public class SortInsert {
public static void insertSort(int[] arr) {
if (arr == null || arr.length <= 1) {
return ;
}
for (int i = 1; i < arr.length; i++) {
for (int j = i - 1; j >= 0; j--) {
if (arr[j + 1] < arr[j]) {
Sort.swap(arr, j + 1, j);
}
else {
break;
}
}
}
}
public static void insertSort2(int[] arr) {
if (arr == null || arr.length <= 1) {
return ;
}
for (int i = 1; i < arr.length; i++) {
int j = i - 1;
while (j >= 0 && arr[j + 1] < arr[j]) {
Sort.swap(arr, j + 1, j--);
}
}
}
}
2.2、选择排序-selection
package sort;
/**
* @author zll
* @description 选择排序
*/
public class SortSelection {
public static void selectSort(int[] arr) {
if (arr == null || arr.length < 2) {
return ;
}
int len = arr.length;
for (int i = 0; i < len - 1; i++) {
int minIndex = i;
for (int j = i + 1; j < len; j++) {
if (arr[minIndex] > arr[j]) {
minIndex = j;
}
}
Sort.swap(arr, i, minIndex);
}
}
public static void selectSort2(int[] arr) {
if (arr == null || arr.length < 2) {
return ;
}
int len = arr.length;
for (int i = 0; i < len - 1; i++) {
int minIndex = i;
int j = i + 1;
while (j < len) {
if (arr[minIndex] > arr[j]) {
minIndex = j;
}
j++;
}
Sort.swap(arr, i, minIndex);
}
}
public static void selectSort3(int[] arr) {
if (arr == null || arr.length < 2) {
return ;
}
for (int i = 0; i < arr.length; i++) {
for (int j = 0; j < i; j++) {
if (arr[j] > arr[i]) {
Sort.swap(arr, i, j);
}
}
}
}
public static void selectSort4(int[] arr) {
if (arr == null || arr.length < 2) {
return ;
}
for (int i = 0; i < arr.length; i++) {
int j = 0;
while (j < i) {
if (arr[j] > arr[i]) {
Sort.swap(arr, i, j);
}
j ++;
}
}
}
}
2.3、冒泡排序-bubble
package sort;
/**
* @author zll
* @description 冒泡排序
*/
public class SortBubble {
public static void bubbleSort(int[] arr) {
if (arr == null || arr.length <= 1) {
return ;
}
for (int i = 0; i < arr.length; i++) {
for (int j = 0; j < arr.length - i - 1; j++) {
if (arr[j] > arr[j + 1]) {
Sort.swap(arr, j, j + 1);
}
}
}
}
public static void bubbleSort2(int[] arr) {
if (arr == null || arr.length <= 1) {
return ;
}
for (int i = arr.length - 1; i > 0; i--) {
for (int j = 0; j < i; j++) {
if (arr[j] > arr[j + 1]) {
Sort.swap(arr, j, j + 1);
}
}
}
}
}
2.4、快速排序-quick
package sort;
import java.util.Stack;
/**
* @author zll
* @description 快速排序
* 核心就是partition
*
* 1.0 找出arr中值比最右边位置R的数小的位置, 分成小于等于arr[R]和大于arr[R]的数据
* 2.0 arr中值和最右边位置R的数比较, 分成小于arr[R], 等于 arr[R] 和大于arr[R]的数据三部分
* 3.0 arr中值和最右边位置R的数比较, 分成小于arr[R](随机在L和R之间找一个数跟arr[R]交换), 等于 arr[R] 和大于arr[R]
*/
public class SortQuick {
/**
* 1.0
*/
public static void sort1(int[] arr) {
if (arr == null || arr.length <= 1) {
return;
}
process1(arr, 0, arr.length - 1);
}
private static void process1(int[] arr, int L, int R) {
if (L >= R) {
return ;
}
// 找出中心值
int M = partition1(arr, L, R);
process1(arr, L, M - 1);
process1(arr, M + 1, R);
}
/**
* 1.0 找出arr中值比最右边位置R的数小的位置, 分成小于等于arr[R]和大于arr[R]的数据
*/
private static int partition1(int[] arr, int L, int R) {
if (L >= R) {
return L > R ? -1 : L;
}
int lesserLeft = L - 1;
int index = L;
while (index < R) {
if (arr[index] <= arr[R]) {
Sort.swap(arr, index, ++lesserLeft);
}
index ++;
}
Sort.swap(arr, R, ++lesserLeft);
return lesserLeft;
}
/**
* 2.0
*/
public static void sort2(int[] arr) {
if (arr == null || arr.length <= 1) {
return;
}
process2(arr, 0, arr.length - 1);
}
private static void process2(int[] arr, int L, int R) {
if (L >= R) {
return ;
}
// 等于R的左右边界
int[] p = partition2(arr, L, R);
process1(arr, L, p[0] - 1);
process1(arr, p[1] + 1, R);
}
/**
* 2.0 arr中值和最右边位置R的数比较, 分成小于arr[R], 等于 arr[R] 和大于arr[R]的数据三部分
*/
private static int[] partition2(int[] arr, int L, int R) {
if (L >= R) {
return new int[]{-1, -1};
}
int lesserLeft = L - 1;
int rightMore = R;
int index = L;
while (index < rightMore) {
if (arr[index] < arr[R]) {
Sort.swap(arr, index++, ++lesserLeft);
} else if (arr[index] > arr[R]) {
Sort.swap(arr, index, --rightMore);
} else {
index ++;
}
}
// 交换最后一个相等的数
Sort.swap(arr, R, rightMore);
return new int[]{++lesserLeft, rightMore};
}
/**
* 3.0
*/
public static void sort3(int[] arr) {
if (arr == null || arr.length <= 1) {
return;
}
process3(arr, 0, arr.length - 1);
}
private static void process3(int[] arr, int L, int R) {
if (L >= R) {
return ;
}
/**
* 3.0 arr中值和最右边位置R的数比较, 分成小于arr[R](随机在L和R之间找一个数跟arr[R]交换), 等于 arr[R] 和大于arr[R]
*/
// 随机交换L~R之间的位置跟R的数据(防止逆序 情况下的极端问题)
Sort.swap(arr, L + (int) (Math.random() * (R - L + 1)), R);
// 等于R的左右边界
int[] p = partition2(arr, L, R);
process1(arr, L, p[0] - 1);
process1(arr, p[1] + 1, R);
}
/**
* 非递归方式实现快速排序
*/
public static void sortNotRecursive(int[] arr) {
if (arr == null || arr.length <= 1) {
return;
}
Stack<int[]> stack = new Stack<>();
stack.add(new int[]{0, arr.length - 1});
while (!stack.isEmpty()) {
int[] pop = stack.pop();
if (pop[0] < pop[1]) {
// 随机交换最右边数值
Sort.swap(arr, pop[0] + (int)(Math.random() * (pop[1] - pop[0] + 1)), pop[1]);
int[] p = partition2(arr, pop[0], pop[1]);
stack.add(new int[]{pop[0], p[0] - 1});
stack.add(new int[]{p[1] + 1, pop[1]});
}
}
}
}
2.5、归并排序-merge
package sort;
/**
* @author zll
* @description 每次分一半为左右, 左右继续分一半, 直至最小粒度,比较大小成有序, 开始合并左右成为有序,直至最后有序
*/
public class SortMerge {
public static void sort(int[] arr) {
if (arr == null || arr.length <= 1) {
return;
}
process(arr, 0, arr.length -1);
}
private static void process(int[] arr, int L, int R) {
if (L >= R) {
return;
}
int mid = L + ((R - L) >> 2);
process(arr, L, mid);
process(arr, mid+1, R);
merge(arr, L, mid, R);
}
/**
* 合并数组两边,按照大小
* @param arr 数组
* @param from 左
* @param mid 中
* @param to 右
*/
private static void merge(int[] arr, int from, int mid, int to) {
int[] tempArr = new int[to - from + 1];
int L = from, R = mid + 1;
int index = 0;
// 比较左右数组大小
while (L <= mid && R <= to) {
tempArr[index++] = arr[L] > arr[R] ? arr[R++] : arr[L++];
}
// 左边还剩余
while (L <= mid) {
tempArr[index++] = arr[L++];
}
// 右边还剩余
while (R <= to) {
tempArr[index++] = arr[R++];
}
// 替换原始数组
for (int i = 0; i < tempArr.length; i++) {
arr[from + i] = tempArr[i];
}
}
public static void sortNotRecursive(int[] arr) {
if (arr == null || arr.length <= 1) {
return;
}
int step = 1;
int N = arr.length;
while (step < N) {
int L = 0;
while (L < N) {
// 取得中间值(-1是要把L位置算上)
int M = L + step - 1;
if (M >= N - 1) {
break;
}
// 右边界
int R = M + step;
if (R > N - 1) {
R = N - 1;
}
merge(arr, L, M, R);
// 跳转下一组
L = R + 1;
}
// 2的倍数增长
step *= 2;
}
}
}
2.6、基数排序-radix
/**
* @author zll
* @description 基数排序 - 基于桶排序
* 基于 0 ~ 9 - 10个桶
* 从低位 -> 高位的数字依次进桶(队列) (从右向左进桶)
* 从低位比较,到高位比较
*/
public class RadixSort {
/**
* 排序
* @param arr
*/
public static void sort(int[] arr) {
if (arr == null || arr.length <= 1) {
return ;
}
int length = arr.length;
//思路:
// 1.取得最大数据的位数
int d = maxBits(arr);
int radix = 10;
int[] help = new int[length];
for (int i = 1; i <= d; i++) {
// 2.从左往右从个位到高位,把数据累计到0~9的数组中C
int[] count = new int[radix];
for (int j = 0; j < length; j++) {
int digit = getDigit(arr[j], i);
count[digit]++;
}
// 3.然后计算0~9的数组的累加和C,保证值小的获得小的索引
for (int k = 1; k < radix; k++) {
count[k] = count[k] + count[k - 1];
}
// 4.然后数组从右往左,个位到高位的数据落入到0~9数组A中
// 5.取得上一步桶中的数值x,即是<=x的数有多少个,然后把最右边取得得数放入0~x下标的数组中x位置,相应的C累计和-1
for (int m = length - 1; m >= 0 ; m--) {
int digit = getDigit(arr[m], i);
int x = count[digit];
if (x > 0) {
// 获取出去排序的值
help[x - 1] = arr[m];
// 索引被占删除
count[digit]--;
}
}
// 排好序的写回原始数据
for (int j = 0; j < help.length; j++) {
arr[j] = help[j];
}
}
}
/**
* 数组中数值最大的位数
* @param arr 数组
*/
private 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) {
max /= 10;
res++;
}
return res;
}
/**
* 获取数字x第d位数值
* @param x 数值
* @param d 位数
* @return int
*/
private static int getDigit(int x, int d) {
return ((x / ((int) Math.pow(10, d - 1))) % 10);
}
public static void main(String[] args) {
int testCount = 100000;
int maxVal = 200;
int maxSize = 200;
for (int i = 0; i < testCount; i++) {
int[] arr = ArrayUtil.randomPositiveArr(maxSize, maxVal);
int[] arr1 = ArrayUtil.copyArr(arr);
sort(arr);
Arrays.sort(arr1);
boolean equal = ArrayUtil.isEqual(arr, arr1);
if (!equal) {
System.out.println("Opos!");
break;
}
}
System.out.println("finished!!");
}
}
2.7、计数排序-count
/**
* @author zll
* @description 计数排序 - 基于桶的排序
* 基于样本, 比较少的类型, 比如年龄,可以细化到 0-200岁, 创建201个桶
*/
public class CountSort {
/**
* only for 0~200 value, 超过200也不会报错,但是效率会变低
* @param arr
*/
public static void sort(int[] arr) {
if (arr == null || arr.length < 2) {
return ;
}
// 找出arr的最大值 / 直接创建201个桶,省去数组循环的次数(这个就只适用于0-200了)
int max = Integer.MIN_VALUE;
int length = arr.length;
for (int i = 0; i < length; i++) {
max = Math.max(max, arr[i]);
}
int[] bucket = new int[max + 1];
for (int i = 0; i < length; i++) {
bucket[arr[i]]++;
}
int j = 0;
for (int i = 0; i < bucket.length; i++) {
while (bucket[i]-- > 0) {
arr[j++] = i;
}
}
}
public static void main(String[] args) {
int testCount = 100000;
int maxVal = 200;
int maxSize = 200;
for (int i = 0; i < testCount; i++) {
int[] arr = ArrayUtil.randomPositiveArr(maxSize, maxVal);
int[] arr1 = ArrayUtil.copyArr(arr);
sort(arr);
Sorted.selectSort(arr1);
boolean equal = ArrayUtil.isEqual(arr, arr1);
if (!equal) {
System.out.println("Opos!");
break;
}
}
System.out.println("finished!!");
}
}
2.8、堆排序
package com.algorithm.algorithm.basic.sort;
import com.algorithm.algorithm.utils.ArrayUtil;
import java.util.Arrays;
/**
* @author zll
* @description 堆的排序
*/
public class HeapSort {
/**
* 堆排序
* @param arr
*/
public static void sort(int[] arr) {
if (arr == null || arr.length < 2) {
return ;
}
// 首先做成大顶堆,这个一般一下数据
for (int i = arr.length - 1; i >= 0;i--) {
heapify(arr, i, arr.length);
}
int heapSize = arr.length;
// 把最大值交换到数组最末
ArrayUtil.swap(arr, 0, --heapSize);
// O(N*logN)
while (heapSize > 0) { // O(N)
heapify(arr, 0, heapSize); // O(logN)
// 继续交换最大数字
ArrayUtil.swap(arr, 0, --heapSize); // O(1)
}
}
/**
* 往下沉, 从下往上一次不断找自己下面的孩子是否比自己大
* @param arr
* @param index
* @param heapSize
*/
public static void heapify(int[] arr, int index, int heapSize) {
// 从下往上一次不断找自己下面的孩子是否比自己大
int leftIndex = index * 2 + 1;
while (leftIndex < heapSize) {
// 找出来右孩子
int rightIndex = leftIndex + 1;
// 如果右孩子存在并且既大于左孩子又大于index值则取右孩子的下标, 否则如果左孩子大于index值则取左孩子下标, 否还是index
int largeIndex = rightIndex < heapSize &&
arr[rightIndex] > arr[leftIndex] &&
arr[rightIndex] > arr[index] ?
rightIndex :
(arr[leftIndex] > arr[index] ? leftIndex : index);
if (largeIndex == index) {
break;
}
ArrayUtil.swap(arr, index, largeIndex);
index = largeIndex;
leftIndex = index * 2 + 1;
}
}
public static void main(String[] args) {
System.out.println("开始");
for (int i = 0; i < 500000; i++) {
int[] arr = ArrayUtil.randomArr(100, 1000);
int[] arr1 = ArrayUtil.copyArr(arr);
Arrays.sort(arr);
sort(arr1);
if (!ArrayUtil.isEqual(arr, arr1)) {
System.out.println("错误了");
ArrayUtil.printArr(arr);
ArrayUtil.printArr(arr1);
break;
}
}
System.out.println("结束");
}
}