912. 排序数组
给你一个整数数组 nums,请你将该数组升序排列。
示例 1:
输入:nums = [5,2,3,1]
输出:[1,2,3,5]
示例 2:
输入:nums = [5,1,1,2,0,0]
输出:[0,0,1,1,2,5]
提示:
1 <= nums.length <= 5 * 104
-5 * 104 <= nums[i] <= 5 * 104
目录
01-问题分析
02-时间复杂度为O(n²)的排序算法
1. 选择排序
2. 冒泡排序
3. 插入排序
03-时间复杂度为O(nlogn)的排序算法
1. 快速排序
2. 归并排序
3. 堆排序
04-桶排序
1. 计数排序
2. 基数排序
注:以下3种排序算法时间复杂度较高,无法通过本题
class Solution {
public void selectionSort(int[] arr) {
if (arr == null || arr.length < 2) {
return;
}
for (int i = 0; i < arr.length - 1; i++) {// i ~ N-1
int minIndex = i;
for (int j = i; j < arr.length; j++) {// i ~ N-1 上找最小值的下标
minIndex = arr[j] < arr[minIndex] ? j : minIndex;
}
swap(arr, i, minIndex);
}
}
public void swap(int[] arr, int i, int j) {
int tmp = arr[i];
arr[i] = arr[j];
arr[j] = tmp;
}
public int[] sortArray(int[] nums) {
selectionSort(nums);
return nums;
}
}
class Solution {
public void bubbleSort(int[] arr) {
if (arr == null || arr.length < 2) {
return;
}
for (int i = 0; i < arr.length - 1; i++) {
for (int j = 0; j < arr.length - 1 - i; j++) {
if (arr[j] > arr[j + 1]) {
swap(arr, j, j + 1);
}
}
}
}
// i和j是一个位置的话,会出错
public 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 int[] sortArray(int[] nums) {
bubbleSort(nums);
return nums;
}
}
class Solution {
public void insertionSort(int[] arr) {
if (arr == null || arr.length < 2) {
return;
}
// 0~0 有序的
// 0~i 想有序
for (int i = 1; i < arr.length; i++) {// 0~i 做到有序
for (int j = i - 1; j >= 0 && arr[j] > arr[j + 1]; j--) {
swap(arr, j , j + 1);
}
}
}
// i和j是一个位置的话,会出错
public 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 int[] sortArray(int[] nums) {
insertionSort(nums);
return nums;
}
}
class Solution {
public void quickSort(int[] arr) {
if (arr == null || arr.length < 2) {
return;
}
process(arr, 0, arr.length - 1);
}
// 将arr[L..R]排好序
public void process(int[] arr, int L, int R) {
if (L < R) {
swap(arr, L + (int)(Math.random() * (R - L + 1)), R); // 要想通过本题,需要使用随机值拆分数组
int[] p = partition(arr, L, R);
process(arr, L, p[0] - 1); // 小于区域
process(arr, p[1] + 1, R);// 大于区域
}
}
// 这是一个处理arr[L..R]的函数
// 默认以arr[R]做划分 arr[R] -> p
// 返回一个两个元素的int数组,为等于区域的左边界和右边界
public int[] partition(int[] arr, int L, int R) {
int lt = L;// 小于区域的后一个位置
int gt = R - 1;// 大于区域的前一个位置
while (L <= gt) {// L为当前位置 arr[R]为划分值
if (arr[L] < arr[R]) {
// 当前数比划分值小,交换当前数和小于区域后一个数。当前位置后移,小于区域后移。
swap(arr, lt++, L++);
} else if (arr[L] > arr[R]) {
// 当前数比划分值大,交换当前数和大于区域前一个数。当前位置不动,大于区域前移。
swap(arr, gt--, L);
} else {
// 当前数和划分值相等,当前位置后移,同时等于区域后移。
L++;
}
}
swap(arr, gt + 1, R);
return new int[]{lt, gt + 1};
}
public void swap(int[] arr, int i, int j) {
if (i == j) {
return;
}
arr[j] ^= arr[i] ^= arr[j];
arr[i] ^= arr[j];
}
public int[] sortArray(int[] nums) {
quickSort(nums);
return nums;
}
}
class Solution {
public void mergeSort(int[] arr) {
if (arr == null || arr.length < 2) {
return;
}
process(arr, 0, arr.length - 1);
}
// 请把arr[L..R]排有序
// l...r N
// T(N) = 2 * T(N / 2) + O(N)
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);
}
public static 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++];
}
// 要么p1越界了,要么p2越界了
while (p1 <= M) {
help[i++] = arr[p1++];
}
while (p2 <= R) {
help[i++] = arr[p2++];
}
for (i = 0; i < help.length; i++) {
arr[L + i] = help[i];
}
}
public int[] sortArray(int[] nums) {
mergeSort(nums);
return nums;
}
}
class Solution {
public void heapSort(int[] arr) {
if (arr == null || arr.length < 2) {
return;
}
/*//顺序插入每个元素,将数组转化成大根堆 O(NlogN)
for (int i = 0; i < arr.length; i++) {//O(N)
heapInert(arr, i);//O(logN)
}*/
//让整个数组直接变成大根堆 O(N)
for (int i = arr.length - 1; i >= 0; i--) {
heapify(arr, i, arr.length);
}
int heapSize = arr.length;
swap(arr, 0, --heapSize);
while (heapSize > 0) {//O(N)
heapify(arr, 0, heapSize);//O(logN)
swap(arr, 0, --heapSize);//O(1)
}
}
//某个数现在处在index位置,向上继续移动
public void heapInert(int[] arr, int index) {
while (arr[index] > arr[(index - 1) / 2]) {
swap(arr, index, (index - 1) / 2);
index = (index - 1) / 2;
}
}
//某个数在index位置,能否往下移动
public void heapify(int[] arr, int index, int heapSize) {
int left = index * 2 + 1;//左孩子的下标
while (left < heapSize) {
//两个孩子中,谁的值大,把下标给largest
int largest = left + 1 < heapSize && arr[left + 1] > arr[left] ? left + 1 : left;
//父和较大的孩子之间,谁的值大,把下标给largest
largest = arr[largest] > arr[index] ? largest : index;
if (largest == index) {
break;
}
swap(arr, index, largest);
index = largest;
left = index * 2 + 1;
}
}
public void swap(int[] arr, int i, int j) {
int tmp = arr[i];
arr[i] = arr[j];
arr[j] = tmp;
}
public int[] sortArray(int[] nums) {
heapSort(nums);
return nums;
}
}
class Solution {
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, Math.abs(arr[i]));
}
int[][] bucket = new int[max + 1][2];
for (int i = 0; i < arr.length; i++) {
if (arr[i] >= 0) {
bucket[arr[i]][0]++;
} else {
bucket[-arr[i]][1]++;
}
}
int i = 0;
for (int j = bucket.length - 1; j > 0; j--) {
while (bucket[j][1]-- > 0) {
arr[i++] = -j;
}
}
for (int j = 0; j < bucket.length; j++) {
while (bucket[j][0]-- > 0) {
arr[i++] = j;
}
}
}
public int[] sortArray(int[] nums) {
countSort(nums);
return nums;
}
}
class Solution {
//基数排序
//范围:-50000-50000
public void radixSort(int[] arr) {
if (arr == null || arr.length < 2) {
return;
}
for (int i = 0; i < arr.length; i++) {
arr[i] += 50000;
}
radixSort(arr, 0, arr.length - 1, maxbits(arr));
}
//获取数组中最大数字的位数
public int maxbits(int[] arr) {
int max = Integer.MIN_VALUE;
for (int num : arr) {
max = Math.max(num, max);
}
int res = 0;
while (max != 0) {
res++;
max /= 10;
}
return res;
}
//获取某个数的某位置的数字
public int getDigit(int x, int d) {
return x / (int)Math.pow(10, d - 1) % 10;
}
//对一个最大位数为digit的数组,在begin到end上进行基数排序
public void radixSort(int[] arr, int begin, int end, int digit) {
final int radix = 10;
int i = 0, j = 0;
int[] bucket = new int[end - begin + 1];//桶的长度为待排序数字元素个数
for (int d = 1; d <= digit; d++) {
int[] count = new int[radix];
//使用count数组统计原数组每个元素某一位数字的出现次数
for (i = begin; i <= end; i++) {
j = getDigit(arr[i], d);
count[j]++;
}
//count数组下标为i的元素值对应原数组中当前位大于等于i的数字出现的次数
//相当于对桶进行了分区,0-count[0]下标的区域为0区域,count[0]-count[1]为1区域,... ,count[8]-count[9]为9区域
for (i = 1; i < radix; i++) {
count[i] += count[i - 1];
}
//将原数组的每个元素倒序倒入桶中对应分区
for (i = end; i >= begin; i--) {
j = getDigit(arr[i], d);
bucket[count[j]-- - 1] = arr[i];
}
//将桶中元素拷贝回原数组
for (i = begin, j = 0; i <= end; i++, j++) {
arr[i] = bucket[j];
}
}
}
public int[] sortArray(int[] nums) {
radixSort(nums);
if (nums == null || nums.length < 2) {
return nums;
}
for (int i = 0; i < nums.length; i++) {
nums[i] -= 50000;
}
return nums;
}
}