目录
- 冒泡排序 - 递增
- 冒泡排序 - 递减
- 选择排序 - 递增
- 选择排序 - 递减
- 插入排序 - 递增
- 插入排序 - 递减
- 快速排序 - 递增
- 快速排序 - 递减
- 堆排序 - 最大堆
- 堆排序 - 最小堆
- 归并排序 - 递增
- 归并排序 - 递减
- 希尔排序 - 递增
- 希尔排序 - 递减
- 计数排序 - 递增
- 计数排序 - 递减
- 桶排序 - 递增
- 桶排序 - 递减
- 基数排序 - 递增
- 基数排序 - 递减
冒泡排序 - 递增
/**
* 交换数组中两个元素的位置
*
* @param nums
* @param i
* @param j
*/
private void swap(int[] nums, int i, int j) {
if (i != j) { // 两个相同的数^结果为0,开头加一个判断可以避免这种情况
nums[i] ^= nums[j];
nums[j] ^= nums[i];
nums[i] ^= nums[j];
}
}
/**
* 冒泡排序(递增排序)
*
* @param nums
*/
private void bubbleSort_1(int[] nums) {
int len = nums.length;
boolean flag;
for (int i = 0; i < len - 1; i++) { // 进行性len-1趟排序
flag = true;
for (int j = 0; j < len - i - 1; j++) { // 遍历无序区
if (nums[j] > nums[j + 1]) { // 将最大的元素移动到无序区末尾
swap(nums, j, j + 1);
flag = false;
}
}
if (flag) { // 无序区中没有发生交换说明已经有序
break;
}
}
}
/**
* 冒泡排序 - 递增
* 1. 思想
* [无序区,有序区],从无序区通过交换找出最大元素放到有序区前端
* 2. 复杂度
* (1)平均时间复杂度:O(n^2)
* (2)最差时间复杂度:O(n^2)
* (3)空间复杂度:O(1)
* (4)稳定性:稳定
*
* @param nums
* @param k
* @return
*/
public void sortTest(int[] nums) {
bubbleSort_1(nums);
}
冒泡排序 - 递减
/**
* 交换数组中两个元素的位置
*
* @param nums
* @param i
* @param j
*/
private void swap(int[] nums, int i, int j) {
if (i != j) { // 两个相同的数^结果为0,开头加一个判断可以避免这种情况
nums[i] ^= nums[j];
nums[j] ^= nums[i];
nums[i] ^= nums[j];
}
}
/**
* 冒泡排序(递减排序)
*
* @param nums
*/
private void bubbleSort_2(int[] nums) {
int len = nums.length;
boolean flag;
for (int i = 0; i < len - 1; i++) { // 进行性len-1趟排序
flag = true;
for (int j = 0; j < len - i - 1; j++) { // 遍历无序区
if (nums[j] < nums[j + 1]) { // 将最小的元素移动到无序区末尾
swap(nums, j, j + 1);
flag = false;
}
}
if (flag) { // 无序区中没有发生交换说明已经有序
break;
}
}
}
/**
* 冒泡排序 - 递减
* 1. 思想
* [无序区,有序区],从无序区通过交换找出最小元素放到有序区前端
* 2. 复杂度
* (1)平均时间复杂度:O(n^2)
* (2)最差时间复杂度:O(n^2)
* (3)空间复杂度:O(1)
* (4)稳定性:稳定
*
* @param nums
* @param k
* @return
*/
public void sortTest(int[] nums) {
bubbleSort_2(nums);
}
选择排序 - 递增
/**
* 交换数组中两个元素的位置
*
* @param nums
* @param i
* @param j
*/
private void swap(int[] nums, int i, int j) {
if (i != j) { // 两个相同的数^结果为0,开头加一个判断可以避免这种情况
nums[i] ^= nums[j];
nums[j] ^= nums[i];
nums[i] ^= nums[j];
}
}
/**
* 选择排序(递增排序)
*
* @param nums
*/
private void selectSort_1(int[] nums) {
int len = nums.length;
for (int i = 0; i < len - 1; i++) { // 进行性len-1趟排序
int min = i; // 初始为有序区末尾元素
for (int j = i + 1; j < len; j++) { // 遍历无序区
if (nums[j] < nums[min]) { // 找到[有序区末尾元素,无序区]中最小元素下标
min = j;
}
}
if (i != min) { // i == min时也可以交换,不影响结果
swap(nums, i, min); // 交换有序区末尾元素和[有序区末尾元素,无序区]中最小元素
}
}
}
/**
* 选择排序 - 递增
* 1. 思想
* [有序区,无序区],在无序区里找一个最小的元素跟在有序区的后面
* 2. 复杂度
* (1)平均时间复杂度:O(n^2)
* (2)最差时间复杂度:O(n^2)
* (3)空间复杂度:O(1)
* (4)稳定性:数组不稳定、链表稳定
*
* @param nums
* @param k
* @return
*/
public void sortTest(int[] nums) {
selectSort_1(nums);
}
选择排序 - 递减
/**
* 交换数组中两个元素的位置
*
* @param nums
* @param i
* @param j
*/
private void swap(int[] nums, int i, int j) {
if (i != j) { // 两个相同的数^结果为0,开头加一个判断可以避免这种情况
nums[i] ^= nums[j];
nums[j] ^= nums[i];
nums[i] ^= nums[j];
}
}
/**
* 选择排序(递减排序)
*
* @param nums
*/
private void selectSort_2(int[] nums) {
int len = nums.length;
for (int i = 0; i < len - 1; i++) { // 进行性len-1趟排序
int max = i; // 初始为有序区末尾元素
for (int j = i + 1; j < len; j++) { // 遍历无序区
if (nums[j] > nums[max]) { // 找到[有序区末尾元素,无序区]中最大元素下标
max = j;
}
}
if (i != max) { // i == min时也可以交换,不影响结果
swap(nums, i, max); // 交换有序区末尾元素和[有序区末尾元素,无序区]中最大元素
}
}
}
/**
* 选择排序 - 递减
* 1. 思想
* [有序区,无序区],在无序区里找一个最大的元素跟在有序区的后面
* 2. 复杂度
* (1)平均时间复杂度:O(n^2)
* (2)最差时间复杂度:O(n^2)
* (3)空间复杂度:O(1)
* (4)稳定性:数组不稳定、链表稳定
*
* @param nums
* @param k
* @return
*/
public void sortTest(int[] nums) {
selectSort_2(nums);
}
插入排序 - 递增
/**
* 插入排序(递增排序)
* 针对有序序列在插入时采用移动法
*
* @param nums
*/
private void insertSort_1(int[] nums) {
int len = nums.length;
for (int i = 1; i < len; i++) { // 有序区从前往后扩张
int insert = nums[i]; // insert为无序区第一个元素
for (int j = i - 1; j >= 0; j--) { // 从后往前遍历有序区
if (insert < nums[j]) { // insert小于前一个元素
nums[j + 1] = nums[j]; // 将nums[j]后移一位覆盖nums[j + 1]
nums[j] = insert; // insert插入原来nums[j]的位置
} else { // insert大于前一个的元素,insert保持原来位置即可
break;
}
}
}
}
/**
* 插入排序 - 递增
* 1. 思想
* [有序区,无序区],把无序区的第一个元素插入到有序区的合适的位置
* 2. 复杂度
* (1)平均时间复杂度:O(n^2)
* (2)最差时间复杂度:O(n^2)
* (3)空间复杂度:O(1)
* (4)稳定性:稳定
*
* @param nums
* @param k
* @return
*/
public void sortTest(int[] nums) {
insertSort_1(nums);
}
插入排序 - 递减
/**
* 插入排序(递减排序)
* 针对有序序列在插入时采用移动法
*
* @param nums
*/
private void insertSort_2(int[] nums) {
int len = nums.length;
for (int i = 1; i < len; i++) { // 有序区从前往后扩张
int insert = nums[i]; // insert为无序区第一个元素
for (int j = i - 1; j >= 0; j--) { // 从后往前遍历有序区
if (insert > nums[j]) { // insert大于前一个元素
nums[j + 1] = nums[j]; // 将nums[j]后移一位覆盖nums[j + 1]
nums[j] = insert; // insert插入原来nums[j]的位置
} else { // insert小于前一个的元素,insert保持原来位置即可
break;
}
}
}
}
/**
* 插入排序 - 递减
* 1. 思想
* [有序区,无序区],把无序区的第一个元素插入到有序区的合适的位置
* 2. 复杂度
* (1)平均时间复杂度:O(n^2)
* (2)最差时间复杂度:O(n^2)
* (3)空间复杂度:O(1)
* (4)稳定性:稳定
*
* @param nums
* @param k
* @return
*/
public void sortTest(int[] nums) {
insertSort_2(nums);
}
快速排序 - 递增
/**
* 交换数组中两个元素的位置
*
* @param nums
* @param i
* @param j
*/
private void swap(int[] nums, int i, int j) {
if (i != j) { // 两个相同的数^结果为0,开头加一个判断可以避免这种情况
nums[i] ^= nums[j];
nums[j] ^= nums[i];
nums[i] ^= nums[j];
}
}
/**
* 快速排序(递增排序)
*
* @param nums
* @param left
* @param right
*/
private void quickSort_1(int[] nums, int left, int right) {
if (left < right) {
int pivot = left; // 简便起见,默认以第一个元素作为主元
int index = left + 1; // index指向[小数]区间最后一个元素下标
for (int i = index; i <= right; i++) {
if (nums[i] < nums[pivot]) { // 比主元小的元素都交换至[小数]区间
swap(nums, index, i);
index++;
}
}
swap(nums, index - 1, pivot); // 将主元和[小数]区间最后一位元素交换位置
pivot = index - 1; // 主元下标
quickSort_1(nums, left, pivot - 1); // 对[小数]区间进行快排
quickSort_1(nums, pivot + 1, right); // 对[大数]区间进行快排
}
}
/**
* 快速排序 - 递增
* 1. 思想
* [小数,基准元素,大数],在区间中随机挑选一个元素作基准,将小于基准的元素放在基准之前,
* 大于基准的元素放在基准之后,再分别对小数区与大数区进行排序
* 2. 复杂度
* (1)平均时间复杂度:O(nlogn)
* (2)最差时间复杂度:O(n^2)
* (3)空间复杂度:O(logn)
* (4)稳定性:不稳定
*
* @param nums
* @param k
* @return
*/
public void sortTest(int[] nums) {
quickSort_1(nums, 0, nums.length - 1);
}
快速排序 - 递减
/**
* 交换数组中两个元素的位置
*
* @param nums
* @param i
* @param j
*/
private void swap(int[] nums, int i, int j) {
if (i != j) { // 两个相同的数^结果为0,开头加一个判断可以避免这种情况
nums[i] ^= nums[j];
nums[j] ^= nums[i];
nums[i] ^= nums[j];
}
}
/**
* 快速排序(递减排序)
*
* @param nums
* @param left
* @param right
*/
private void quickSort_2(int[] nums, int left, int right) {
if (left < right) {
int pivot = left; // 简便起见,默认以第一个元素作为主元
int index = left + 1; // index指向[大数]区间最后一个元素下标
for (int i = index; i <= right; i++) {
if (nums[i] > nums[pivot]) { // 比主元大的元素都交换至[大数]区间
swap(nums, index, i);
index++;
}
}
swap(nums, index - 1, pivot); // 将主元和[大数]区间最后一位元素交换位置
pivot = index - 1; // 主元下标
quickSort_2(nums, left, pivot - 1); // 对[大数]区间进行快排
quickSort_2(nums, pivot + 1, right); // 对[小数]区间进行快排
}
}
/**
* 快速排序 - 递减
* 1. 思想
* [大数,基准元素,小数],在区间中随机挑选一个元素作基准,将大于基准的元素放在基准之前,
* 小于基准的元素放在基准之后,再分别对大数区与小数区进行排序
* 2. 复杂度
* (1)平均时间复杂度:O(nlogn)
* (2)最差时间复杂度:O(n^2)
* (3)空间复杂度:O(logn)
* (4)稳定性:不稳定
*
* @param nums
* @param k
* @return
*/
public void sortTest(int[] nums) {
quickSort_2(nums, 0, nums.length - 1);
}
堆排序 - 最大堆
/**
* 交换数组中两个元素的位置
*
* @param nums
* @param i
* @param j
*/
private void swap(int[] nums, int i, int j) {
if (i != j) { // 两个相同的数^结果为0,开头加一个判断可以避免这种情况
nums[i] ^= nums[j];
nums[j] ^= nums[i];
nums[i] ^= nums[j];
}
}
/**
* 对nums数组在[low, high]范围进行向下调整
* 时间复杂度:O(logn)
*
* @param nums
* @param low
* @param high
*/
private void downAdjust_1(int[] nums, int low, int high) {
int dad = low, son = dad * 2 + 1; // dad为欲调整节点下标,son为其左孩子下标
while (son <= high) { // 存在孩子节点
if (son + 1 <= high && nums[son + 1] > nums[son]) { // 右孩子存在且右孩子大于左孩子
son++; // son指向右孩子下标
}
if (nums[son] > nums[dad]) { // 左右孩子中最大者比欲调整节点大
swap(nums, son, dad); // 欲调整节点和左右孩子中最大者交换位置
dad = son; // dad指向欲调整节点最新位置
son = dad * 2 + 1; // son指向欲调整节点最新位置左孩子下标
} else { // 左右孩子中最大者比欲调整节点小,调整结束
break;
}
}
}
/**
* 建最大堆
* (1)完全二叉树叶子节点个数为len/2,因此数组下标在[0, len/2-1]范围内的节点都是非叶子节点
* (2)故可以从len/2-1开始倒着枚举节点
* 时间复杂度:O(n)
*
* @param nums
* @param len
*/
private void createMaxHeap(int[] nums, int len) {
for (int i = len / 2 - 1; i >= 0; i--) { // 从后往前遍历非叶子节点
downAdjust_1(nums, i, len - 1);
}
}
/**
* 堆排序 - 最大堆
* 1. 思想
* [最大堆,有序区],从堆顶把根拿出来放在有序区之前,再恢复堆
* 2. 复杂度
* (1)平均时间复杂度:O(nlogn)
* (2)最差时间复杂度:O(nlogn)
* (3)空间复杂度:O(1)
* (4)稳定性:不稳定
*
* @param nums
* @param k
* @return
*/
public void sortTest(int[] nums) {
int len = nums.length;
createMaxHeap(nums, len); // 建最大堆
for (int i = len - 1; i > 0; i--) { // 从后往前遍历,直到堆中只剩一个元素
swap(nums, i, 0); // 交换最大堆中最后一个元素nums[i]与堆顶nums[0]
downAdjust_1(nums, 0, i - 1); // 堆中元素减1,调整堆顶
}
}
堆排序 - 最小堆
/**
* 交换数组中两个元素的位置
*
* @param nums
* @param i
* @param j
*/
private void swap(int[] nums, int i, int j) {
if (i != j) { // 两个相同的数^结果为0,开头加一个判断可以避免这种情况
nums[i] ^= nums[j];
nums[j] ^= nums[i];
nums[i] ^= nums[j];
}
}
/**
* 对nums数组在[low, high]范围进行向下调整
* 时间复杂度:O(logn)
*
* @param nums
* @param low
* @param high
*/
private void downAdjust_2(int[] nums, int low, int high) {
int dad = low, son = dad * 2 + 1; // dad为欲调整节点下标,son为其左孩子下标
while (son <= high) { // 存在孩子节点
if (son + 1 <= high && nums[son + 1] < nums[son]) { // 右孩子存在且右孩子小于左孩子
son++; // son指向右孩子下标
}
if (nums[son] < nums[dad]) { // 左右孩子中最小者比欲调整节点小
swap(nums, son, dad); // 欲调整节点和左右孩子中最小者交换位置
dad = son; // dad指向欲调整节点最新位置
son = dad * 2 + 1; // son指向欲调整节点最新位置左孩子下标
} else { // 左右孩子中最小者比欲调整节点大,调整结束
break;
}
}
}
/**
* 建最小堆
* (1)完全二叉树叶子节点个数为len/2,因此数组下标在[0, len/2-1]范围内的节点都是非叶子节点
* (2)故可以从len/2-1开始倒着枚举节点
* 时间复杂度:O(n)
*
* @param nums
* @param len
*/
private void createMinHeap(int[] nums, int len) {
for (int i = len / 2 - 1; i >= 0; i--) { // 从后往前遍历非叶子节点
downAdjust_2(nums, i, len - 1);
}
}
/**
* 堆排序 - 最小堆
* 1. 思想
* [最小堆,有序区],从堆顶把根拿出来放在有序区之前,再恢复堆
* 2. 复杂度
* (1)平均时间复杂度:O(nlogn)
* (2)最差时间复杂度:O(nlogn)
* (3)空间复杂度:O(1)
* (4)稳定性:不稳定
*
* @param nums
* @param k
* @return
*/
public void sortTest(int[] nums) {
int len = nums.length;
createMinHeap(nums, len); // 建最小堆
for (int i = len - 1; i > 0; i--) { // 从后往前遍历,直到堆中只剩一个元素
swap(nums, i, 0); // 交换最小堆中最后一个元素nums[i]与堆顶nums[0]
downAdjust_2(nums, 0, i - 1); // 堆中元素减1,调整堆顶
}
}
归并排序 - 递增
/**
* 将递增区间nums[l1, r1]与nums[l2, r2]合并为递增区间
* l1 = left, r1 = mid, l2 = mid + 1, r2 = right
*
* @param nums
* @param left
* @param mid
* @param right
*/
private void merge_1(int[] nums, int left, int mid, int right) {
int i = left, j = mid + 1, index = 0; // i指向nums[l1],j指向nums[l2]
int[] temp = new int[right - left + 1]; // temp临时存放合并后的数组,index为其下标
while (i <= mid && j <= right) { // 遍历nums[l1, r1]和nums[l2, r2]公共长度部分
temp[index++] = nums[i] < nums[j] ? nums[i++] : nums[j++]; // 将较小的元素放前面
}
while (i <= mid) { // 将nums[l1, r1]剩余元素加入temp
temp[index++] = nums[i++];
}
while (j <= right) { // 将nums[l2, r2]剩余元素加入temp
temp[index++] = nums[j++];
}
for (int num : temp) {
nums[left++] = num; // 将合并后的序列temp赋值回nums
}
}
/**
* 归并排序(递增排序)
*/
private void mergeSort_1(int[] nums, int left, int right) {
if (left < right) {
int mid = left + (right - left) / 2; // 取[left, right]中点
mergeSort_1(nums, left, mid); // 左子区间归并排序
mergeSort_1(nums, mid + 1, right); // 右子区间归并排序
merge_1(nums, left, mid, right); // 将左子区间和右子区间合并
}
}
/**
* 归并排序 - 递增
* 1. 思想
* 归并排序的核心在于将两个有序序列合并为一个有序序列
* 2. 复杂度
* (1)平均时间复杂度:O(nlogn)
* (2)最差时间复杂度:O(nlogn)
* (3)空间复杂度:O(n)
* (4)稳定性:稳定
*
* @param nums
* @param k
* @return
*/
public void sortTest(int[] nums) {
mergeSort_1(nums, 0, nums.length - 1);
}
归并排序 - 递减
/**
* 将递减区间nums[l1, r1]与nums[l2, r2]合并为递减区间
* l1 = left, r1 = mid, l2 = mid + 1, r2 = right
*
* @param nums
* @param left
* @param mid
* @param right
*/
private void merge_2(int[] nums, int left, int mid, int right) {
int i = left, j = mid + 1, index = 0; // i指向nums[l1],j指向nums[l2]
int[] temp = new int[right - left + 1]; // temp临时存放合并后的数组,index为其下标
while (i <= mid && j <= right) { // 遍历nums[l1, r1]和nums[l2, r2]公共长度部分
temp[index++] = nums[i] > nums[j] ? nums[i++] : nums[j++]; // 将较大的元素放前面
}
while (i <= mid) { // 将nums[l1, r1]剩余元素加入temp
temp[index++] = nums[i++];
}
while (j <= right) { // 将nums[l2, r2]剩余元素加入temp
temp[index++] = nums[j++];
}
for (int num : temp) {
nums[left++] = num; // 将合并后的序列temp赋值回nums
}
}
/**
* 归并排序(递减排序)
*/
private void mergeSort_2(int[] nums, int left, int right) {
if (left < right) {
int mid = left + (right - left) / 2; // 取[left, right]中点
mergeSort_2(nums, left, mid); // 左子区间归并排序
mergeSort_2(nums, mid + 1, right); // 右子区间归并排序
merge_2(nums, left, mid, right); // 将左子区间和右子区间合并
}
}
/**
* 归并排序 - 递减
* 1. 思想
* 归并排序的核心在于将两个有序序列合并为一个有序序列
* 2. 复杂度
* (1)平均时间复杂度:O(nlogn)
* (2)最差时间复杂度:O(nlogn)
* (3)空间复杂度:O(n)
* (4)稳定性:稳定
*
* @param nums
* @param k
* @return
*/
public void sortTest(int[] nums) {
mergeSort_2(nums, 0, nums.length - 1);
}
希尔排序 - 递增
/**
* 交换数组中两个元素的位置
*
* @param nums
* @param i
* @param j
*/
private void swap(int[] nums, int i, int j) {
if (i != j) { // 两个相同的数^结果为0,开头加一个判断可以避免这种情况
nums[i] ^= nums[j];
nums[j] ^= nums[i];
nums[i] ^= nums[j];
}
}
/**
* 希尔排序(递增排序)
* (1)针对有序序列在插入时采用交换法
* (2)选择增量gap=len/2,缩小增量继续以gap=gap/2的方式
* (3){n/2, (n/2)/2, ..., 1},称为增量序列
*
* @param nums
*/
private void shellSort_1(int[] nums) {
int len = nums.length;
for (int gap = len / 2; gap > 0; gap /= 2) {
for (int i = gap; i < len; i++) { // 有序区从前往后扩张
for (int j = i - gap; j >= 0; j -= gap) { // 从后往前遍历有序区
if (nums[j + gap] < nums[j]) { // 欲插入元素小于前一位元素
swap(nums, j, j + gap); // 交换二者位置
}
}
}
}
}
/**
* 希尔排序 - 递增
* 1. 思想
* 每一轮按照事先决定的间隔进行插入排序,间隔会依次缩小,最后一次一定要是1
* 2. 复杂度
* (1)平均时间复杂度:O(n(logn)^2)
* (2)最差时间复杂度:O(n^2)
* (3)空间复杂度:O(1)
* (4)稳定性:不稳定
*
* @param nums
* @param k
* @return
*/
public void sortTest(int[] nums) {
shellSort_1(nums);
}
希尔排序 - 递减
/**
* 交换数组中两个元素的位置
*
* @param nums
* @param i
* @param j
*/
private void swap(int[] nums, int i, int j) {
if (i != j) { // 两个相同的数^结果为0,开头加一个判断可以避免这种情况
nums[i] ^= nums[j];
nums[j] ^= nums[i];
nums[i] ^= nums[j];
}
}
/**
* 希尔排序(递减排序)
* (1)针对有序序列在插入时采用交换法
* (2)选择增量gap=len/2,缩小增量继续以gap=gap/2的方式
* (3){n/2, (n/2)/2, ..., 1},称为增量序列
*
* @param nums
*/
private void shellSort_2(int[] nums) {
int len = nums.length;
for (int gap = len / 2; gap > 0; gap /= 2) {
for (int i = gap; i < len; i++) { // 有序区从前往后扩张
for (int j = i - gap; j >= 0; j -= gap) { // 从后往前遍历有序区
if (nums[j + gap] > nums[j]) { // 欲插入元素大于前一位元素
swap(nums, j, j + gap); // 交换二者位置
}
}
}
}
}
/**
* 希尔排序 - 递减
* 1. 思想
* 每一轮按照事先决定的间隔进行插入排序,间隔会依次缩小,最后一次一定要是1
* 2. 复杂度
* (1)平均时间复杂度:O(n(logn)^2)
* (2)最差时间复杂度:O(n^2)
* (3)空间复杂度:O(1)
* (4)稳定性:不稳定
*
* @param nums
* @param k
* @return
*/
public void sortTest(int[] nums) {
shellSort_2(nums);
}
计数排序 - 递增
/**
* 计数排序(递增排序)
* 计数排序不是比较排序,排序的速度快于任何比较排序算法
*
* @param nums
*/
private void countSort_1(int[] nums) {
int max = nums[0], min = nums[0];
for (int num : nums) { // 找到最大值和最小值
if (num > max) {
max = num;
}
if (num < min) {
min = num;
}
}
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++) { // 从前往后遍历count数组,替换掉nums数组
for (int j = 0; j < count[i]; j++) {
nums[k++] = min + i;
}
}
}
/**
* 计数排序 - 递增
* 1. 思想
* 在给定的一组序列中,先找出该序列中的最大值和最小值,从而确定需要开辟多大的辅助空间,
* 每一个数在对应的辅助空间中都有唯一的下标
* 2. 复杂度
* (1)平均时间复杂度:O(n+m)
* (2)最差时间复杂度:O(n+m)
* (3)空间复杂度:O(m)
* (4)稳定性:稳定
* m:代表计数范围
*
* @param nums
* @param k
* @return
*/
public void sortTest(int[] nums) {
countSort_1(nums);
}
计数排序 - 递减
/**
* 计数排序(递减排序)
* 计数排序不是比较排序,排序的速度快于任何比较排序算法
*
* @param nums
*/
private void countSort_2(int[] nums) {
int max = nums[0], min = nums[0];
for (int num : nums) { // 找到最大值和最小值
if (num > max) {
max = num;
}
if (num < min) {
min = num;
}
}
int[] count = new int[max - min + 1]; // 根据最大值和最小值确定计数范围
for (int num : nums) { // 开始计数
count[num - min]++;
}
int k = 0;
for (int i = count.length - 1; i >= 0; i--) { // 从后往前遍历count数组,替换掉nums数组
for (int j = 0; j < count[i]; j++) {
nums[k++] = min + i;
}
}
}
/**
* 计数排序 - 递减
* 1. 思想
* 在给定的一组序列中,先找出该序列中的最大值和最小值,从而确定需要开辟多大的辅助空间,
* 每一个数在对应的辅助空间中都有唯一的下标
* 2. 复杂度
* (1)平均时间复杂度:O(n+m)
* (2)最差时间复杂度:O(n+m)
* (3)空间复杂度:O(m)
* (4)稳定性:稳定
* m:代表计数范围
*
* @param nums
* @param k
* @return
*/
public void sortTest(int[] nums) {
countSort_2(nums);
}
桶排序 - 递增
/**
* 桶排序(递增排序)
*
* @param nums
*/
private void bucketSort_1(int[] nums) {
int len = nums.length;
int max = nums[0], min = nums[0];
for (int num : nums) { // 找到最大值和最小值
if (num > max) {
max = num;
}
if (num < min) {
min = num;
}
}
int gap = (max - min) / len + 1; // 确定桶的跨度大小
int bucketNum = (max - min) / gap + 1; // 确定桶的个数
List<List<Integer>> bucketList = new ArrayList<>(); // 创建一组桶
for (int i = 0; i < bucketNum; i++) { // 初始化每个桶
bucketList.add(new LinkedList<>());
}
for (int i = 0; i < nums.length; i++) { // 遍历数组,将数据放入对应桶中
bucketList.get((nums[i] - min) / gap).add(nums[i]);
}
for (int i = 0; i < bucketList.size(); i++) { // 对每个桶内部从小到大进行排序
Collections.sort(bucketList.get(i)); // 底层采用了归并排序的优化版本
}
int k = 0;
for (int i = 0; i < bucketNum; i++) { // 把每个桶排序好的数据进行合并汇总放回原数组
for (Integer num : bucketList.get(i)) {
nums[k++] = num;
}
}
}
/**
* 桶排序 - 递增
* 1. 思想
* 将最大值和最小值之间的元素进行瓜分,设定某个桶的容量,然后再定义每个桶的跨度,然后根据元素的大小范围,
* 将他们放入各自的桶中,再对每个桶进行排序,最后按顺序输出所有桶中元素
* 2. 复杂度
* (1)平均时间复杂度:O(n+m)
* (2)最差时间复杂度:O(n^2)
* (3)空间复杂度:O(n+m)
* (4)稳定性:稳定
* m:代表计数范围
*
* @param nums
* @param k
* @return
*/
public void sortTest(int[] nums) {
bucketSort_1(nums);
}
桶排序 - 递减
/**
* 桶排序(递减排序)
*
* @param nums
*/
private void bucketSort_2(int[] nums) {
int len = nums.length;
int max = nums[0], min = nums[0];
for (int num : nums) { // 找到最大值和最小值
if (num > max) {
max = num;
}
if (num < min) {
min = num;
}
}
int gap = (max - min) / len + 1; // 确定桶的跨度大小
int bucketNum = (max - min) / gap + 1; // 确定桶的个数
List<List<Integer>> bucketList = new ArrayList<>(); // 创建一组桶
for (int i = 0; i < bucketNum; i++) { // 初始化每个桶
bucketList.add(new LinkedList<>());
}
for (int i = 0; i < nums.length; i++) { // 遍历数组,将数据放入对应桶中
bucketList.get((nums[i] - min) / gap).add(nums[i]);
}
for (int i = 0; i < bucketList.size(); i++) { // 对每个桶内部从大到小进行排序
Collections.sort(bucketList.get(i), Collections.reverseOrder()); // 底层采用了归并排序的优化版本
}
int k = 0;
for (int i = bucketNum - 1; i >= 0; i--) { // 把每个桶排序好的数据进行合并汇总放回原数组
for (Integer num : bucketList.get(i)) {
nums[k++] = num;
}
}
}
/**
* 桶排序 - 递减
* 1. 思想
* 将最大值和最小值之间的元素进行瓜分,设定某个桶的容量,然后再定义每个桶的跨度,然后根据元素的大小范围,
* 将他们放入各自的桶中,再对每个桶进行排序,最后按顺序输出所有桶中元素
* 2. 复杂度
* (1)平均时间复杂度:O(n+m)
* (2)最差时间复杂度:O(n^2)
* (3)空间复杂度:O(n+m)
* (4)稳定性:稳定
* m:代表计数范围
*
* @param nums
* @param k
* @return
*/
public void sortTest(int[] nums) {
bucketSort_2(nums);
}
基数排序 - 递增
/**
* 基数排序(递增排序)
* (1)所有数字都是由0~9组成,因此需要10个桶来排序
* (2)暂时无法处理负数
*
* @param nums
*/
private void radixSort_1(int[] nums) {
int len = nums.length;
int max = nums[0];
for (int num : nums) { // 找到最大数
if (num > max) {
max = num;
}
}
int digit = 0;
while (max != 0) { // 获取最大数的位数
max /= 10;
digit++;
}
int bucketNum = 10; // 创建10个桶
List<List<Integer>> bucketList = new ArrayList<>();
for (int i = 0; i < bucketNum; i++) { // 初始化每个桶
bucketList.add(new LinkedList<>());
}
for (int i = 0; i < digit; i++) { // 按最低位优先依次对各关键字进行分配和收集
for (int j = 0; j < len; j++) { // 第i趟分配
int radix = (int) (nums[j] / Math.pow(10, i)) % 10; // 获取当前数字第i位的数
bucketList.get(radix).add(nums[j]); // 放进对应的桶里
}
int k = 0;
for (List<Integer> bucket : bucketList) { // 第i趟收集,按第i位从小到大的顺序将num排序
for (Integer num : bucket) {
nums[k++] = num;
}
bucket.clear(); // 将每个桶都清空,供下一次分配和收集使用
}
}
}
/**
* 基数排序 - 递增
* 1. 思想
* 按位数从低到高分配到桶中,有多少位分配多少次,分配完成后,从低位到高位依次取出
* 2. 复杂度
* (1)平均时间复杂度:O(n*k)
* (2)最差时间复杂度:O(n*k)
* (3)空间复杂度:O(n+k)
* (4)稳定性:稳定
* k:代表数值中的"数位"个数
* 3. 优缺点
* (1)优点:在数字位数相差不大的情况下具有较高效率,如[345, 652, 984, 439],都是三位数,需要分配+收集三次
* (2)缺点:在数字位数差距非常大时,效率低下,如[1,999999999999999],需要分配+收集m次,m为较大数字的位数
*
* @param nums
* @param k
* @return
*/
public void sortTest(int[] nums) {
radixSort_1(nums);
}
基数排序 - 递减
/**
* 基数排序(递减排序)
* (1)所有数字都是由0~9组成,因此需要10个桶来排序
* (2)暂时无法处理负数
*
* @param nums
*/
private void radixSort_2(int[] nums) {
int len = nums.length;
int max = nums[0];
for (int num : nums) { // 找到最大数
if (num > max) {
max = num;
}
}
int digit = 0;
while (max != 0) { // 获取最大数的位数
max /= 10;
digit++;
}
int bucketNum = 10; // 创建10个桶
List<List<Integer>> bucketList = new ArrayList<>();
for (int i = 0; i < bucketNum; i++) { // 初始化每个桶
bucketList.add(new LinkedList<>());
}
for (int i = 0; i < digit; i++) { // 按最低位优先依次对各关键字进行分配和收集
for (int j = 0; j < len; j++) { // 第i趟分配
int radix = (int) (nums[j] / Math.pow(10, i)) % 10; // 获取当前数字第i位的数
bucketList.get(radix).add(nums[j]); // 放进对应的桶里
}
int k = 0;
for (int j = bucketNum - 1; j >= 0; j--) { // 第i趟收集,按第i位从大到小的顺序将num排序
List<Integer> bucket = bucketList.get(j);
for (Integer num : bucket) {
nums[k++] = num;
}
bucket.clear(); // 将每个桶都清空,供下一次分配和收集使用
}
}
}
/**
* 基数排序 - 递减
* 1. 思想
* 按位数从低到高分配到桶中,有多少位分配多少次,分配完成后,从低位到高位依次取出
* 2. 复杂度
* (1)平均时间复杂度:O(n*k)
* (2)最差时间复杂度:O(n*k)
* (3)空间复杂度:O(n+k)
* (4)稳定性:稳定
* k:代表数值中的"数位"个数
* 3. 优缺点
* (1)优点:在数字位数相差不大的情况下具有较高效率,如[345, 652, 984, 439],都是三位数,需要分配+收集三次
* (2)缺点:在数字位数差距非常大时,效率低下,如[1,999999999999999],需要分配+收集m次,m为较大数字的位数
*
* @param nums
* @param k
* @return
*/
public void sortTest(int[] nums) {
radixSort_2(nums);
}