赞
踩
算法 | 最好 | 最坏 | 平均 | 空间 | 稳定 | 思想 | 注意事项 |
---|---|---|---|---|---|---|---|
冒泡 | O(n) | O(n^2) | O(n^2) | O(1) | Y | 比较 | 最好情况需要额外判断 |
选择 | O(n^2) | O(n^2) | O(n^2) | O(1) | N | 比较 | 交换次数一般少于冒泡 |
堆 | O(nlogn) | O(nlogn) | O(nlogn) | O(1) | N | 选择 | 堆排序的辅助性较强,理解前先理解堆的数据结构 |
插入 | O(n) | O(n^2) | O(n^2) | O(1) | Y | 比较 | 插入排序对于近乎有序的数据处理速度比较快,复杂度有所下降,可以提前结束 |
希尔 | O(nlogn) | O(n^2) | O(nlogn) | O(1) | N | 插入 | gap序列的构造有多种方式,不同方式处理的数据复杂度可能不同 |
归并 | O(nlogn) | O(nlogn) | O(nlogn) | O(n) | Y | 分治 | 需要额外的O(n)的存储空间 |
快速 | O(nlogn) | O(n^2) | O(nlogn) | O(logn) | N | 分治 | 快排可能存在最坏情况,需要把枢轴值选取得尽量随机化来缓解最坏情况下的时间复杂度 |
非比较排序算法 | 时间复杂度 | 空间复杂度 | 稳定性 |
---|---|---|---|
计数排序 | O(n+k) | O(n+k) | 稳定 |
桶排序 | O(n+k) | O(n+k) | 稳定 |
基数排序 | O(d*(n+k)) | O(n+k) | 稳定 |
其中,n是数组长度,k是桶长度,d是基数位数。
Arrays.sort
JDK 7 ~ 13中的排序实现
排序目标 | 条件 | 采用算法 |
---|---|---|
int[] long[] float[] double[] | size < 47 | 混合插入排序 (pair) |
size < 286 | 双基准点快排 | |
有序度低 | 双基准点快排 | |
有序度高 | 归并排序 | |
byte[] | size <= 29 | 插入排序 |
size > 29 | 计数排序 | |
char[] short[] | size < 47 | 插入排序 |
size < 286 | 双基准点快排 | |
有序度低 | 双基准点快排 | |
有序度高 | 归并排序 | |
size > 3200 | 计数排序 | |
Object[] | -Djava.util.Arrays.useLegacyMergeSort=true | 传统归并排序 |
TimSort(归并 + 插入) |
JDK 14~20中的排序实现
排序目标 | 条件 | 采用算法 |
---|---|---|
int[] long[] float[] double[] | size < 44 并位于最左侧 | 插入排序 |
size < 65 并不是最左侧 | 混合插入排序 (pin) | |
有序度低 | 双基准点快排 | |
递归次数超过 384 | 堆排序 | |
对于整个数组或非最左侧 size > 4096,有序度高 | 归并排序 | |
byte[] | size <= 64 | 插入排序 |
size > 64 | 计数排序 | |
char[] short[] | size < 44 | 插入排序 |
再大 | 双基准点快排 | |
递归次数超过 384 | 计数排序 | |
size > 1750 | 计数排序 | |
Object[] | -Djava.util.Arrays.useLegacyMergeSort=true | 传统归并排序 |
TimSort |
要点:
以数组3、2、1的冒泡排序为例,第一轮冒泡
第二轮冒泡
未排序区域内就剩一个元素,结束
优化手段:每次循环时,若能确定更合适的右边界,则可以减少冒泡轮数(x记录最后一次交换的位置)
以数组3、2、1、4、5为例,第一轮结束后记录的x,即为右边界
递归实现
- package com.itheima.algorithms.sort;
-
- import java.util.Arrays;
-
- public class BubbleSort {
-
- private static void bubble(int[] a) {
- helper(a, a.length - 1);
- }
-
- private static void helper(int[] a, int j) {
- if(j == 0) {
- return;
- }
-
- int x = 0; // 记录最后一次交换的位置
- for(int i = 0; i < j; i++) {
- if(a[i] > a[i + 1]) {
- int t = a[i];
- a[i] = a[i + 1];
- a[i + 1] = t;
- x = i;
- }
- }
- helper(a, x);
- }
-
- public static void main(String[] args) {
- int[] a = {6, 5, 4, 3, 2, 1};
- System.out.println(Arrays.toString(a));
- bubble(a);
- System.out.println(Arrays.toString(a));
- }
- }
非递归实现
- package com.itheima.algorithms.sort;
-
- import java.util.Arrays;
-
- public class BubbleSort {
-
- private static void bubble(int[] a) {
- int j = a.length - 1;
- while(true) {
- int x = 0;
- for(int i = 0; i < j; i++) {
- if(a[i] > a[i + 1]) {
- int t = a[i];
- a[i] = a[i + 1];
- a[i + 1] = t;
- x = i;
- }
- }
- j = x; // 更新右边界
- if(j == 0) {
- // 没有发生交换,已经有序
- break;
- }
- }
- }
-
- public static void main(String[] args) {
- int[] a = {6, 5, 4, 3, 2, 1};
- System.out.println(Arrays.toString(a));
- bubble(a);
- System.out.println(Arrays.toString(a));
- }
- }
要点:
以下面的数组选择最大值为例
非递归实现
- package com.itheima.algorithms.sort;
-
- import java.util.Arrays;
-
- public class SelectionSort {
-
- public static void sort(int[] a) {
- // 1. 选择轮数 a.length - 1
- // 2. 交换的索引位置 初始a.length - 1,每次递减
- for(int right = a.length - 1; right >= 0; right--) {
- int max = right;
- for(int i = 0; i < right; i++) {
- if(a[i] > a[max]) {
- max = i;
- }
- }
- if(max != right) {
- int t = a[max];
- a[max] = a[right];
- a[right] = t;
- }
- }
- }
-
- public static void main(String[] args) {
- int[] a = {6, 5, 4, 3, 2, 1};
- System.out.println(Arrays.toString(a));
- sort(a);
- System.out.println(Arrays.toString(a));
- }
- }
要点:
建堆
交换,下潜调整
代码实现
- package com.itheima.algorithms.sort;
-
- import java.util.Arrays;
-
- public class HeapSort {
- public static void sort(int[] a) {
- // 建堆
- heapify(a, a.length);
- // 交换,下潜调整
- for (int right = a.length - 1; right > 0; right--) {
- swap(a, 0, right);
- down(a, 0, right);
- }
- }
-
- // 建堆 O(n)
- private static void heapify(int[] array, int size) {
- // 如何找到最后这个非叶子节点 size / 2 - 1
- for (int i = size / 2 - 1; i >= 0; i--) {
- down(array, i, size);
- }
- }
-
- // 下潜
- // leetcode 上数组排序题目用堆排序求解,非递归实现比递归实现大约快 6ms
- // 将 parent 索引处的元素下潜: 与两个孩子较大者交换, 直至没孩子或孩子没它大
- private static void down(int[] array, int parent, int size) {
- while (true) {
- int left = parent * 2 + 1;
- int right = left + 1;
- int max = parent;
- if (left < size && array[left] > array[max]) {
- max = left;
- }
- if (right < size && array[right] > array[max]) {
- max = right;
- }
- if (max == parent) { // 没找到更大的孩子
- break;
- }
- swap(array, max, parent);
- parent = max;
- }
- }
-
- // 交换
- private static void swap(int[] a, int i, int j) {
- int t = a[i];
- a[i] = a[j];
- a[j] = t;
- }
-
- public static void main(String[] args) {
- int[] a = {2, 3, 1, 7, 6, 4, 5};
- System.out.println(Arrays.toString(a));
- sort(a);
- System.out.println(Arrays.toString(a));
- }
- }
要点:
将数组分为两部分 [0 .. low - 1] [low .. a.length - 1]
每次从未排序区域取出 low 位置的元素,插入到已排序区域
例如,
递归实现
- package com.itheima.algorithms.sort;
-
- import org.checkerframework.checker.units.qual.A;
-
- import java.util.Arrays;
-
- public class InsertionSort {
- private static void insertion(int[] a, int low, int high) {
- if(low > high) {
- return;
- }
- int i = low - 1;
- int t = a[low];
- while(i >= 0 && a[i] > t) { // 没有找到插入位置
- a[i + 1] = a[i]; // 空出插入位置
- i--;
- }
-
- if(i + 1 != low) {
- a[i + 1] = t;
- }
- insertion(a, low + 1, high);
- }
-
- public static void main(String[] args) {
- int[] a = {9, 3, 7, 6, 2, 5, 8, 1, 4};
- System.out.println(Arrays.toString(a));
- insertion(a, 1, a.length - 1);
- System.out.println(Arrays.toString(a));
- }
- }
非递归实现
- package com.itheima.algorithms.sort;
-
- import java.util.Arrays;
-
- public class InsertionSort {
-
- public static void sort(int[] a) {
- for(int low = 1; low < a.length; low++) {
- // 将low位置的元素插入到[0 .. low - 1]的已排序区域
- int t = a[low];
- int i = low - 1;
-
- while(i >= 0 && t < a[i]) {
- a[i + 1] = a[i];
- i--;
- }
- if(i != low - 1) {
- a[i + 1] = t;
- }
- }
- }
-
- public static void main(String[] args) {
- int[] a = {9, 3, 7, 6, 2, 5, 8, 1, 4};
- System.out.println(Arrays.toString(a));
- sort(a);
- System.out.println(Arrays.toString(a));
- }
- }
要点:
下图演示了gap = 4,gap =2,gap = 1的三轮排序前后对比
代码
- package com.itheima.algorithms.sort;
-
- import java.util.Arrays;
-
- public class MergeSortTopDown {
-
- /**
- * 合并有序数组
- * @param a1
- */
- public static void sort(int[] a1) {
- int[] a2 = new int[a1.length];
- split(a1, 0, a1.length - 1, a2);
- }
-
- /**
- * 划分数据
- * @param a1
- * @param left
- * @param right
- */
- private static void split(int[] a1, int left, int right, int[] a2) {
- // 2. 治
- if(left == right) {
- return;
- }
- // 1. 分
- int m = (left + right) >>> 1;
- split(a1, left, m, a2);
- split(a1, m + 1, right, a2);
- // 3. 合
- merge(a1, left, m, m + 1, right, a2);
- System.arraycopy(a2, left, a1, left, right - left + 1);
- }
-
- /**
- *
- * @param a1 原始数组
- * @param i 第一个有序范围
- * @param iEnd
- * @param j 第二个有序范围
- * @param jEnd
- * @param a2 临时数组
- */
- public static void merge(int[] a1, int i, int iEnd, int j, int jEnd, int[] a2) {
- int k = i;
- while(i <= iEnd && j <= jEnd) {
- if(a1[i] < a1[j]) {
- a2[k] = a1[i];
- i++;
- } else {
- a2[k] = a1[j];
- j++;
- }
- k++;
- }
- // 第二个有序数组的剩余元素
- if(i > iEnd) {
- System.arraycopy(a1, j, a2, k, jEnd -j + 1);
- }
- // 第一个有序数组的剩余元素
- if(j > jEnd) {
- System.arraycopy(a1, i, a2, k, iEnd - i + 1);
- }
- }
-
- public static void main(String[] args) {
- int[] a = {9, 3, 7, 6, 2, 5, 8, 1, 4};
- System.out.println(Arrays.toString(a));
- sort(a);
- System.out.println(Arrays.toString(a));
- }
- }
要点:
代码
- package com.itheima.algorithms.sort;
-
- import java.util.Arrays;
-
- public class MergeSort {
-
- /**
- * 合并有序数组
- * @param a1
- */
- public static void sort(int[] a1) {
- int[] a2 = new int[a1.length];
- split(a1, 0, a1.length - 1, a2);
- }
-
- /**
- * 划分数据
- * @param a1
- * @param left
- * @param right
- */
- private static void split(int[] a1, int left, int right, int[] a2) {
- // 2. 治
- if(left == right) {
- return;
- }
- // 1. 分
- int m = (left + right) >>> 1;
- split(a1, left, m, a2);
- split(a1, m + 1, right, a2);
- // 3. 合
- merge(a1, left, m, m + 1, right, a2);
- System.arraycopy(a2, left, a1, left, right - left + 1);
- }
-
- /**
- *
- * @param a1 原始数组
- * @param i 第一个有序范围
- * @param iEnd
- * @param j 第二个有序范围
- * @param jEnd
- * @param a2 临时数组
- */
- public static void merge(int[] a1, int i, int iEnd, int j, int jEnd, int[] a2) {
- int k = i;
- while(i <= iEnd && j <= jEnd) {
- if(a1[i] < a1[j]) {
- a2[k] = a1[i];
- i++;
- } else {
- a2[k] = a1[j];
- j++;
- }
- k++;
- }
- // 第二个有序数组的剩余元素
- if(i > iEnd) {
- System.arraycopy(a1, j, a2, k, jEnd -j + 1);
- }
- // 第一个有序数组的剩余元素
- if(j > jEnd) {
- System.arraycopy(a1, i, a2, k, iEnd - i + 1);
- }
- }
-
- public static void main(String[] args) {
- int[] a = {9, 3, 7, 6, 2, 5, 8, 1, 4};
- System.out.println(Arrays.toString(a));
- sort(a);
- System.out.println(Arrays.toString(a));
- }
- }
两个长度为m和n的链表合并,时间复杂度是m + n
归并,时间复杂度:f(n) = 2f(n / 2) + n, f(1) = c,等价解 f(n) = nlog_2(n) + cn
- package com.itheima.algorithms.sort;
-
- import java.util.Arrays;
-
- public class MergeSortBottomUp {
- /**
- *
- * @param a1 原始数组
- * @param i 第一个有序范围
- * @param iEnd
- * @param j 第二个有序范围
- * @param jEnd
- * @param a2 临时数组
- */
- public static void merge(int[] a1, int i, int iEnd, int j, int jEnd, int[] a2) {
- int k = i;
- while(i <= iEnd && j <= jEnd) {
- if(a1[i] < a1[j]) {
- a2[k] = a1[i];
- i++;
- } else {
- a2[k] = a1[j];
- j++;
- }
- k++;
- }
- // 第二个有序数组的剩余元素
- if(i > iEnd) {
- System.arraycopy(a1, j, a2, k, jEnd -j + 1);
- }
- // 第一个有序数组的剩余元素
- if(j > jEnd) {
- System.arraycopy(a1, i, a2, k, iEnd - i + 1);
- }
- }
-
- public static void sort(int[] a1) {
- int n= a1.length;
- int[] a2 = new int[n];
- // width代表有序区间的宽度,取值依次为1、2、4、...
- for(int width = 1; width < n; width *= 2) {
- // [left, right]分别代表待合并区间的左右边界
- for (int left = 0; left < n ; left += 2 * width) {
- int right = Math.min(left + 2 * width - 1, n - 1);
- // System.out.printf("width %d [%d, %d] %n", width, left, right);
- int mid = Math.min(left + width - 1, n - 1);
- merge(a1, left, mid, mid + 1, right, a2);
- }
- // 从a2复制到a1
- System.arraycopy(a2, 0, a1,0, n);
- }
- }
-
- public static void main(String[] args) {
- int[] a = {8, 3, 6, 2, 5, 7, 1, 4};
- System.out.println(Arrays.toString(a));
- sort(a);
- System.out.println(Arrays.toString(a));
- }
- }
基本思路:
1. 选择阈值:设定一个阈值(如,32等),当子数组的大小小于该阈值时,使用插入排序进行排序;否则,使用归并排序。
2. 实现过程:
3. 优势:
- package com.itheima.algorithms.sort;
-
- import java.util.Arrays;
-
- public class MergeInsertionSort {
-
- /**
- * 插入排序
- * @param a
- * @param left
- * @param right
- */
- public static void insertionSort(int[] a, int left, int right) {
- for(int low = left + 1; low <= right; low++) {
- // 将low位置的元素插入到[0 .. low - 1]的已排序区域
- int t = a[low];
- int i = low - 1;
-
- while(i >= left && t < a[i]) {
- a[i + 1] = a[i];
- i--;
- }
- if(i != low - 1) {
- a[i + 1] = t;
- }
- }
- }
-
-
- /**
- * 归并排序
- * @param a1
- */
- public static void sort(int[] a1) {
- int[] a2 = new int[a1.length];
- split(a1, 0, a1.length - 1, a2);
- }
-
- /**
- * 划分数据
- * @param a1
- * @param left
- * @param right
- */
- private static void split(int[] a1, int left, int right, int[] a2) {
- // 2. 治
- // 当子数组的大小小于阈值时,使用插入排序,否则使用归并排序
- if(right - left <= 32) {
- // 插入排序
- insertionSort(a1, left, right);
- return;
- }
- // 1. 分
- int m = (left + right) >>> 1;
- split(a1, left, m, a2);
- split(a1, m + 1, right, a2);
- // 3. 合
- merge(a1, left, m, m + 1, right, a2);
- System.arraycopy(a2, left, a1, left, right - left + 1);
- }
-
- /**
- * 合并有序数组
- * @param a1 原始数组
- * @param i 第一个有序范围
- * @param iEnd
- * @param j 第二个有序范围
- * @param jEnd
- * @param a2 临时数组
- */
- public static void merge(int[] a1, int i, int iEnd, int j, int jEnd, int[] a2) {
- int k = i;
- while(i <= iEnd && j <= jEnd) {
- if(a1[i] < a1[j]) {
- a2[k] = a1[i];
- i++;
- } else {
- a2[k] = a1[j];
- j++;
- }
- k++;
- }
- // 第二个有序数组的剩余元素
- if(i > iEnd) {
- System.arraycopy(a1, j, a2, k, jEnd -j + 1);
- }
- // 第一个有序数组的剩余元素
- if(j > jEnd) {
- System.arraycopy(a1, i, a2, k, iEnd - i + 1);
- }
- }
-
- public static void main(String[] args) {
- int[] a = {9, 3, 7, 6, 2, 5, 8, 1, 4};
- System.out.println(Arrays.toString(a));
- sort(a);
- System.out.println(Arrays.toString(a));
- }
- }
单边循环(lomuto分区)要点:
选择最右侧元素作为基准点
j 找比基准点小的,i 找比基准点大的,一旦找到,二者进行交换
交换时机:j 找到小的,且与 i 不相等
i 找到 >= 基准点元素后,不应自增
最后基准点与 i 交换,i 即为基准点最终索引
例:
i和j都从左边出发向右查找,i找到比基准点4大的5,j找到比基准点小的2,停下来交换
i找到了比基准点大的5,j找到比基准点小的3,停下来交换
j到达right处结束,right与i交换,一轮分区结束
代码:
- package com.itheima.algorithms.sort;
-
- import java.util.Arrays;
-
- /**
- * 单边循环快排(lomuto洛穆托分区方案)
- * 核心思想:每轮找到一个基准点元素,把比他小的放在它左边,比它大的放在它右边,这称为分区
- * 1. 选择最右边的元素作为基准点元素
- * 2. j指针负责找到比基准点小的元素,一旦找到则将j位置的元素与i位置的元素交换
- * 3. i指针指向大于基准点元素的左边界,也是每次交换的目标索引
- * 4. 最后基准点与i位置的元素交换,i即为分区位置
- */
- public class QuickSortLomuto {
-
- public static void sort(int[] a) {
- quick(a, 0, a.length - 1);
- }
-
- private static void quick(int[] a, int left, int right) {
- if(left < right) {
- // 进行分区并获取基准元素的最终位置
- int pivotIndex = partition(a, left, right);
- // 递归排序基准元素左边的子数组
- quick(a, left, pivotIndex - 1);
- // 递归排序基准元素右边的子数组
- quick(a, pivotIndex + 1, right);
- }
- }
-
- /**
- * 分区
- * @param a
- * @param left
- * @param right
- */
- private static int partition(int[] a, int left, int right) {
- // 1. 选择最右边的元素作为基准点
- int pivot = a[right];
- // 2. 初始化i指针
- int i = left;
- for(int j = left; j < right; j++) {
- // 3. j 找到比基准元素小的了,交换i和j位置的元素
- if(a[j] < pivot) {
- if(i != j) {
- swap(a, i, j);
- }
- i++;
- }
- }
- swap(a, i, right);
- return i;
- }
-
- private static void swap(int[] a, int i, int j) {
- int t = a[i];
- a[i] = a[j];
- a[j] = t;
- }
-
-
- public static void main(String[] args) {
- int[] a = {2, 3, 1, 7, 6, 4, 5};
- System.out.println(Arrays.toString(a));
- sort(a);
- System.out.println(Arrays.toString(a));
- }
- }
双边循环要点:
例:
选择最左侧的4作为基准点,i找到比基准点大的5停下来,j找到比基准点小的1停下来(包含等于),二者交互
i找到8,j找到3,二者交互;i找到7,j找到2,二者交互
i == j,则退出循环,基准点与i位置的元素交互
代码
- package com.itheima.algorithms.sort;
-
- import java.util.Arrays;
-
- public class QuickSortHoare {
-
- public static void sort(int[] a) {
- quick(a, 0, a.length - 1);
- }
-
- private static void quick(int[] a, int left, int right) {
- if (left >= right) {
- return;
- }
- int p = partition(a, left, right);
- quick(a, left, p - 1);
- quick(a, p + 1, right);
- }
-
- /**
- *
- * @param a
- * @param left
- * @param right
- * @return
- */
- private static int partition(int[] a, int left, int right) {
- // i找比基准点大的,j找比基准点小的
- int i = left, j = right;
- int pivot = a[left];
- while(i < j) {
- // 先右侧扫描
- while(i < j && a[j] > pivot) {
- j--;
- }
- // 后左侧扫描(顺序不能调换)
- while(i < j && pivot >= a[i]) {
- i++;
- }
- // 一旦找到,二者进行交换
- swap(a, i, j);
- }
- // 最后基准点与i交换,i即为基准点最终索引
- swap(a, left, i);
- return i;
- }
-
- private static void swap(int[] a, int i, int j) {
- int t = a[i];
- a[i] = a[j];
- a[j] = t;
- }
-
- public static void main(String[] args) {
- int[] a = {2, 3, 1, 7, 6, 4, 5};
- System.out.println(Arrays.toString(a));
- sort(a);
- System.out.println(Arrays.toString(a));
- }
- }
使用随机数作为基准点,避免万一最大值或最小值作为基准点导致的分区不平衡
例
改进代码
- // 随机基准点
- int idx = ThreadLocalRandom.current().nextInt(right - left + 1) + left;
- // 将left与idx位置的元素交换
- swap(a, idx, left);
如果重复值较多,则原来算法中分区效果也不好,如下图中左侧所示,需要想办法改为右侧的分区效果。
改进代码
- package com.itheima.algorithms.sort;
-
- import java.util.Arrays;
- import java.util.concurrent.ThreadLocalRandom;
-
- public class QuickSortHandleDuplicate {
-
-
- public static void sort(int[] a) {
- quick(a, 0, a.length - 1);
- }
-
- private static void quick(int[] a, int left, int right) {
- if (left >= right) {
- return;
- }
- int p = partition(a, left, right);
- quick(a, left, p - 1);
- quick(a, p + 1, right);
- }
-
- /**
- * 循环内
- * i从left + 1开始,从左向右找大的或相等的
- * j从right开始,从右向左找小的或相等的
- * 交换, i++ j--
- * 循环外,j和基准点交换,j即为分区位置
- * @param a
- * @param left
- * @param right
- * @return
- */
- private static int partition(int[] a, int left, int right) {
- // 随机基准点
- int idx = ThreadLocalRandom.current().nextInt(right - left + 1) + left;
- // 将left与idx位置的元素交换
- swap(a, idx, left);
- // 从left + 1开始
- int i = left + 1, j = right;
- int pivot = a[left];
- while(i <= j) {
- // i从左到右找大的或相等的
- while(i <= j && a[i] < pivot) {
- i++;
- }
- // j从右到左找小的或者相等的
- while(i <= j && a[j] > pivot) {
- j--;
- }
-
- if(i <= j) {
- swap(a, i, j);;
- i++;
- j--;
- }
- }
- // 最后基准点与i交换,i即为基准点最终索引
- swap(a, j, left);
- return j;
- }
-
- private static void swap(int[] a, int i, int j) {
- int t = a[i];
- a[i] = a[j];
- a[j] = t;
- }
-
- public static void main(String[] args) {
- int[] a = {2, 3, 1, 7, 6, 4, 5};
- System.out.println(Arrays.toString(a));
- sort(a);
- System.out.println(Arrays.toString(a));
- }
- }
- package com.itheima.algorithms.sort;
-
- import java.util.Arrays;
- import java.util.concurrent.ThreadLocalRandom;
-
- public class QuickSortHandleDuplicate {
-
- /**
- * 插入排序
- * @param a
- * @param left
- * @param right
- */
- public static void insertionSort(int[] a, int left, int right) {
- for(int low = left + 1; low <= right; low++) {
- // 将low位置的元素插入到[0 .. low - 1]的已排序区域
- int t = a[low];
- int i = low - 1;
-
- while(i >= left && t < a[i]) {
- a[i + 1] = a[i];
- i--;
- }
- if(i != low - 1) {
- a[i + 1] = t;
- }
- }
- }
-
- /**
- * 快速排序
- * @param a
- */
- public static void sort(int[] a) {
- quick(a, 0, a.length - 1);
- }
-
- private static void quick(int[] a, int left, int right) {
- if (right - left <= 32) {
- insertionSort(a, left, right);
- return;
- }
- int p = partition(a, left, right);
- quick(a, left, p - 1);
- quick(a, p + 1, right);
- }
-
- /**
- * 循环内
- * i从left + 1开始,从左向右找大的或相等的
- * j从right开始,从右向左找小的或相等的
- * 交换, i++ j--
- * 循环外,j和基准点交换,j即为分区位置
- * @param a
- * @param left
- * @param right
- * @return
- */
- private static int partition(int[] a, int left, int right) {
- // 随机基准点
- int idx = ThreadLocalRandom.current().nextInt(right - left + 1) + left;
- // 将left与idx位置的元素交换
- swap(a, idx, left);
- // 从left + 1开始
- int i = left + 1, j = right;
- int pivot = a[left];
- while(i <= j) {
- // i从左到右找大的或相等的
- while(i <= j && a[i] < pivot) {
- i++;
- }
- // j从右到左找小的或者相等的
- while(i <= j && a[j] > pivot) {
- j--;
- }
-
- if(i <= j) {
- swap(a, i, j);;
- i++;
- j--;
- }
- }
- // 最后基准点与i交换,i即为基准点最终索引
- swap(a, j, left);
- return j;
- }
-
- private static void swap(int[] a, int i, int j) {
- int t = a[i];
- a[i] = a[j];
- a[j] = t;
- }
-
- public static void main(String[] args) {
- int[] a = {2, 3, 1, 7, 6, 4, 5};
- System.out.println(Arrays.toString(a));
- sort(a);
- System.out.println(Arrays.toString(a));
- }
- }
性能对比
要点
- package com.itheima.algorithms.sort;
-
- import java.util.Arrays;
-
- /**
- * 计数排序
- */
- public class CountingSort {
-
- /*
- 要点
- 1. 让原始数组的最小值映射到 count[0] 最大值映射到 count 最右侧
- 2. 原始数组元素 - 最小值 = count 索引
- 3. count 索引 + 最小值 = 原始数组元素
- */
-
- public static void main(String[] args) {
- int[] a = {5, 1, 1, 3, 0, -1};
- System.out.println(Arrays.toString(a));
- sort(a);
- System.out.println(Arrays.toString(a));
- }
-
-
- // 2N + K n*log(n)
- /*
- {5, 1, 1, 3, 0, -1} 原始数组 a
- [1 1 2 0 1 0 1 ] count
- 0 1 2 3 4 5 6
- -1 0 1 3 5
- */
-
- private static void sort(int[] a) {
- int max = a[0];
- int min = a[0];
- for (int i = 1; i < a.length; i++) {
- if(a[i] > max) {
- max = a[i];
- }
- if(a[i] < min) {
- min = a[i];
- }
- }
- int[] count = new int[max - min + 1];
-
- // v原始数组元素 - 最小值 = count索引
- for (int v : a) {
- count[v - min]++;
- }
-
- int k = 0;
- for (int i = 0; i < count.length; i++) {
- // i + min代表原始数组元素,count[i]出现次数
- while(count[i] > 0) {
- a[k++] = i + min;
- count[i]--;
- }
- }
- }
- }
性能对比
针对byte[],因为数据范围已知,省去了求最大、最小值的过程,java中对char[]、short[]、byte[]的排序都可能采用counting排序。
- public static void sort(byte[] a) {
- int[] count = new int[256];
- for (int i : a) {
- // 记录每个字节值的出现次数
- // i & 0xFF是为了将byte转换为int类型,这样可以有效地处理负值
- // 因为字节的值范围是-128到127,而count数组的索引应该在0-255之间
- count[i & 0xFF]++;
- }
- // 反向填充排序后的值
- int k = a.length - 1;
- for(int i = 128 + 256; k >= 0; ) {
- // 找到一个计数大于0的字节值
- while (count[--i & 0xFF] == 0);
- int v = i & 0xFF; // 当前处理的字节值
- int c = count[i & 0xFF]; // 获取当前字节值的计数
- for(int j = 0; j < c; j++) {
- a[k] = (byte) v; // 将字节值填充到结果数组的后面
- k--;
- }
- }
- }
稳定计数排序
- /**
- * 稳定版计数排序
- * @param a
- */
- public static void sort2(int[] a) {
- int min = a[0];
- int max = a[0];
- for (int i : a) {
- if(i > max) {
- max = i;
- } else if(i < min) {
- min = i;
- }
- }
- int[] count = new int[max - min + 1];
- for (int i : a) {
- count[i - min]++;
- }
-
- // 累加计数数组 如果count[i] = 5,则表示有5个元素会放在排序数组中i的位置之前
- for (int i = 1; i < count.length; i++) {
- count[i] = count[i] + count[i - 1];
- }
-
- int[] b = new int[a.length];
- for(int i = a.length - 1; i >= 0; i--) {
- // 找到当前元素在计数数组中的索引
- int j = a[i] - min;
- // 减少计数,表示该元素已经放置过了
- count[j]--;
- // 将元素放入结果数组b的正确位置
- b[count[j]] = a[i];
- }
- System.arraycopy(b, 0, a, 0, a.length);
- }
初步实现
- package com.itheima.algorithms.sort;
-
- import com.itheima.datastructure.Array.DynamicArray;
-
- import java.util.Arrays;
-
- public class BucketSort {
- public static void main(String[] args) {
- int[] ages = {20, 18, 28, 66, 25, 31, 67, 30}; // 假设年龄范围1~99
- System.out.println(Arrays.toString(ages));
- sort(ages);
- System.out.println(Arrays.toString(ages));
- }
-
- /**
- * 桶排序
- * @param ages
- */
- private static void sort(int[] ages) {
- // 1. 准备桶
- DynamicArray[] buckets = new DynamicArray[10];
- for (int i = 0; i < buckets.length; i++) {
- buckets[i] = new DynamicArray();
- }
- // 2. 放入年龄数据
- for (int age : ages) {
- buckets[age / 10].addLast(age);
- }
- int k = 0;
- for (DynamicArray bucket : buckets) {
- // 3. 对桶内元素进行排序
- int[] array = bucket.stream().toArray();
- InsertionSort.sort(array);
- // 4. 把每个桶排序好的内容,依次放回原始数组
- for (int i : array) {
- ages[k++] = i;
- }
- }
- }
- }
通用
- package com.itheima.algorithms.sort;
-
- import com.itheima.datastructure.Array.DynamicArray;
-
- import java.util.Arrays;
-
- public class BucketSortGeneric {
-
- public static void main(String[] args) {
- int[] ages = {9, 0, 5, 1, 4, 7, 2, 8}; // 假设年龄范围1~99
- System.out.println(Arrays.toString(ages));
- sort(ages, 20);
- System.out.println(Arrays.toString(ages));
- }
-
- /**
- * 桶排序通用版 - 参考计数排序
- * @param ages
- * @param range
- */
- private static void sort(int[] ages, int range) {
- // 0. 找最大最小值
- int max = ages[0];
- int min = ages[0];
- for (int i = 1; i < ages.length; i++) {
- if (ages[i] > max) {
- max = ages[i];
- }
- if (ages[i] < min) {
- min = ages[i];
- }
- }
-
- // 1. 准备桶 c
- // (max - min) / range + 1 减少桶的个数
- DynamicArray[] buckets = new DynamicArray[(max - min) / range + 1];
- for (int i = 0; i < buckets.length; i++) {
- buckets[i] = new DynamicArray();
- }
- // 2. 放入年龄数据
- for (int age : ages) {
- buckets[(age - min) / range].addLast(age);
- }
- int k = 0;
- for (DynamicArray bucket : buckets) {
- // 3. 对桶内元素进行排序
- int[] array = bucket.stream().toArray();
- InsertionSort.sort(array);
- // 4. 把每个桶排序好的内容,依次放回原始数组
- for (int i : array) {
- ages[k++] = i;
- }
- }
- }
- }
基数排序(Radix Sort)的核心思路是按位排序,即将数字按位(从最低位到最高位或从最高位到最低位)逐位排序。基数排序通常适用于整数和字符串排序。以下是基数排序的基本步骤和思路:
1. 确定最大位数
2. 按位排序
例如
代码:
- package com.itheima.algorithms.sort;
-
- import java.util.ArrayList;
-
- public class RadixSort {
-
- /**
- * 基数排序
- *
- * @param a 待排序字符串数组
- */
- public static void radixSort(String[] a) {
- // 1. 找出最长字符串的长度
- int maxLength = 0;
- for (String s : a) {
- if (s.length() > maxLength) {
- maxLength = s.length();
- }
- }
-
- // 2. 准备桶
- ArrayList<String>[] buckets = new ArrayList[128];
- for (int i = 0; i < buckets.length; i++) {
- buckets[i] = new ArrayList<>();
- }
-
- // 3. 从字符串的低位到高位,进行多轮按位桶排序
- for (int i = maxLength - 1; i >= 0; i--) {
- // 根据某一个位的值放入到对应桶
- for (String s : a) {
- int num = Integer.parseInt(s);
- if(s.length() < maxLength) {
- // 处理字符串长度不等的情况
- s = String.format("%0" + maxLength + "d", num);
- }
- buckets[s.charAt(i) - '0'].add(String.valueOf(num));
- }
- int k = 0;
- for (ArrayList<String> bucket : buckets) {
- // 4. 重新取出排好序的字符串,依次放回原始数组
- for (String s : bucket) {
- a[k++] = s;
- }
- bucket.clear();
- }
- }
- }
-
- public static void main(String[] args) {
- /*String[] phoneNumbers = new String[10];
- phoneNumbers[0] = "13812345678";
- phoneNumbers[1] = "13912345678";
- phoneNumbers[2] = "13612345678";
- phoneNumbers[3] = "13712345678";
- phoneNumbers[4] = "13512345678";
- phoneNumbers[5] = "13412345678";
- phoneNumbers[6] = "15012345678";
- phoneNumbers[7] = "15112345678";
- phoneNumbers[8] = "15212345678";
- phoneNumbers[9] = "15712345678";*/
-
- String[] phoneNumbers = new String[10];
- phoneNumbers[0] = "138";
- phoneNumbers[1] = "139";
- phoneNumbers[2] = "1068";
- phoneNumbers[3] = "137";
- phoneNumbers[4] = "135";
- phoneNumbers[5] = "14";
- phoneNumbers[6] = "150";
- phoneNumbers[7] = "151";
- phoneNumbers[8] = "162";
- phoneNumbers[9] = "57";
- RadixSort.radixSort(phoneNumbers);
- for (String phoneNumber : phoneNumbers) {
- System.out.print(phoneNumber + " ");
- }
- }
- }
给你两个数组,arr1
和 arr2
,arr2
中的元素各不相同,arr2
中的每个元素都出现在 arr1
中。
对 arr1
中的元素进行排序,使 arr1
中项的相对顺序和 arr2
中的相对顺序相同。未在 arr2
中出现过的元素需要按照升序放在 arr1
的末尾。
示例 1:
输入:arr1 = [2,3,1,3,2,4,6,7,9,2,19], arr2 = [2,1,4,3,9,6] 输出:[2,2,2,1,4,3,3,9,6,7,19]
示例 2:
输入:arr1 = [28,6,22,8,44,17], arr2 = [22,28,8,6] 输出:[22,28,8,6,17,44]
提示:
1 <= arr1.length, arr2.length <= 1000
0 <= arr1[i], arr2[i] <= 1000
arr2
中的元素 arr2[i]
各不相同 arr2
中的每个元素 arr2[i]
都出现在 arr1
中解法一:计数排序
- class Solution {
- public int[] relativeSortArray(int[] arr1, int[] arr2) {
- // 1. 记录每个元素出现的次数
- int[] count = new int[1001];
- for (int i : arr1) {
- count[i]++;
- }
- int[] result = new int[arr1.length];
- int k = 0;
- // 2. 如果元素有在arr2出现,添加到result中
- for (int i : arr2) {
- while (count[i] > 0) {
- result[k++] = i;
- count[i]--;
- }
- }
- // 3. 处理不在arr2中的元素
- for (int i = 0; i < count.length; i++) {
- while (count[i] > 0) {
- result[k++] = i;
- count[i]--;
- }
- }
- return result;
- }
- }
给你一个整数数组 nums
,请你将数组按照每个值的频率 升序 排序。如果有多个值的频率相同,请你按照数值本身将它们 降序 排序。
请你返回排序后的数组。
示例 1:
输入:nums = [1,1,2,2,2,3] 输出:[3,1,1,2,2,2] 解释:'3' 频率为 1,'1' 频率为 2,'2' 频率为 3 。
示例 2:
输入:nums = [2,3,1,3,2] 输出:[1,3,3,2,2] 解释:'2' 和 '3' 频率都为 2 ,所以它们之间按照数值本身降序排序。
示例 3:
输入:nums = [-1,1,-6,4,5,-6,1,4,1] 输出:[5,-1,4,4,-6,-6,1,1,1]
提示:
1 <= nums.length <= 100
-100 <= nums[i] <= 100
解法一:计数排序。执行耗时8ms
- class Solution {
- public int[] frequencySort(int[] nums) {
- // 1. 记录每个元素出现的次数
- int[] count = new int[201];
- for (int num : nums) {
- // 加100是为了处理num为负数的情况
- count[num + 100]++;
- }
-
- return Arrays.stream(nums).boxed().sorted((a, b) -> {
- int fa = count[a + 100];
- int fb = count[b + 100];
- if(fa == fb) {
- // 如果频率相同,按照数值本身将它们降序排序
- return Integer.compare(b, a);
- } else {
- // 按照每个值的频率升序排序
- return fa - fb;
- }
- }).mapToInt(Integer::intValue).toArray();
- }
- }
解法二:计数排序 + HashMap。执行耗时6ms
- import java.util.*;
-
- class Solution {
- public int[] frequencySort(int[] nums) {
- // 1. 记录每个元素出现的次数
- Map<Integer, Integer> count = new HashMap<>();
-
- for (int num : nums) {
- count.put(num, count.getOrDefault(num, 0) + 1);
- }
-
- // 2. 使用 List 存储元素及其频率,方便排序
- List<Map.Entry<Integer, Integer>> entryList = new ArrayList<>(count.entrySet());
-
- // 3. 按照频率升序,然后数值降序排序
- entryList.sort((a, b) -> {
- if (a.getValue().equals(b.getValue())) {
- return Integer.compare(b.getKey(), a.getKey()); // 按数值降序
- }
- return Integer.compare(a.getValue(), b.getValue()); // 按频率升序
- });
-
- // 4. 构造结果数组
- int index = 0;
- for (Map.Entry<Integer, Integer> entry : entryList) {
- int frequency = entry.getValue();
- for (int i = 0; i < frequency; i++) {
- nums[index++] = entry.getKey();
- }
- }
-
- return nums;
- }
- }
给定一个无序的数组 nums
,返回 数组在排序之后,相邻元素之间最大的差值 。如果数组元素个数小于 2,则返回 0
。
您必须编写一个在「线性时间」内运行并使用「线性额外空间」的算法。(桶排序或基数排序)
示例 1:
输入: nums = [3,6,9,1] 输出: 3 解释: 排序后的数组是 [1,3,6,9], 其中相邻元素 (3,6) 和 (6,9) 之间都存在最大差值 3。
示例 2:
输入: nums = [10] 输出: 0 解释: 数组元素个数小于 2,因此返回 0。
提示:
1 <= nums.length <= 10^5
0 <= nums[i] <= 10^9
解法一:桶排序。超出内存限制
- class Solution {
- public int maximumGap(int[] nums) {
- int n = nums.length;
- if (n < 2) {
- return 0;
- }
-
- sort(nums, 1);
-
- int ret = 0;
- for (int i = 1; i < n; i++) {
- ret = Math.max(ret, nums[i] - nums[i - 1]);
- }
- return ret;
- }
-
- public static void sort(int[] nums, int range) {
- int max = nums[0];
- int min = nums[0];
- for (int i = 1; i < nums.length; i++) {
- if (nums[i] > max) {
- max = nums[i];
- }
- if (nums[i] < min) {
- min = nums[i];
- }
- }
-
- ArrayList[] buckets = new ArrayList[(max - min) / range + 1];
- for (int i = 0; i < buckets.length; i++) {
- buckets[i] = new ArrayList();
- }
- for (int num : nums) {
- buckets[(num - min) / range].addLast(num);
- }
-
- int k = 0;
- for (ArrayList<Integer> bucket : buckets) {
- // 创建一个新的int数组
- int[] array = new int[bucket.size()];
-
- // 将ArrayList的元素复制到int数组
- for (int i = 0; i < bucket.size(); i++) {
- array[i] = bucket.get(i);
- }
-
- // 执行插入排序
- insertionSort(array, 0, array.length - 1);
-
- // 将排序后的结果写回nums数组
- for (int a : array) {
- nums[k++] = a;
- }
- }
- }
-
- public static void insertionSort(int[] nums, int left, int right) {
- for (int low = left + 1; low <= right; low++) {
- int t = nums[low];
- int i = low - 1;
-
- while (i >= left && t < nums[i]) {
- nums[i + 1] = nums[i];
- i--;
- }
- if (i != low - 1) {
- nums[i + 1] = t;
- }
- }
- }
- }
解法二:桶排序 - 合理化桶的数量。执行耗时52ms
- class Solution {
- public int maximumGap(int[] nums) {
- int n = nums.length;
- if (n < 2) {
- return 0;
- }
-
- sort(nums);
-
- int ret = 0;
- for (int i = 1; i < n; i++) {
- ret = Math.max(ret, nums[i] - nums[i - 1]);
- }
- return ret;
- }
-
- public static void sort(int[] nums) {
- int max = nums[0];
- int min = nums[0];
- for (int i = 1; i < nums.length; i++) {
- if (nums[i] > max) {
- max = nums[i];
- }
- if (nums[i] < min) {
- min = nums[i];
- }
- }
-
- // ArrayList[] buckets = new ArrayList[(max - min) / range + 1];
- int range = Math.max((max - min) / (nums.length - 1), 1);
- ArrayList[] buckets = new ArrayList[(max - min) / range + 1];
- for (int i = 0; i < buckets.length; i++) {
- buckets[i] = new ArrayList();
- }
- for (int num : nums) {
- buckets[(num - min) / range].addLast(num);
- }
-
- int k = 0;
- for (ArrayList<Integer> bucket : buckets) {
- // 创建一个新的int数组
- int[] array = new int[bucket.size()];
-
- // 将ArrayList的元素复制到int数组
- for (int i = 0; i < bucket.size(); i++) {
- array[i] = bucket.get(i);
- }
-
- // 执行插入排序
- insertionSort(array, 0, array.length - 1);
-
- // 将排序后的结果写回nums数组
- for (int a : array) {
- nums[k++] = a;
- }
- }
- }
-
- public static void insertionSort(int[] nums, int left, int right) {
- for (int low = left + 1; low <= right; low++) {
- int t = nums[low];
- int i = low - 1;
-
- while (i >= left && t < nums[i]) {
- nums[i + 1] = nums[i];
- i--;
- }
- if (i != low - 1) {
- nums[i + 1] = t;
- }
- }
- }
- }
解法三:桶排序 - 只保留桶内最大最小值。执行耗时13ms
- class Solution {
-
- static class Pair {
- int max = 0;
- int min = 1000_000_000;
-
- public void add(int v) {
- max = Math.max(max, v);
- min = Math.min(min, v);
- }
-
- @Override
- public String toString() {
- return "[" + min + "," + max + "]";
- }
- }
-
- public int maximumGap(int[] nums) {
- // 1. 处理特殊情况
- if (nums.length < 2) {
- return 0;
- }
- // 2. 桶排序
- // 桶个数 (max - min) / range + 1 期望桶个数 nums.length + 1
- // range = (max - min) / nums.length
- int max = nums[0];
- int min = nums[0];
- for (int i = 1; i < nums.length; i++) {
- if (nums[i] > max) {
- max = nums[i];
- }
- if (nums[i] < min) {
- min = nums[i];
- }
- }
- if (max == min) {
- return 0;
- }
-
- int range = Math.max(1, (max - min) / nums.length);
- int size = (max - min) / range + 1;
- Pair[] buckets = new Pair[size];
-
- // 2. 放入数据
- for (int num : nums) {
- int idx = (num - min) / range;
- if (buckets[idx] == null) {
- buckets[idx] = new Pair();
- }
- buckets[idx].add(num);
- }
- // 3. 寻找最大差值
- int r = 0;
- int lastMax = buckets[0].max;
- for (int i = 1; i < buckets.length; i++) {
- Pair pair = buckets[i];
- if (pair != null) {
- r = Math.max(r, pair.min - lastMax);
- lastMax = pair.max;
- }
- }
- return r;
- }
- }
解法四:基数排序。执行耗时68ms
- public int maximumGap(int[] nums) {
- // 0. 数组长度小于2,直接返回0
- if(nums.length < 2) {
- return 0;
- }
- // 1. 计算数组的最大值
- int max = nums[0];
- for (int num : nums) {
- max = Math.max(num, max);
- }
-
- // 2. 准备10个桶
- ArrayList<Integer>[] buckets = new ArrayList[10];
- for (int i = 0; i < buckets.length; i++) {
- buckets[i] = new ArrayList<>();
- }
-
- // 3. 没超过最大值
- long exp = 1;
- while(max >= exp) {
- for (int num : nums) {
- buckets[(num / (int) exp % 10)].add(num);
- }
- int k = 0;
- for (ArrayList<Integer> bucket : buckets) {
- for (Integer i : bucket) {
- nums[k++] = i;
- }
- bucket.clear();
- }
- exp *= 10;
- }
-
- // 4. 求最大间距
- int r = 0;
- for (int i = 1; i < nums.length; i++) {
- r = Math.max(r, nums[i] - nums[i - 1]);
- }
- return r;
- }
给你一个整数数组 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 * 10^4
-5 * 10^4 <= nums[i] <= 5 * 10^4
解法一:单边快排(随机轴值)。执行耗时1918ms
- class Solution {
- public static int[] sortArray(int[] nums) {
- quickSort(nums, 0, nums.length - 1);
- return nums;
- }
-
- private static void quickSort(int[] nums, int left, int right) {
- if (left < right) {
- int pivot = partition(nums, left, right);
- quickSort(nums, left, pivot - 1);
- quickSort(nums, pivot + 1, right);
- }
- }
-
- private static int partition(int[] nums, int left, int right) {
- int idx = ThreadLocalRandom.current().nextInt(right - left + 1) + left;
- swap(nums, idx, right);
- int pivot = nums[right];
- int i = left;
- for (int j = left; j < right; j++) {
- if (nums[j] < pivot) {
- if (i != j) {
- swap(nums, i, j);
- }
- i++;
- }
- }
- swap(nums, i, right);
- return i;
- }
-
- public static void swap(int[] a, int i, int j) {
- int t = a[i];
- a[i] = a[j];
- a[j] = t;
- }
- }
解法二:双边快排(随机)。执行耗时1643ms
- class Solution {
- public static int[] sortArray(int[] nums) {
- quickSort(nums, 0, nums.length - 1);
- return nums;
- }
-
- private static void quickSort(int[] nums, int left, int right) {
- if (left < right) {
- int pivot = partition(nums, left, right);
- quickSort(nums, left, pivot - 1);
- quickSort(nums, pivot + 1, right);
- }
- }
-
- private static int partition(int[] nums, int left, int right) {
- int idx = ThreadLocalRandom.current().nextInt(right - left + 1) + left;
- swap(nums, idx, left);
- int pivot = nums[left];
- int i = left, j = right;
- while (i < j) {
- while (i < j && nums[j] > pivot) {
- j--;
- }
- while (i < j && nums[i] <= pivot) {
- i++;
- }
- swap(nums, i, j);
- }
- swap(nums, left, i);
- return i;
- }
-
- public static void swap(int[] a, int i, int j) {
- int t = a[i];
- a[i] = a[j];
- a[j] = t;
- }
- }
解法三:快排(随机 + 重复)。执行耗时26ms
- class Solution {
- public static int[] sortArray(int[] nums) {
- quickSort(nums, 0, nums.length - 1);
- return nums;
- }
-
- private static void quickSort(int[] nums, int left, int right) {
- if (left < right) {
- int pivot = partition(nums, left, right);
- quickSort(nums, left, pivot - 1);
- quickSort(nums, pivot + 1, right);
- }
- }
-
- private static int partition(int[] nums, int left, int right) {
- int idx = ThreadLocalRandom.current().nextInt(right - left + 1) + left;
- swap(nums, idx, left);
- int pivot = nums[left];
- int i = left + 1, j = right;
- while (i <= j) {
- while (i <= j && nums[i] < pivot) {
- i++;
- }
- while (i <= j && nums[j] > pivot) {
- j--;
- }
- if (i <= j) {
- swap(nums, i, j);
- i++;
- j--;
- }
- }
- swap(nums, j, left);
- return j;
- }
-
- public static void swap(int[] a, int i, int j) {
- int t = a[i];
- a[i] = a[j];
- a[j] = t;
- }
- }
解法四:快排(随机 + 重复 + 插入)。执行耗时18ms
- class Solution {
- public static int[] sortArray(int[] nums) {
- quickSort(nums, 0, nums.length - 1);
- return nums;
- }
-
- public static void insertionSort(int[] nums, int left, int right) {
- for (int low = left + 1; low <= right; low++) {
- int t = nums[low];
- int i = low - 1;
-
- while (i >= left && t < nums[i]) {
- nums[i + 1] = nums[i];
- i--;
- }
- if (i != low - 1) {
- nums[i + 1] = t;
- }
- }
- }
-
- private static void quickSort(int[] nums, int left, int right) {
- if (right - left <= 32) {
- insertionSort(nums, left, right);
- return;
- }
- int pivot = partition(nums, left, right);
- quickSort(nums, left, pivot - 1);
- quickSort(nums, pivot + 1, right);
- }
-
- private static int partition(int[] nums, int left, int right) {
- int idx = ThreadLocalRandom.current().nextInt(right - left + 1) + left;
- swap(nums, idx, left);
- int pivot = nums[left];
- int i = left + 1, j = right;
- while (i <= j) {
- while (i <= j && nums[i] < pivot) {
- i++;
- }
- while (i <= j && nums[j] > pivot) {
- j--;
- }
- if (i <= j) {
- swap(nums, i, j);
- i++;
- j--;
- }
- }
- swap(nums, j, left);
- return j;
- }
-
- public static void swap(int[] a, int i, int j) {
- int t = a[i];
- a[i] = a[j];
- a[j] = t;
- }
- }
解法五:计数排序。执行耗时3ms
- class Solution {
- public static int[] sortArray(int[] nums) {
- int max = nums[0];
- int min = nums[0];
- for (int i = 1; i < nums.length; i++) {
- if (nums[i] > max) {
- max = nums[i];
- }
- if (nums[i] < min) {
- min = nums[i];
- }
- }
- int[] count = new int[max - min + 1];
-
- for (int num : nums) {
- count[num - min]++;
- }
-
- int k = 0;
- for (int i = 0; i < count.length; i++) {
- while (count[i] > 0) {
- nums[k++] = i + min;
- count[i]--;
- }
- }
- return nums;
- }
- }
给你链表的头结点 head
,请将其按 升序 排列并返回 排序后的链表 。
示例 1:
输入:head = [4,2,1,3] 输出:[1,2,3,4]
示例 2:
输入:head = [-1,5,3,4,0] 输出:[-1,0,3,4,5]
示例 3:
输入:head = [] 输出:[]
提示:
[0, 5 * 10^4]
内-10^5 <= Node.val <= 10^5
进阶:你可以在 O(n log n)
时间复杂度和常数级空间复杂度下,对链表进行排序吗?
解法一:把链表中的值放入数组当中进行排序,再根据排序后的值创建新的链表返回
- /**
- * Definition for singly-linked list.
- * public class ListNode {
- * int val;
- * ListNode next;
- * ListNode() {}
- * ListNode(int val) { this.val = val; }
- * ListNode(int val, ListNode next) { this.val = val; this.next = next; }
- * }
- */
- class Solution {
- public ListNode sortList(ListNode head) {
- if (head == null || head.next == null) {
- return head;
- }
-
- List<Integer> values = new ArrayList<>();
- ListNode current = head;
-
- while (current != null) {
- values.add(current.val);
- current = current.next;
- }
- Collections.sort(values);
-
- ListNode newHead = new ListNode(values.get(0));
- ListNode temp = newHead;
-
- for (int i = 1; i < values.size(); i++) {
- temp.next = new ListNode(values.get(i));
- temp = temp.next;
- }
-
- return newHead;
- }
- }
解法二:快慢指针 + 合并有序链表(归并)
- /**
- * Definition for singly-linked list.
- * public class ListNode {
- * int val;
- * ListNode next;
- * ListNode() {}
- * ListNode(int val) { this.val = val; }
- * ListNode(int val, ListNode next) { this.val = val; this.next = next; }
- * }
- */
- class Solution {
- public ListNode sortList(ListNode head) {
- if (head == null || head.next == null) {
- return head;
- }
-
- ListNode mid = findMiddle(head);
- ListNode secondHalf = mid.next;
- mid.next = null;
-
- ListNode sortedFirstHalf = sortList(head);
- ListNode sortedSecondHalf = sortList(secondHalf);
-
- return merge(sortedFirstHalf, sortedSecondHalf);
-
- }
-
- private ListNode merge(ListNode l1, ListNode l2) {
- ListNode dummy = new ListNode(0);
- ListNode current = dummy;
-
- while (l1 != null && l2 != null) {
- if (l1.val < l2.val) {
- current.next = l1;
- l1 = l1.next;
- } else {
- current.next = l2;
- l2 = l2.next;
- }
- current = current.next;
- }
- current.next = l1 != null ? l1 : l2;
-
- return dummy.next;
- }
-
- private ListNode findMiddle(ListNode head) {
- ListNode slow = head;
- ListNode fast = head.next;
- while (fast != null && fast.next != null) {
- slow = slow.next;
- fast = fast.next.next;
- }
- return slow;
- }
- }
给你两个数组,arr1
和 arr2
,arr2
中的元素各不相同,arr2
中的每个元素都出现在 arr1
中。
对 arr1
中的元素进行排序,使 arr1
中项的相对顺序和 arr2
中的相对顺序相同。未在 arr2
中出现过的元素需要按照升序放在 arr1
的末尾。
示例 1:
输入:arr1 = [2,3,1,3,2,4,6,7,9,2,19], arr2 = [2,1,4,3,9,6] 输出:[2,2,2,1,4,3,3,9,6,7,19]
示例 2:
输入:arr1 = [28,6,22,8,44,17], arr2 = [22,28,8,6] 输出:[22,28,8,6,17,44]
提示:
1 <= arr1.length, arr2.length <= 1000
0 <= arr1[i], arr2[i] <= 1000
arr2
中的元素 arr2[i]
各不相同 arr2
中的每个元素 arr2[i]
都出现在 arr1
中解法一:
- class Solution {
- public int[] relativeSortArray(int[] arr1, int[] arr2) {
- // 1. 记录每个元素出现的次数
- int[] count = new int[1001];
- for (int i : arr1) {
- count[i]++;
- }
- int[] result = new int[arr1.length];
- int k = 0;
- // 2. 如果元素有在arr2出现,添加到result中
- for (int i : arr2) {
- while (count[i] > 0) {
- result[k++] = i;
- count[i]--;
- }
- }
- // 3. 处理不在arr2中的元素
- for (int i = 0; i < count.length; i++) {
- while (count[i] > 0) {
- result[k++] = i;
- count[i]--;
- }
- }
- return result;
- }
- }
给你一个整数数组 nums
,请你将数组按照每个值的频率 升序 排序。如果有多个值的频率相同,请你按照数值本身将它们 降序 排序。
请你返回排序后的数组。
示例 1:
输入:nums = [1,1,2,2,2,3] 输出:[3,1,1,2,2,2] 解释:'3' 频率为 1,'1' 频率为 2,'2' 频率为 3 。
示例 2:
输入:nums = [2,3,1,3,2] 输出:[1,3,3,2,2] 解释:'2' 和 '3' 频率都为 2 ,所以它们之间按照数值本身降序排序。
示例 3:
输入:nums = [-1,1,-6,4,5,-6,1,4,1] 输出:[5,-1,4,4,-6,-6,1,1,1]
提示:
1 <= nums.length <= 100
-100 <= nums[i] <= 100
解法一:计数排序
- class Solution {
- public int[] frequencySort(int[] nums) {
- // 1. 记录每个元素出现的次数
- int[] count = new int[201];
- for (int num : nums) {
- // 加100是为了处理num为负数的情况
- count[num + 100]++;
- }
-
- return Arrays.stream(nums).boxed().sorted((a, b) -> {
- int fa = count[a + 100];
- int fb = count[b + 100];
- if(fa == fb) {
- // 如果频率相同,按照数值本身将它们降序排序
- return Integer.compare(b, a);
- } else {
- // 按照每个值的频率升序排序
- return fa - fb;
- }
- }).mapToInt(Integer::intValue).toArray();
- }
- }
优化:
- import java.util.*;
-
- class Solution {
- public int[] frequencySort(int[] nums) {
- // 1. 记录每个元素出现的次数
- Map<Integer, Integer> count = new HashMap<>();
-
- for (int num : nums) {
- count.put(num, count.getOrDefault(num, 0) + 1);
- }
-
- // 2. 使用 List 存储元素及其频率,方便排序
- List<Map.Entry<Integer, Integer>> entryList = new ArrayList<>(count.entrySet());
-
- // 3. 按照频率升序,然后数值降序排序
- entryList.sort((a, b) -> {
- if (a.getValue().equals(b.getValue())) {
- return Integer.compare(b.getKey(), a.getKey()); // 按数值降序
- }
- return Integer.compare(a.getValue(), b.getValue()); // 按频率升序
- });
-
- // 4. 构造结果数组
- int index = 0;
- for (Map.Entry<Integer, Integer> entry : entryList) {
- int frequency = entry.getValue();
- for (int i = 0; i < frequency; i++) {
- nums[index++] = entry.getKey();
- }
- }
-
- return nums;
- }
- }
给你一个整数数组 nums
,按要求返回一个新数组 counts
。数组 counts
有该性质: counts[i]
的值是 nums[i]
右侧小于 nums[i]
的元素的数量。
示例 1:
输入:nums = [5,2,6,1] 输出:[2,1,1,0] 解释: 5 的右侧有 2 个更小的元素 (2 和 1) 2 的右侧仅有 1 个更小的元素 (1) 6 的右侧有 1 个更小的元素 (1) 1 的右侧有 0 个更小的元素
示例 2:
输入:nums = [-1] 输出:[0]
示例 3:
输入:nums = [-1,-1] 输出:[0,0]
提示:
1 <= nums.length <= 10^5
-10^4 <= nums[i] <= 10^4
解法一:暴力解。超出时间限制
- class Solution {
- public List<Integer> countSmaller(int[] nums) {
- List<Integer> counts = new ArrayList<>();
- int n = nums.length;
- for (int i = 0; i < n; i++) {
- int count = 0;
- for (int j = n - 1; j >= i; j--) {
- if (nums[j] < nums[i]) {
- count++;
- }
- }
- counts.add(count);
- }
- return counts;
- }
- }
解法二:
- class Solution {
- public List<Integer> countSmaller(int[] nums) {
- int n = nums.length;
- List<Integer> res = new ArrayList<>(n);
-
- int[] index = new int[n];
- int[] helper = new int[n];
- int[] count = new int[n];
-
- for (int i = 0; i < n; i++) {
- index[i] = i;
- }
-
- merge(nums, index, helper, count, 0, nums.length - 1);
-
- for (int i = 0; i < n; i++) {
- res.add(i, count[i]);
- }
- return res;
- }
-
- private void merge(int[] nums, int[] index, int[] helper, int[] count, int left, int right) {
- if (left == right || left > right)
- return;
- int mid = (left + right) >> 1;
-
- if (left < mid) {
- merge(nums, index, helper, count, left, mid);
- }
-
- if (mid + 1 < right) {
- merge(nums, index, helper, count, mid + 1, right);
- }
-
- int i = left, j = mid + 1;
- int hi = left;
- while (i <= mid && j <= right) {
- if (nums[index[i]] <= nums[index[j]]) {
- helper[hi++] = index[j++];
- } else {
- count[index[i]] += right - j + 1;
- helper[hi++] = index[i++];
- }
- }
- while (i <= mid) {
- helper[hi++] = index[i++];
- }
-
- while (j <= right) {
- helper[hi++] = index[j++];
- }
-
- for (int k = left; k <= right; k++) {
- index[k] = helper[k];
- }
- }
- }
给你一个整数数组 nums
和一个整数 k
,请你返回其中出现频率前 k
高的元素。你可以按 任意顺序 返回答案。
示例 1:
输入: nums = [1,1,1,2,2,3], k = 2 输出: [1,2]
示例 2:
输入: nums = [1], k = 1 输出: [1]
提示:
1 <= nums.length <= 10^5
k
的取值范围是 [1, 数组中不相同的元素的个数]
k
个高频元素的集合是唯一的进阶:你所设计算法的时间复杂度 必须 优于 O(n log n)
,其中 n
是数组大小。
解法一:HashMap + 小根堆
(图片来自. - 力扣(LeetCode))
- public class Solution {
- public int[] topKFrequent(int[] nums, int k) {
- HashMap<Integer, Integer> count = new HashMap<>();
-
- // 1. 使用HashMap统计每个元素出现的次数
- for (int num : nums) {
- count.put(num, count.getOrDefault(num, 0) + 1);
- }
-
- // 2. 使用一个小根堆来维护前k个高频元素
- PriorityQueue<Map.Entry<Integer, Integer>> minHeap = new PriorityQueue<>(k,
- (a, b) -> a.getValue() - b.getValue());
- for (Map.Entry<Integer, Integer> entry : count.entrySet()) {
- minHeap.offer(entry);
- // 如果堆的大小超过k,移除频率最低的元素,最后剩下的k个元素即为所求
- if (minHeap.size() > k) {
- minHeap.poll();
- }
- }
-
- // 3. 获取结果
- int i = 0;
- int[] result = new int[k];
- while (!minHeap.isEmpty()) {
- result[i++] = minHeap.poll().getKey();
- }
- return result;
- }
- }
解法二:HashMap + 桶排序
- class Solution {
- public int[] topKFrequent(int[] nums, int k) {
- HashMap<Integer, Integer> count = new HashMap<>();
- List<Integer> res = new ArrayList<>();
-
- // 1. 使用HashMap统计每个元素出现的次数
- for (int num : nums) {
- count.put(num, count.getOrDefault(num, 0) + 1);
- }
-
- // 2. 将频率作为数组下标,对于出现频率不同的数字集合,存入对应的数组下标
- List<Integer>[] list = new List[nums.length + 1];
- for (Integer key : count.keySet()) {
- int i = count.get(key);
- if (list[i] == null) {
- list[i] = new ArrayList<>();
- }
- list[i].add(key);
- }
-
- // 3. 倒序遍历数组获取出现频率从大到小的排列
- for (int i = list.length - 1; i >= 0 && res.size() < k; i--) {
- if (list[i] == null) {
- continue;
- }
- res.addAll(list[i]);
- }
- return res.stream().mapToInt(Integer::intValue).toArray();
- }
- }
给定一个包含红色、白色和蓝色、共 n
个元素的数组 nums
,原地 对它们进行排序,使得相同颜色的元素相邻,并按照红色、白色、蓝色顺序排列。
我们使用整数 0
、 1
和 2
分别表示红色、白色和蓝色。
必须在不使用库内置的 sort 函数的情况下解决这个问题。
示例 1:
输入:nums = [2,0,2,1,1,0] 输出:[0,0,1,1,2,2]
示例 2:
输入:nums = [2,0,1] 输出:[0,1,2]
提示:
n == nums.length
1 <= n <= 300
nums[i]
为 0
、1
或 2
进阶:
解法一:维护三个指针,分别指向当前分析的元素、红色的末尾边界和蓝色的起始边界。
- class Solution {
- public void sortColors(int[] nums) {
- int zeroIndex = 0;
- int twoIndex = nums.length - 1;
- int current = 0;
-
- while (current <= twoIndex) {
- if (nums[current] == 0) {
- // 红色
- swap(nums, current, zeroIndex);
- zeroIndex++;
- current++;
- } else if (nums[current] == 2) {
- // 蓝色
- swap(nums, current, twoIndex);
- twoIndex--;
- } else {
- // 白色
- current++;
- }
- }
- }
-
- private void swap(int[] nums, int i, int j) {
- int temp = nums[i];
- nums[i] = nums[j];
- nums[j] = temp;
- }
- }
给定整数数组 nums
和整数 k
,请返回数组中第 k
个最大的元素。
请注意,你需要找的是数组排序后的第 k
个最大的元素,而不是第 k
个不同的元素。
你必须设计并实现时间复杂度为 O(n)
的算法解决此问题。
示例 1:
输入: [3,2,1,5,6,4], k = 2 输出: 5
示例 2:
输入: [3,2,3,1,2,4,5,5,6], k = 4 输出: 4
提示:
1 <= k <= nums.length <= 10^5
-10^4 <= nums[i] <= 10^4
解法一:自定义堆小根堆。执行耗时21ms
- class Solution {
- static class MinHeap {
- int[] array;
- int size;
-
- public MinHeap(int capacity) {
- array = new int[capacity];
- }
-
- public int peek() {
- return array[0];
- }
-
- public boolean offer(int offered) {
- if (size == array.length) {
- return false;
- }
- up(offered);
- size++;
- return true;
- }
-
- public void replace(int replaced) {
- array[0] = replaced;
- down(0);
- }
-
- private void up(int offered) {
- int child = size;
- while (child > 0) {
- int parent = (child - 1) >> 1;
- if (offered < array[parent]) {
- array[child] = array[parent];
- } else {
- break;
- }
- child = parent;
- }
- array[child] = offered;
- }
-
- private void down(int parent) {
- int left = (parent << 1) + 1;
- int right = left + 1;
- int min = parent;
- if (left < size && array[left] < array[min]) {
- min = left;
- }
- if (right < size && array[right] < array[min]) {
- min = right;
- }
- if (min != parent) {
- swap(min, parent);
- down(min);
- }
- }
-
- // 交换两个索引处的元素
- private void swap(int i, int j) {
- int t = array[i];
- array[i] = array[j];
- array[j] = t;
- }
- }
-
- public int findKthLargest(int[] nums, int k) {
- MinHeap heap = new MinHeap(k);
- // 先将k个元素入堆
- for (int i = 0; i < k; i++) {
- heap.offer(nums[i]);
- }
- // 将剩余的元素与堆顶元素比较,如果比堆顶元素大则替换。比较完成后,堆顶元素即为第k大的元素(小根堆)
- for (int i = k; i < nums.length; i++) {
- if (nums[i] > heap.peek()) {
- heap.replace(nums[i]);
- }
- }
-
- return heap.peek();
- }
- }
解法二:堆排序(优先队列)。执行耗时63ms
- class Solution {
- public int findKthLargest(int[] nums, int k) {
- PriorityQueue<Integer> minHeap = new PriorityQueue<>();
- for (int num : nums) {
- minHeap.offer(num);
- if(minHeap.size() > k) {
- minHeap.poll();
- }
- }
- return minHeap.peek();
- }
- }
解法三:快排(随机 + 处理重复 + 插入)。执行耗时28ms
- class Solution {
-
- public int findKthLargest(int[] nums, int k) {
- quickSort(nums, 0, nums.length - 1);
- return nums[nums.length - k];
- }
-
- private static void quickSort(int[] nums, int left, int right) {
- if (right - left <= 32) {
- insertionSort(nums, left, right);
- return;
- }
- int pivot = partition(nums, left, right);
- ;
- quickSort(nums, left, pivot - 1);
- quickSort(nums, pivot + 1, right);
- }
-
- private static int partition(int[] nums, int left, int right) {
- int idx = ThreadLocalRandom.current().nextInt(right - left + 1) + left;
- swap(nums, idx, left);
- int i = left + 1, j = right;
- int pivot = nums[left];
-
- while (i <= j) {
- while (i <= j && nums[i] < pivot) {
- i++;
- }
- while (i <= j && nums[j] > pivot) {
- j--;
- }
- if (i <= j) {
- swap(nums, i, j);
- i++;
- j--;
- }
- }
- swap(nums, j, left);
- return j;
- }
-
- private static void insertionSort(int[] nums, int left, int right) {
- for (int low = left + 1; low <= right; low++) {
- int t = nums[low];
- int i = low - 1;
- while (i >= left && t < nums[i]) {
- nums[i + 1] = nums[i];
- i--;
- }
- if (i != low - 1) {
- nums[i + 1] = t;
- }
- }
- }
-
- private static void swap(int[] nums, int i, int j) {
- int temp = nums[i];
- nums[i] = nums[j];
- nums[j] = temp;
- }
- }
给定一个数组 nums
,如果 i < j
且 nums[i] > 2*nums[j]
我们就将 (i, j)
称作一个重要翻转对。
你需要返回给定数组中的重要翻转对的数量。
示例 1:
输入: [1,3,2,3,1] 输出: 2
示例 2:
输入: [2,4,3,5,1] 输出: 3
注意:
50000
。解法一:归并排序
- class Solution {
- public int reversePairs(int[] nums) {
- if (nums == null || nums.length < 2) {
- return 0;
- }
- return mergeSort(nums, 0, nums.length - 1);
- }
-
- private int mergeSort(int[] nums, int left, int right) {
- if (left >= right) {
- return 0;
- }
- int mid = left + (right - left) / 2;
-
- // 统计左半部分和右半部分的翻转对
- int count = mergeSort(nums, left, mid) + mergeSort(nums, mid + 1, right);
- // 统计跨越两部分的翻转对数量
- count += countImportantPairs(nums, left, mid, right);
- // 合并两个部分
- merge(nums, left, mid, right);
-
- return count;
- }
-
- private void merge(int[] nums, int left, int mid, int right) {
- int[] temp = new int[right - left + 1];
- int i = left, j = mid + 1, k = 0;
-
- while (i <= mid && j <= right) {
- if (nums[i] <= nums[j]) {
- temp[k++] = nums[i++];
- } else {
- temp[k++] = nums[j++];
- }
- }
-
- while (i <= mid) {
- temp[k++] = nums[i++];
- }
-
- while (j <= right) {
- temp[k++] = nums[j++];
- }
- System.arraycopy(temp, 0, nums, left, temp.length);
- }
-
- private int countImportantPairs(int[] nums, int left, int mid, int right) {
- int count = 0;
- int j = mid + 1;
-
- // 使用双指针法,j 代表右边数组的指针
- for (int i = left; i <= mid; i++) {
- // 转换为long处理超出Integer.MAX_VALUE -> 2147483647的情况
- while (j <= right && (long) nums[i] > 2 * (long) nums[j]) {
- j++;
- }
- // 当前 j 的位置表示 (mid + 1) 到 j - 1 都和 nums[i] 满足条件
- count += (j - (mid + 1));
- }
- return count;
- }
- }
给你一个字符串 s
和一个字符串数组 dictionary
,找出并返回 dictionary
中最长的字符串,该字符串可以通过删除 s
中的某些字符得到。
如果答案不止一个,返回长度最长且字母序最小的字符串。如果答案不存在,则返回空字符串。
示例 1:
输入:s = "abpcplea", dictionary = ["ale","apple","monkey","plea"] 输出:"apple"
示例 2:
输入:s = "abpcplea", dictionary = ["a","b","c"] 输出:"a"
提示:
1 <= s.length <= 1000
1 <= dictionary.length <= 1000
1 <= dictionary[i].length <= 1000
s
和 dictionary[i]
仅由小写英文字母组成解法一:
1. 检查子序列:通过遍历字典中的每个字符串,检查它是否是s中的一个子序列
2. 比较长度和字典序:对于所有符合条件的字符串,选择最长的一个。如果有多个长度相同的字符串,则选择字典序较小的那个
- public String findLongestWord(String s, List<String> dictionary) {
- String result = "";
-
- for (String word : dictionary) {
- if(isSubsequence(word, s)) {
- // 比较长度和字典序
- if(word.length() > result.length() || (word.length() == result.length() && word.compareTo(result) < 0)) {
- result = word;
- }
- }
- }
-
- return result;
- }
-
- /**
- * 检查字典中的每个字符串是否为s的一个子序列
- * @param word
- * @param s
- * @return
- */
- private boolean isSubsequence(String word, String s) {
- int wIndex = 0, sIndex = 0;
- while(wIndex < word.length() && sIndex < s.length()) {
- if(word.charAt(wIndex) == s.charAt(sIndex)) {
- wIndex++;
- }
- sIndex++;
- }
-
- return wIndex == word.length();
- }
给你一个按 非递减顺序 排序的整数数组 nums
,返回 每个数字的平方 组成的新数组,要求也按 非递减顺序 排序。
示例 1:
输入:nums = [-4,-1,0,3,10] 输出:[0,1,9,16,100] 解释:平方后,数组变为 [16,1,0,9,100] 排序后,数组变为 [0,1,9,16,100]
示例 2:
输入:nums = [-7,-3,2,3,11] 输出:[4,9,9,49,121]
提示:
1 <= nums.length <= 10^4
-10^4 <= nums[i] <= 10^4
nums
已按 非递减顺序 排序进阶:
O(n)
的算法解决本问题解法一:双指针法。大的元素添加到结果数组末尾
- class Solution {
- public int[] sortedSquares(int[] nums) {
- int n = nums.length;
- int[] result = new int[n];
- // 双指针法
- int left = 0, right = n - 1;
- int position = n - 1;
-
- while (left <= right) {
- int leftSquare = nums[left] * nums[left];
- int rightSquare = nums[right] * nums[right];
-
- // 大的添加到结果数组
- if (leftSquare > rightSquare) {
- result[position--] = leftSquare;
- left++;
- } else {
- result[position--] = rightSquare;
- right--;
- }
- }
- return result;
- }
- }
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。