根据 左神课程 整理补充的排序知识!
如果有帮助,记得三连哦!
Created: November 17, 2022 10:53 AM
• 时间O(N^2) 、空间O(1)、不稳
public void selectSort(int[] arr) {
if (arr == null || arr.length < 2) return;
for (int i = 0; i < arr.length; i++) { // i - N-1
int minValue = i;
for (int j = i + 1; j < arr.length; j++) { // i - N-1 找到最小值的下标
minValue = arr[minValue] > arr[j] ? j : minValue;
}
swap(arr, minValue, i); //最小值放最左边
}
}
private void swap(int[] arr, int minValue, int i) {
int temp = arr[minValue];
arr[minValue] = arr[i];
arr[i] = temp;
}
• 时间O(N^2) 、空间O(1)、稳
private void swap(int[] arr, int i, int j) {
arr[i] = arr[i] ^ arr[j];
arr[j] = arr[i] ^ arr[j];
arr[i] = arr[i] ^ arr[j];
}
public void bubbleSort(int[] arr) {
if (arr == null || arr.length < 2) return;
for (int i = arr.length - 1; i > 0; i--) { // 玩N轮
for (int j = 0; j < i; j++) { //每轮将当前和下一个比大小,谁大谁往右
if (arr[j] > arr[j + 1]) {
swap(arr, j, j + 1);
}
}
}
}
public void insertionSort(int[] arr) {
if (arr == null || arr.length < 2) return;
// 0~0上有序,0~i想有序
for (int i = 0; i < arr.length; i++) { // 0 - i做到有序
for (int j = i - 1; j >= 0 && arr[j] > arr[j + 1]; j--) {
swap(arr, j, j + 1);
}
}
}
private void swap(int[] arr, int i, int j) {
arr[i] = arr[i] ^ arr[j];
arr[j] = arr[i] ^ arr[j];
arr[i] = arr[i] ^ arr[j];
}
• 时间O(NlogN)、空间O(N)、稳
public void mergeSort(int arr[]) {
process(arr, 0, arr.length - 1);
}
public void process(int[] arr, int L, int R) {
if (L == R) return; //只有一个数字就不处理
int mid = L + ((R - L) >> 1);//中点
process(arr, L, mid); //左边排好序
process(arr, mid + 1, R); //右边排好序
merge(arr, L, mid, R);//利用外排序将左右两边排好序
}
private void merge(int[] arr, int L, int M, int R) {
int[] help = new int[R- L + 1]; //外排序的辅助空间
int i = 0; //外排序的索引
int p1 = L; //左边排好序的索引
int p2 = M + 1; //右边排好序的索引
//左右比较,谁小放进去,总有一个会越界
while (p1 <= M && p2 <= R) {
//相等的情况下先左后右
help[i++] = arr[p1] <= arr[p2] ? arr[p1++] : arr[p2++];
}
//肯定有越界的情况,但是help肯定不满,下面情况只会中一个
while (p1 <= M) {
help[i++] = arr[p1++];
}
while (p2 <= R) {
help[i++] = arr[p2++];
}
//将外排序的东西返回原数组
for (int j = 0; j < help.length; j++) {
arr[L + j] = help[j];
}
}
• 时间O(NlogN) 、空间O(logN)、不稳
public void quicksort(int arr[], int l, int r) {
if (l < r) {
//随机抽取一个放到最后面作为比较值
swap(arr, l + (int) (Math.random() * (r - l + 1)), r);
//荷兰国旗问题,最后一个值作为v,p代表左右边界
int[] p = partition(arr, l, r);
//对<区域
quicksort(arr, l, p[0] - 1);
//对>区域
quicksort(arr, p[1] + 1, r);
}
}
public int[] partition(int arr[], int l, int r) {
// input: 3,6,2,5,7,5
int less = l - 1;//< 区域
//这里r是因为,最后一个r表示荷兰问题中value
//l ~ r-1 之前表示荷兰问题
int more = r;//> 区域
//结束条件是 指针和>区域相遇
while (l < more) {
if (arr[l] < arr[r]) {
swap(arr, l++, ++less);
} else if (arr[l] > arr[r]) {
swap(arr, l, --more);
}else {
l++;
}
}
// 荷兰问题之后:3, 2, 5, 7, 6, 5
//将最后一个值和>区域的第一个值交换,因为最后一个是=区域的
swap(arr,r,more);
// 交换完之后: 3, 2, 5, 5, 6, 7
return new int[]{less+1,more};
}
public void swap(int[] arr, int i, int j) {
int temp = arr[i];
arr[i] = arr[j];
arr[j] = temp;
}
• 时间O(NlogN)、空间O(1)、不稳
public void heapSort(int[] arr) {
if (arr == null || arr.length < 2) return;
//将数组变为堆
for (int i = 0; i < arr.length; i++) {
heapInsert(arr, i);
}
int heapsize = arr.length;
//最大的在堆顶,放到后面,heapsize--,继续堆化调整
swap(arr, 0, --heapsize);
while (heapsize > 0) {
heapify(arr, 0, heapsize);
swap(arr, 0, --heapsize);
}
}
public void heapInsert(int[] arr, int i) {
while (arr[i] > arr[(i - 1) / 2]) {
swap(arr, i, (i - 1) / 2);
i = (i - 1) / 2;
}
}
public void heapify(int[] arr, int i, int heapsize) {
int left = i * 2 + 1;
while (left < heapsize) {
//孩子比较
int largest = left + 1 < heapsize && arr[left + 1] > arr[left] ? left + 1 : left;
//父子比较
largest = arr[largest] > arr[i] ? largest : i;
//父已经是最大,不用再调整了
if (i == largest) break;
//交换调整
swap(arr, largest, i);
//下一个调整位置
i = largest;
left = i * 2 + 1;
}
}
public void swap(int[] arr, int i, int j) {
int temp = arr[i];
arr[i] = arr[j];
arr[j] = temp;
}
public void countingSort(int[] arr) {
int min = arr[0];
int max = arr[0];
for (int i = 0; i < arr.length; i++) {
min = Math.min(arr[i], min);
max = Math.max(arr[i], max);
}
//生成对应的词频表
int[] words = new int[max - min + 1];
for (int i = 0; i < arr.length; i++) {
words[arr[i] - min]++;
}
//生成前缀和
for (int i = 1; i < words.length; i++) {
words[i] += words[i - 1];
}
int[] sorted = new int[arr.length];
//逆序输出
for (int i = arr.length - 1; i >= 0; i--) {
//查询词汇表中,arr[i]的下标为词频数量--
int index = --words[arr[i] - min];
sorted[index] = arr[i];
}
for (int i = 0; i < arr.length; i++) {
arr[i] = sorted[i];
}
}
public void radixSort(int[] arr) {
int max = Integer.MAX_VALUE;
for (int i = 0; i < arr.length; i++) {
max = Math.max(arr[i], max);
}
//求得最大值的位数
int digit = 0;
while (max != 0) {
digit++;
max /= 10;
}
//对每一个位进行操作
for (int i = 0; i < digit; i++) {
//辅助空间,入桶出桶操作
int[] bucket = new int[arr.length];
//每个数字的词频
int[] words = new int[10];
//该位上的进制
int dev = (int) Math.pow(10, i);
//统计所有数字的该位的词频
for (int j = 0; j < arr.length; j++) {
words[arr[j] / dev % 10]++;
}
//前缀和
for (int j = 1; j < 10; j++) {
words[j] += words[j - 1];
}
//逆序输出,查询词频表的数量--就是下标位置
for (int j = arr.length - 1; j >= 0; j--) {
bucket[--words[arr[j] / dev % 10]] = arr[j];
}
for (int j = 0; j < arr.length; j++) {
arr[j] = bucket[j];
}
}
}