排序算法是计算机领域非常重要的一类算法,是很多数据结构和算法的基础,比如查找算法、二叉树结构...
排序算法类 SortTen.java
/**
* @Author: ltx
* @Date: 2021/3/30
* @Description: 10种排序算法
*/
public class SortTen {
/**
* 随机n长度的数组
*
* @param min 最小值
* @param max 最大值
* @param n 数组长度
* @return
*/
public static int[] randomArr(int n, int min, int max) {
int[] arr = new int[n];
Random rand = new Random();
//nextInt该方法的作用是生成一个随机的int值,该值介于[min,max)的区间。
for (int i = 0; i < n; i++) {
arr[i] = min + rand.nextInt(max - min);
}
return arr;
}
/**
* 随机n长度的数组
*
* @param n 数组长度
* @return
*/
public static int[] randomArr(int n) {
return randomArr(n, 0, 100);
}
/**
* 交换元素
*
* @param arr 数组
* @param a 位置a
* @param b 位置b
*/
public static void swap(int[] arr, int a, int b) {
int temp = arr[a];
arr[a] = arr[b];
arr[b] = temp;
}
/**
* 冒泡排序
* 原理:把数字依次和每个数字做比较,大的往后排
*
* @param arr 原数组
* @return 排序之后的数组
*/
private static int[] bubbleSort(int[] arr) {
for (int i = 0; i < arr.length; i++) {
//这里-(i+1),因为冒过泡的数字已经是最大的了,没必要往后比较
for (int j = 0; j < arr.length - (i + 1); j++) {
//前一个 > 后一个,交换
if (arr[j] > arr[j + 1]) {
swap(arr, j, j + 1);
}
}
}
return arr;
}
/**
* 选择排序
* 原理:每次找最小的数字,排到前面
*
* @param arr 原数组
* @return 排序之后的数组
*/
private static int[] selectSort(int[] arr) {
for (int i = 0; i < arr.length; i++) {
int minIndex = i;
//往后比较,查找最小的数字,这里的j=i+1是当前数字和下一个数字开始比较
for (int j = i + 1; j < arr.length; j++) {
if (arr[minIndex] > arr[j]) {
minIndex = j;
}
}
//当前数字不是最小的,执行交换
if (minIndex != i) {
swap(arr, minIndex, i);
}
}
return arr;
}
/**
* 插入排序
* 原理:依次把数字插入到前面已经排好序的数字里面
*
* @param arr 原数组
* @return 排序之后的数组
*/
public static int[] insertionSort(int[] arr) {
//-1是因为用的arr+1开始
for (int i = 0; i < arr.length - 1; i++) {
//当前元素
int t = arr[i + 1];
int j = i;
//将前面大于当前元素的值都往后移
while (j >= 0 && arr[j] > t) {
arr[j + 1] = arr[j];
j--;
}
//找到位置放进来
arr[j + 1] = t;
}
return arr;
}
/**
* 希尔排序
* 原理:插入排序的升级版本,分组做插入排序
*
* @param arr 原数组
* @return 排序之后的数组
*/
public static int[] ShellSort(int[] arr) {
//分组数量
int g = arr.length / 2;
while (g > 0) {
//步长为g的插入排序
//-g是因为用的arr+g开始
for (int i = 0; i < arr.length - g; i++) {
//当前元素
int t = arr[i + g];
int j = i;
//将前面大于当前元素的值都往后移
while (j >= 0 && arr[j] > t) {
arr[j + g] = arr[j];
j -= g;
}
//找到位置放进来
arr[j + g] = t;
}
g /= 2;
}
return arr;
}
/**
* 归并排序
* 原理:借助外部内存,将数组拆分排序
*
* @param arr 原数组
* @return 排序之后的数组
*/
public static int[] mergeSort(int[] arr) {
if (arr.length < 2) {
return arr;
}
int mid = arr.length / 2;
int[] left = Arrays.copyOfRange(arr, 0, mid);
int[] right = Arrays.copyOfRange(arr, mid, arr.length);
return merge(mergeSort(left), mergeSort(right));
}
/**
* 归并排序
*
* @param arrL 左边数组
* @param arrR 右边数组
* @return
*/
public static int[] merge(int[] arrL, int[] arrR) {
int[] arr = new int[arrL.length + arrR.length];
for (int i = 0, l = 0, r = 0; i < arr.length; i++) {
if (l >= arrL.length) {
//左边数组取完了,将右边的数据全部依次放arr里面
arr[i] = arrR[r++];
} else if (r >= arrR.length) {
//右边数组取完了,将左边的数据全部依次放arr里面
arr[i] = arrL[l++];
} else if (arrL[l] > arrR[r]) {
//左边第一个数>右边第一个数,取右边第一个数
arr[i] = arrR[r++];
} else {
//左边第一个数<=右边第一个数,取左边第一个数
arr[i] = arrL[l++];
}
}
return arr;
}
/**
* 快速排序
* 原理:小于基准的放左边,大于基准的放右边,再递归各自排序
*
* @param arr 原数组
* @return 排序之后的数组
*/
public static int[] quickSort(int[] arr) {
return quick(arr, 0, arr.length - 1);
}
public static int[] quick(int[] arr, int start, int end) {
//基准(取第一个数)
int t = arr[start];
//头
int i = start;
//尾
int j = end;
//头尾靠拢
while (j > i) {
//数字大于t就往前移指针
while (j > i && arr[j] >= t) {
j--;
}
arr[i] = arr[j];
//数字小于t就往后移指针
while (j > i && arr[i] < t) {
i++;
}
arr[j] = arr[i];
}
arr[i] = t;
//左边部分快排
if (i - 1 > start) {
quick(arr, start, i - 1);
}
//右边部分快排
if (i + 1 < end) {
quick(arr, i + 1, end);
}
return arr;
}
/**
* 堆排序
* 原理:
* 大顶堆和小顶堆,结构同完全二叉树,一层一层堆下来,叶子节点靠左边排
* 构建大顶堆,得到顶部元素是最大的,然后顶部元素和最后一个元素交换,其余元素按此步骤走完
* 公式:
* 第一个非叶子节点 i = arr.length/2-1,其余的叶子节点为 i--;
* 元素的子节点 左子节点 2i+1, 右子节点 2i+2
* 大顶堆:arr[i] >= arr[2i+1] && arr[i] >= arr[2i+2]
* 小顶堆:arr[i] <= arr[2i+1] && arr[i] <= arr[2i+2]
*
* @param arr 原数组
* @return 排序之后的数组
*/
public static int[] heapSort(int[] arr) {
int len = arr.length;
//这里arr.length > 1,是因为最后一个不用换了
while (len > 1) {
//非叶子节点
int i = len / 2 - 1;
while (i >= 0) {
//小于左子节点,则交换
if (arr[i] < arr[2 * i + 1]) {
swap(arr, i, 2 * i + 1);
}
//小于右子节点,则交换,先判断右子节点在不在len范围内
if (2 * i + 2 <= len - 1 && arr[i] < arr[2 * i + 2]) {
swap(arr, i, 2 * i + 2);
}
//上一个非叶子节点
i--;
}
//将顶部最大值和最后一个元素交换
swap(arr, 0, len - 1);
len--;
}
return arr;
}
/**
* 计数排序
* 原理:按数值大小,放到对应的外部数组下标位置,速度快,局限于确定范围内的整数排序,
*
* @param arr 原数组
* @return 排序之后的数组
*/
public static int[] countingSort(int[] arr) {
//最大数和最小数
int min = arr[0];
int max = arr[0];
for (int i = 0; i < arr.length; i++) {
if (arr[i] < min) {
min = arr[i];
}
if (arr[i] > max) {
max = arr[i];
}
}
//随机数组的范围是min-max
//计数数组大小
int[] bucket = new int[max - min + 1];
// System.out.println(Arrays.toString(bucket));
for (int i = 0; i < arr.length; i++) {
//数放桶的位置
bucket[arr[i] - min]++;
}
// System.out.println(Arrays.toString(bucket));
int i = 0, j = 0;
while (i < arr.length) {
if (bucket[j] != 0) {
arr[i] = j + min;
bucket[j]--;
i++;
} else {
j++;
}
}
return arr;
}
/**
* 桶排序
* 原理:计数排序的升级版本,计数排序是特殊的桶,每个桶大小为1,桶排序每个桶的大小不固定,数字按大小顺序放入顺序桶,然后分别对每个桶都排序,然后按顺序取出来
*
* @param arr 原数组
* @return 排序之后的数组
*/
public static int[] bucketSort(int[] arr) {
//最大数和最小数
int min = arr[0];
int max = arr[0];
for (int i = 0; i < arr.length; i++) {
if (arr[i] < min) {
min = arr[i];
}
if (arr[i] > max) {
max = arr[i];
}
}
//桶数:(max - min) / array.length的结果为数组大小的倍数(最大倍数),以倍数作为桶数
List<Integer>[] buckets = new ArrayList[(max - min) / arr.length + 1];
//初始化桶
for (int i = 0; i < buckets.length; i++) {
buckets[i] = new ArrayList();
}
// System.out.println("桶数量: " + buckets.length);
//初始化桶里面的数据
for (int i = 0; i < arr.length; i++) {
//计算每个(array[i] - min)是数组大小的多少倍,看看放入哪个桶里
buckets[(arr[i] - min) / arr.length].add(arr[i]);
}
//桶的数据-桶内排序之前
// System.out.println(Arrays.deepToString(buckets));
//每只桶各自排序
for (int i = 0; i < buckets.length; i++) {
Collections.sort(buckets[i]);
}
//桶的数据-桶内排序之后
// System.out.println(Arrays.deepToString(buckets));
//桶内数字依次取出
for (int i = 0, j = 0; i < buckets.length; i++) {
for (Integer num : buckets[i]) {
arr[j++] = num;
}
}
return arr;
}
/**
* 基数排序
* 原理:根据数字位数,低位优先排序,依赖有限的字符字典表顺序,其实应该算作字符串排序
*
* @param arr 原数组
* @return 排序之后的数组
*/
public static int[] radixSort(int[] arr) {
//最大数字位数
int max = arr[0];
for (int i = 0; i < arr.length; i++) {
if (arr[i] > max) {
max = arr[i];
}
}
//都是0的情况,直接返回arr
if (max == 0) {
return arr;
}
int maxDigit = 0;
while (max != 0) {
max /= 10;
maxDigit++;
}
//初始化桶
//桶数0-9,10个
List<Integer>[] buckets = new ArrayList[10];
//初始化桶
for (int i = 0; i < buckets.length; i++) {
buckets[i] = new ArrayList();
}
int mod = 1;
//maxDigit轮低位优先排序
for (int j = 0; j < maxDigit; j++) {
// System.out.println("第" + (j + 1) + "轮低位优先排序");
if (j >= 1) {
mod *= 10;
}
for (int i = 0; i < arr.length; i++) {
// x / mod % 10得到低位,mode=1,10,100...
//第j轮低位,放入低位对应的卡槽
buckets[arr[i] / mod % 10].add(arr[i]);
}
//桶的数据-桶内排序之后
// System.out.println(Arrays.deepToString(buckets));
for (int i = 0, k = 0; i < buckets.length; i++) {
for (Integer num : buckets[i]) {
arr[k++] = num;
}
}
//低位排序之后
// System.out.println(Arrays.toString(arr));
//桶里面的元素清空
for (int i = 0; i < buckets.length; i++) {
buckets[i].clear();
}
}
return arr;
}
public static void main(String[] args) {
//初始化数组
int n = 10;
System.out.println("随机数组...");
int[] arr = randomArr(n);
System.out.println(Arrays.toString(arr));
// System.out.println("1.冒泡排序...");
// System.out.println(Arrays.toString(bubbleSort(arr)));
// System.out.println("2.选择排序...");
// System.out.println(Arrays.toString(selectSort(arr)));
// System.out.println("3.插入排序...");
// System.out.println(Arrays.toString(insertionSort(arr)));
// System.out.println("4.希尔排序...");
// System.out.println(Arrays.toString(ShellSort(arr)));
// System.out.println("5.归并排序...");
// System.out.println(Arrays.toString(mergeSort(arr)));
// System.out.println("6.快速排序...");
// System.out.println(Arrays.toString(quickSort(arr)));
// System.out.println("7.堆排序...");
// System.out.println(Arrays.toString(heapSort(arr)));
// System.out.println("8.计数排序...");
// System.out.println(Arrays.toString(countingSort(arr)));
// System.out.println("9.桶排序...");
// System.out.println(Arrays.toString(bucketSort(arr)));
System.out.println("10.基数排序...");
System.out.println(Arrays.toString(radixSort(arr)));
}
}