冒泡排序
每轮冒泡不断地比较相邻的两个元素,如果它们是逆序的,则交换它们的位置
下一轮冒泡,可以调整未排序的右边界,减少不必要比较每一轮选择,找出最大(最小)的元素,并把它交换到合适的位置
java
private static void bubble(int[] a) {
// 初始化 j 为数组的最后一个索引
int j = a.length - 1;
// 外层循环:持续遍历直到没有需要交换的元素为止
while (true) {
// 初始化 x 为 0,用于记录最后一次交换的位置
int x = 0;
// 内层循环:从头到 j 索引位置进行比较和交换
for (int i = 0; i < j; i++) {
// 如果当前元素大于下一个元素,则交换它们
if (a[i] > a[i + 1]) {
// 交换 a[i] 和 a[i + 1]
int t = a[i];
a[i] = a[i + 1];
a[i + 1] = t;
// 更新 x 为最后一次交换的位置
x = i;
}
}
// 更新 j 为最后一次交换的位置
j = x;
// 如果 j 已经变为 0,说明没有更多的元素需要排序,退出循环
if (j == 0) {
break;
}
}
}
选择排序
每一轮选择,找出最大(最小)的元素,并把它交换到合适的位置
java
public static void sort(int[] a) {
// 1. 选择轮数为数组长度减1 (a.length - 1)
// 2. 每一轮将当前未排序部分的最大值放到正确的位置上
// 初始时 right 指向数组的最后一个索引,每一轮递减
// 外层循环:控制需要排序的轮数
for (int right = a.length - 1; right > 0; right--) {
// 初始化 max 为当前未排序部分的最后一个索引
int max = right;
// 内层循环:遍历当前未排序部分,找到最大值的索引
for (int i = 0; i < right; i++) {
// 如果当前元素大于已知的最大值,则更新 max 的值
if (a[i] > a[max]) {
max = i;
}
}
// 如果最大值的索引不是当前未排序部分的最后一个索引,则交换它们
if (max != right) {
swap(a, max, right);
}
}
}
// 辅助方法:交换数组中两个指定索引处的元素
private static void swap(int[] a, int i, int j) {
// 使用临时变量 t 来交换 a[i] 和 a[j]
int t = a[i];
a[i] = a[j];
a[j] = t;
}
堆排序
建立大顶堆
每次将堆顶元素(最大值)交换到末尾,调整堆顶元素,让它重新符合大顶堆特性
java
public class HeapSort {
/**
* 对数组进行堆排序
*
* @param a 待排序的数组
*/
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);
}
}
/**
* 建堆操作:将数组转换为最大堆
*
* @param array 数组
* @param size 数组的有效大小
*/
private static void heapify(int[] array, int size) {
// 从最后一个非叶子节点开始,依次向上调整每个节点
for (int i = size / 2 - 1; i >= 0; i--) {
// 对每个非叶子节点进行下沉操作
down(array, i, size);
}
}
/**
* 下沉操作:确保某个节点及其子树满足最大堆的性质
*
* @param array 数组
* @param parent 当前需要调整的父节点索引
* @param size 数组的有效大小
*/
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;
}
}
/**
* 交换数组中两个指定索引处的元素
*
* @param a 数组
* @param i 索引 i
* @param j 索引 j
*/
private static void swap(int[] a, int i, int j) {
// 使用临时变量 t 来交换 a[i] 和 a[j]
int t = a[i];
a[i] = a[j];
a[j] = t;
}
}
插入排序
将数组分为两部分 [0 .. low-1] [low .. a.length-1],左边 [0 .. low-1] 是已排序部分 ,右边 [low .. a.length-1] 是未排序部分 ,每次从未排序区域取出 low 位置的元素 , 插入到已排序区域
java
public static void sort(int[] a) {
// 遍历数组中的每个元素,从第二个元素开始(索引为1)
for (int low = 1; low < a.length; low++) {
// 将 low 位置的元素存储在临时变量 t 中
int t = a[low];
// 初始化 i 为已排序区域的最后一个索引(即 low - 1)
int i = low - 1; // 已排序区域指针
// 在已排序区域中从后向前扫描,寻找插入位置
while (i >= 0 && t < a[i]) { // 没有找到插入位置
// 将大于 t 的元素向右移动一位,空出插入位置
a[i + 1] = a[i];
i--;
}
// 找到插入位置
if (i != low - 1) { // 如果 i 没有变化,说明 t 应该放在原位
a[i + 1] = t;
}
}
}
希尔排序
简单的说,就是分组实现插入,每组元素间隙称为 gap,每轮排序后 gap 逐渐变小,直至 gap 为 1 完成排序,对插入排序的优化,让元素更快速地交换到最终位置
java
public static void sort(int[] a) {
// 初始间隔 gap 设为数组长度的一半 (a.length >> 1 等价于 a.length / 2)
for (int gap = a.length >> 1; gap > 0; gap = gap >> 1) {
// 对每个间隔 gap 进行插入排序
for (int low = gap; low < a.length; low++) {
// 将 low 位置的元素存储在临时变量 t 中
int t = a[low];
// 初始化 i 为已排序区域的指针(相对于 low 向前 gap 个位置)
int i = low - gap;
// 在已排序区域中从后向前扫描,寻找插入位置
while (i >= 0 && t < a[i]) { // 没有找到插入位置
// 将大于 t 的元素向右移动 gap 位,空出插入位置
a[i + gap] = a[i];
i -= gap; // 更新 i 为前一个间隔位置
}
// 找到插入位置
if (i != low - gap) {
// 插入 t 到正确的位置
a[i + gap] = t;
}
}
}
}
归并排序
每次从中间切一刀,处理的数据少一半,当数据仅剩一个时可以认为有序,当数据仅剩一个时可以认为有序
java
import java.util.Arrays;
public class MergeSort {
/**
* 合并两个已排序的子数组
*
* @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; // 初始化目标数组的索引 k 为起始位置 i
// 当两个子数组都没有处理完时,比较并合并它们
while (i <= iEnd && j <= jEnd) {
if (a1[i] < a1[j]) {
a2[k] = a1[i]; // 将较小的元素放入目标数组 a2
i++; // 移动指针 i
} else {
a2[k] = a1[j]; // 将较小的元素放入目标数组 a2
j++; // 移动指针 j
}
k++; // 移动目标数组的索引 k
}
// 如果左子数组还有剩余元素,复制到目标数组 a2
if (i > iEnd) {
System.arraycopy(a1, j, a2, k, jEnd - j + 1);
}
// 如果右子数组还有剩余元素,复制到目标数组 a2
if (j > jEnd) {
System.arraycopy(a1, i, a2, k, iEnd - i + 1);
}
}
/**
* 归并排序的入口函数
*
* @param a1 待排序的数组
*/
public static void sort(int[] a1) {
int[] a2 = new int[a1.length]; // 初始化辅助数组 a2
split(a1, 0, a1.length - 1, a2); // 调用 split 方法进行递归排序
}
/**
* 递归地分割数组并进行排序
*
* @param a1 原始数组
* @param left 子数组的起始索引
* @param right 子数组的结束索引
* @param a2 辅助数组
*/
private static void split(int[] a1, int left, int right, int[] a2) {
// 基本情况:如果子数组只有一个元素,则不需要进一步排序
if (left == right) {
return;
}
// 分割点 m,将数组分成两部分
int m = (left + right) >>> 1;
// 递归调用 split 方法对左半部分进行排序
split(a1, left, m, a2);
// 递归调用 split 方法对右半部分进行排序
split(a1, m + 1, right, a2);
// 合并两个已排序的子数组
merge(a1, left, m, m + 1, right, a2);
// 将合并后的结果从辅助数组 a2 复制回原数组 a1
System.arraycopy(a2, left, a1, left, right - left + 1);
}
public static void main(String[] args) {
int[] array = {5, 2, 4, 6, 1, 3};
System.out.println("Before sorting: " + Arrays.toString(array));
sort(array);
System.out.println("After sorting: " + Arrays.toString(array));
}
}
非递归实现:
java
public class BottomUpMergeSort {
/**
* 合并两个已排序的子数组
*
* @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; // 初始化目标数组的索引 k 为起始位置 i
// 当两个子数组都没有处理完时,比较并合并它们
while (i <= iEnd && j <= jEnd) {
if (a1[i] < a1[j]) {
a2[k] = a1[i]; // 将较小的元素放入目标数组 a2
i++; // 移动指针 i
} else {
a2[k] = a1[j]; // 将较小的元素放入目标数组 a2
j++; // 移动指针 j
}
k++; // 移动目标数组的索引 k
}
// 如果左子数组还有剩余元素,复制到目标数组 a2
if (i > iEnd) {
System.arraycopy(a1, j, a2, k, jEnd - j + 1);
}
// 如果右子数组还有剩余元素,复制到目标数组 a2
if (j > jEnd) {
System.arraycopy(a1, i, a2, k, iEnd - i + 1);
}
}
/**
* 自底向上的归并排序入口函数
*
* @param a1 待排序的数组
*/
public static void sort(int[] a1) {
int n = a1.length;
int[] a2 = new int[n]; // 初始化辅助数组 a2
// 从宽度为1开始,逐步增加宽度,直到覆盖整个数组
for (int width = 1; width < n; width *= 2) {
// 对每个宽度为 width 的子数组进行合并
for (int i = 0; i < n; i += 2 * width) {
// 计算左子数组的结束索引 m 和右子数组的结束索引 j
int m = Integer.min(i + width - 1, n - 1);
int j = Integer.min(i + 2 * width - 1, n - 1);
// 打印当前合并的子数组范围(调试信息)
System.out.println("Merging subarrays from index " + i + " to " + m + " and from " + (m + 1) + " to " + j);
// 合并两个子数组并将结果存入辅助数组 a2
merge(a1, i, m, m + 1, j, a2);
}
// 将辅助数组 a2 中的结果复制回原数组 a1
System.arraycopy(a2, 0, a1, 0, n);
}
}
public static void main(String[] args) {
int[] array = {5, 2, 4, 6, 1, 3};
System.out.println("Before sorting: " + java.util.Arrays.toString(array));
sort(array);
System.out.println("After sorting: " + java.util.Arrays.toString(array));
}
}
归并+插入
java
import java.util.Arrays;
public class HybridSort {
/**
* 插入排序算法
*
* @param a 待排序的数组
* @param left 子数组的起始索引
* @param right 子数组的结束索引
*/
public static void insertion(int[] a, int left, int right) {
// 从 left + 1 开始遍历到 right
for (int low = left + 1; low <= right; low++) {
int t = a[low]; // 当前要插入的元素
int i = low - 1; // 比较指针,从当前元素的前一个位置开始
// 将大于 t 的元素向右移动一位
while (i >= left && t < a[i]) {
a[i + 1] = a[i];
i--;
}
// 如果 i != low - 1,说明 t 需要插入到正确的位置
if (i != low - 1) {
a[i + 1] = t;
}
}
}
/**
* 合并两个已排序的子数组
*
* @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; // 初始化目标数组的索引 k 为起始位置 i
// 当两个子数组都没有处理完时,比较并合并它们
while (i <= iEnd && j <= jEnd) {
if (a1[i] < a1[j]) {
a2[k] = a1[i]; // 将较小的元素放入目标数组 a2
i++; // 移动指针 i
} else {
a2[k] = a1[j]; // 将较小的元素放入目标数组 a2
j++; // 移动指针 j
}
k++; // 移动目标数组的索引 k
}
// 如果左子数组还有剩余元素,复制到目标数组 a2
if (i > iEnd) {
System.arraycopy(a1, j, a2, k, jEnd - j + 1);
}
// 如果右子数组还有剩余元素,复制到目标数组 a2
if (j > jEnd) {
System.arraycopy(a1, i, a2, k, iEnd - i + 1);
}
}
/**
* 排序算法的入口函数
*
* @param a1 待排序的数组
*/
public static void sort(int[] a1) {
int[] a2 = new int[a1.length]; // 初始化辅助数组 a2
split(a1, 0, a1.length - 1, a2); // 调用 split 方法进行递归排序
}
/**
* 递归地分割数组并进行排序
*
* @param a1 原始数组
* @param left 子数组的起始索引
* @param right 子数组的结束索引
* @param a2 辅助数组
*/
private static void split(int[] a1, int left, int right, int[] a2) {
// 基本情况:如果子数组只有一个元素,则不需要进一步排序
if (right == left) {
return;
}
// 如果子数组长度小于或等于32,使用插入排序
if (right - left <= 32) {
insertion(a1, left, right);
return;
}
// 分割点 m,将数组分成两部分
int m = (left + right) >>> 1;
// 递归调用 split 方法对左半部分进行排序
split(a1, left, m, a2);
// 递归调用 split 方法对右半部分进行排序
split(a1, m + 1, right, a2);
// 打印调试信息
System.out.println(left + " " + right + " " + Arrays.toString(a1));
// 合并两个已排序的子数组
merge(a1, left, m, m + 1, right, a2);
// 将合并后的结果从辅助数组 a2 复制回原数组 a1
System.arraycopy(a2, left, a1, left, right - left + 1);
}
public static void main(String[] args) {
int[] array = {5, 2, 4, 6, 1, 3, 9, 8, 7, 0};
System.out.println("Before sorting: " + Arrays.toString(array));
sort(array);
System.out.println("After sorting: " + Arrays.toString(array));
}
}
快速排序
单边循环
选择最右侧元素作为基准点,j 找比基准点小的,i 找比基准点大的,一旦找到,二者进行交换,
交换时机:j 找到小的,且与 i 不相等,i 找到 >= 基准点元素后,不应自增,最后基准点与 i 交换,i 即为基准点最终索引
java
public class QuickSort {
/**
* 快速排序的入口函数
*
* @param a 待排序的数组
*/
public static void sort(int[] a) {
quick(a, 0, a.length - 1); // 调用 quick 方法进行递归排序
}
/**
* 快速排序的核心逻辑
*
* @param a 待排序的数组
* @param left 子数组的起始索引
* @param right 子数组的结束索引
*/
private static void quick(int[] a, int left, int right) {
if (left >= right) { // 基本情况:如果子数组长度小于等于1,则不需要进一步排序
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) {
int pv = a[right]; // 基准点元素值,选择数组最后一个元素作为基准点
int i = left; // i 指向当前已找到的小于基准点的元素的最后一个位置
int j = left; // j 用于遍历数组,寻找小于基准点的元素
while (j < right) { // 遍历数组直到 j 到达基准点前一个位置
if (a[j] < pv) { // 如果当前元素小于基准点
if (i != j) { // 如果 i 和 j 不相等,交换它们指向的元素
swap(a, i, j);
}
i++; // 移动 i 指针
}
j++; // 移动 j 指针
}
swap(a, i, right); // 将基准点元素放到正确的位置
return i; // 返回基准点元素的最终位置
}
/**
* 交换数组中两个元素的位置
*
* @param a 数组
* @param i 第一个元素的索引
* @param j 第二个元素的索引
*/
private static void swap(int[] a, int i, int j) {
int t = a[i]; // 临时保存 a[i]
a[i] = a[j]; // 将 a[j] 的值赋给 a[i]
a[j] = t; // 将临时保存的值赋给 a[j]
}
public static void main(String[] args) {
int[] array = {5, 2, 4, 6, 1, 3, 9, 8, 7, 0};
System.out.println("Before sorting: " + java.util.Arrays.toString(array));
sort(array);
System.out.println("After sorting: " + java.util.Arrays.toString(array));
}
}
双边循环
选择最左侧元素作为基准点,j 找比基准点小的,i 找比基准点大的,一旦找到,二者进行交换,
i 从左向右,j 从右向左,最后基准点与 i 交换,i 即为基准点最终索引
java
public class QuickSort {
/**
* 快速排序的入口函数
*
* @param a 待排序的数组
*/
public static void sort(int[] a) {
quick(a, 0, a.length - 1); // 调用 quick 方法进行递归排序
}
/**
* 快速排序的核心逻辑
*
* @param a 待排序的数组
* @param left 子数组的起始索引
* @param right 子数组的结束索引
*/
private static void quick(int[] a, int left, int right) {
if (left >= right) { // 基本情况:如果子数组长度小于等于1,则不需要进一步排序
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) {
int i = left; // 初始化指针 i 指向左边界
int j = right; // 初始化指针 j 指向右边界
int pv = a[left]; // 基准点元素值,选择数组第一个元素作为基准点
while (i < j) { // 当 i 和 j 不相遇时继续循环
// 从右向左寻找比基准点小的元素
while (i < j && a[j] > pv) {
j--; // 如果当前元素大于基准点,继续向左移动 j
}
// 从左向右寻找比基准点大的元素
while (i < j && pv >= a[i]) {
i++; // 如果当前元素小于或等于基准点,继续向右移动 i
}
// 交换找到的两个元素
swap(a, i, j);
}
// 将基准点元素放到正确的位置
swap(a, left, j);
// 返回基准点元素的最终位置
return j;
}
/**
* 交换数组中两个元素的位置
*
* @param a 数组
* @param i 第一个元素的索引
* @param j 第二个元素的索引
*/
private static void swap(int[] a, int i, int j) {
int t = a[i]; // 临时保存 a[i]
a[i] = a[j]; // 将 a[j] 的值赋给 a[i]
a[j] = t; // 将临时保存的值赋给 a[j]
}
public static void main(String[] args) {
int[] array = {5, 2, 4, 6, 1, 3, 9, 8, 7, 0};
System.out.println("Before sorting: " + java.util.Arrays.toString(array));
sort(array);
System.out.println("After sorting: " + java.util.Arrays.toString(array));
}
}
计数排序
java
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;
}
}
// 创建计数数组,大小为 max - min + 1
int[] counting = new int[max - min + 1];
// 统计每个元素出现的次数
for (int i : a) {
counting[i - min]++;
}
// 计算累积频率,使得 counting[i] 表示小于或等于 i 的所有元素的数量
for (int i = 1; i < counting.length; i++) {
counting[i] = counting[i] + counting[i - 1];
}
// 创建输出数组
int[] b = new int[a.length];
// 根据累积频率构建输出数组
for (int i = a.length - 1; i >= 0; i--) {
int j = a[i] - min; // 计算当前元素在计数数组中的索引
counting[j]--; // 减少当前元素的计数
b[counting[j]] = a[i]; // 将当前元素放入输出数组的正确位置
}
// 将输出数组复制回原数组
System.arraycopy(b, 0, a, 0, a.length);
}
桶排序
java
public class BucketSort {
/**
* 实现桶排序算法
*
* @param a 输入的整数数组
* @param range 每个桶覆盖的数值范围
*/
public static void sort(int[] a, int range) {
if (a == null || a.length <= 1) {
return; // 如果数组为空或只有一个元素,则无需排序
}
// 找到数组中的最小值和最大值
int max = a[0];
int min = a[0];
for (int i : a) {
if (i > max) {
max = i;
}
if (i < min) {
min = i;
}
}
// 计算需要多少个桶
// 每个桶覆盖的范围是 `range`
DynamicArray[] buckets = new DynamicArray[(max - min) / range + 1];
System.out.println("Bucket count: " + buckets.length);
// 初始化每个桶
for (int i = 0; i < buckets.length; i++) {
buckets[i] = new DynamicArray();
}
// 将输入数组中的每个元素分配到相应的桶中
for (int v : a) {
DynamicArray bucket = buckets[(v - min) / range];
bucket.addLast(v);
}
int k = 0; // 用于记录排序后数组的下标
for (DynamicArray bucket : buckets) {
// 对每个桶内的元素进行插入排序
int[] array = bucket.array(); // 获取桶中的数据
InsertionSort.sort(array);
System.out.println(Arrays.toString(array)); // 输出排序后的桶内容
// 将排序后的桶内容放回原数组
for (int v : array) {
a[k++] = v;
}
}
}
}
基数排序
java
public static void radixSort(String[] a, int length) {
// 创建128个桶,对应ASCII字符集中的每个可能字符(0-127)
ArrayList<String>[] buckets = new ArrayList[128];
for (int i = 0; i < buckets.length; i++) {
buckets[i] = new ArrayList<>();
}
// 从最低有效位(最右边的字符)开始处理
for (int i = length - 1; i >= 0 ; i--) {
// 将每个字符串放入对应的桶中
for (String s : a) {
// 根据当前字符的ASCII值选择桶
buckets[s.charAt(i)].add(s);
}
// 将所有桶中的字符串重新合并回原数组
int k = 0;
for (ArrayList<String> bucket : buckets) {
for (String s : bucket) {
a[k++] = s;
}
// 清空桶以供下一轮使用
bucket.clear();
}
}
}
根据另一个数组次序排序-Leetcode 1122
给你两个数组,
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]
java
public int[] relativeSortArray(int[] arr1, int[] arr2) {
int[] count=new int[1001];
for(int i:arr1) {
count[i]++;
}
int[] result=new int[arr1.length];
int k=0;
for(int i:arr2) {
while(count[i]>0) {
result[k++]=i;
count[i]--;
}
}
for (int i = 0; i < count.length; i++) {
while(count[i]>0) {
result[k++]=i;
count[i]--;
}
}
return result;
}
按出现频率排序-Leetcode 1636
给你一个整数数组
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]
java
public int[] frequencySort(int[] nums) {
int[] count = new int[201];
for (int i : nums) {
count[i + 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();
}
最大间距-Leetcode 164
给定一个无序的数组
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。
java
public int maximumGap(int[] nums) {
// 1. 处理特殊情况
if (nums.length < 2) {
return 0;
}
// 2. 桶排序
int max = nums[0];
int min = nums[0];
for (int i1 = 1; i1 < nums.length; i1++) {
if (nums[i1] > max) {
max = nums[i1];
}
if (nums[i1] < min) {
min = nums[i1];
}
}
// 2.1 准备桶
/*
计算桶个数 期望桶个数
(max - min) / range + 1 = nums.length
(max - min) / (nums.length - 1) = range
*/
int range = Math.max((max - min) / (nums.length - 1), 1);
DynamicArray[] buckets = new DynamicArray[(max - min) / range + 1];
for (int i1 = 0; i1 < buckets.length; i1++) {
buckets[i1] = new DynamicArray();
}
// 2.2 放入数据
for (int age : nums) {
buckets[(age - min) / range].addLast(age);
}
int k = 0;
for (DynamicArray bucket : buckets) {
// 2.3 排序桶内元素
int[] array = bucket.array();
InsertionSort.sort(array);
System.out.println(Arrays.toString(array));
// 2.4 把每个桶排序好的内容,依次放入原始数组
for (int v : array) {
nums[k++] = v;
}
}
// 3. 寻找最大差值
int r = 0;
for (int i = 1; i < nums.length; i++) {
r = Math.max(r, nums[i] - nums[i - 1]);
}
return r;
}