1. 归并排序
1.1 基本介绍
归并排序(MERGE-SORT)是利用归并的思想实现的排序方法,该算法采用经典的分治(divide-and-conquer)策略(分治法将问题分(divide)成一些小的问题然后递归求解,而治(conquer)的阶段则将分的阶段得到的各答案"修补"在一起,即分而治之)。
1.2 设计思路
首先图片展示一下什么是分治思想
1、可以看到这种结构很像一棵完全二叉树,本文的归并排序我们采用递归去实现(也可采用迭代的方式去实现)。
2、分阶段可以理解为就是递归拆分子序列的过程。
3、治阶段则需要将两个已经有序的子序列合并成一个有序序列,比如上图中的最后一次合并,要将[4,5,7,8]和[1,2,3,6]两个已经有序的子序列,合并为最终序列[1,2,3,4,5,6,7,8]。
- 具体的操作如下图所示
定义一个temp数组来给两个有序数组排序
思路比较简单,接下来我们呈上代码实现
1.3 代码实现
public class Demo22 extends TimeTemplate {
public static void main(String[] args) {
// int arr[] = {8, 4, 5, 7, 1, 3, 6, 2};
// System.out.println(Arrays.toString(arr));
int[] arr = new int[80000];
for (int i = 0; i < 80000; i++) {
arr[i] = (int) (Math.random() * 8000000); // 生成一个[0, 8000000) 数
}
new Demo22().sendTime(arr, "归并排序");
}
@Override
public void code(int[] arr) {
// 设置一个和原长一样的临时数组,防止数组下标越界
int[] tempArr = new int[arr.length];
mergeSort(arr, 0, arr.length - 1, tempArr);
}
/**
* 归并排序(分(递归)-治)
*/
public void mergeSort(int[] arr, int left, int right, int[] tempArr) {
if (left < right) {
int mid = (left + right) / 2;
// 向左递归
mergeSort(arr, left, mid, tempArr);
// 向右递归
mergeSort(arr, mid + 1, right, tempArr);
// 治(合并)
merge(arr, left, mid, right, tempArr);
}
}
/**
* 合并的方法(治)
*
* @param arr 待排序的数组
* @param left 待排序数组的左下标
* @param mid 待排序数组的中间下标
* @param right 待排序数组的右下标
* @param tempArr 临时存放的数组
*/
public void merge(int[] arr, int left, int mid, int right, int[] tempArr) {
int temp = 0; // 用来指向临时数组的下标
int l = left; // 初始化l,用来指向待排序数组的左下标
int r = mid + 1; // 初始化r,用来指向待排序数组的右下标
// 第一步,先从无序数组左右两部分中选择数据进行比较,然后将之放入临时数组形成有序数组
while (l <= mid && r <= right) {
if (arr[l] < arr[r]) {
tempArr[temp++] = arr[l++];
} else {
tempArr[temp++] = arr[r++];
}
}
// 第二步,将无序数组左右两部分中剩下的数据放入临时数组
while (l <= mid) {
tempArr[temp++] = arr[l++];
}
while (r <= right) {
tempArr[temp++] = arr[r++];
}
//第三步,将有序数组中的数返给无序数组,注意返回给正确的区间[left,right]
temp = 0;
l = left;
while (l <= right) {
arr[l++] = tempArr[temp++];
}
}
}
2. 基数排序
2.1 基本介绍
基数排序(radix sort)属于“分配式排序”(distribution sort),又称“桶子法”(bucket sort)或bin sort,顾名思义,它是通过键值的各个位的值,将要排序的元素分配至某些“桶”中,达到排序的作用。
基数排序法是效率高的稳定性排序法,它是桶排序的扩展,是1887年赫尔曼·何乐礼发明的。它是这样实现的:将整数按位数切割成不同的数字,然后按每个位数分别比较。
2.2 设计思路
大致的思路是:将所有待比较数值统一为同样的数位长度,数位较短的数前面补零。然后,从最低位开始,依次进行一次排序。这样从最低位排序一直到最高位排序完成以后, 数列就变成一个有序序列。
具体图解如下:
2.3 代码实现
public class Demo23 extends TimeTemplate {
public static void main(String[] args) {
// int[] arr = {80, 4, 503, 728, 11, 39, 66, 288};
int[] arr = new int[80000];
for (int i = 0; i < 80000; i++) {
arr[i] = (int) (Math.random() * 8000000); // 生成一个[0, 8000000) 数
}
new Demo23().sendTime(arr, "桶排序");
}
/**
* 桶排序算法实现
*/
@Override
public void code(int[] arr) {
// 首先设置10个桶来存放数据,为了防止下标越界,每个桶的长度都必须给到arr.length
int[][] bucket = new int[10][arr.length];
// 用来存放每个桶存放了多少个数据
int[] bucketElementCount = new int[10];
// 用来记录放回arr数组的下标
int index = 0;
// 求出arr数组中最大数的位数长度
int max = arr[0];
for (int j : arr) {
if (j > max) {
max = j;
}
}
// 存放最大数位数长度
int maxLength = (max + "").length();
// 在位数长度内进行循环,保证每个数能进入到桶中
for (int i = 0, n = 1; i < maxLength; i++, n *= 10) {
// 针对每个元素对应的位数进行处理,第一次是个位,第二次是十位,第三次数百位.....
for (int value : arr) {
// 对每个数进行取模获取到对应位数,然后存放到桶中
int digit = value / n % 10;
// 放入到对应的桶中
bucket[digit][bucketElementCount[digit]++] = value;
}
// 遍历每一个桶,放回到arr数组中
for (int j = 0; j < bucket.length; j++) {
// 若桶中有数据,则放入到原来数组
if (bucketElementCount[j] != 0) {
for (int k = 0; k < bucketElementCount[j]; k++) {
// 取出元素放入
arr[index++] = bucket[j][k];
}
}
// 用完之后记得把对应的 bucketElementCount[j] 清空
bucketElementCount[j] = 0;
}
// 每一个位数排序完要把 index 清空
index = 0;
// System.out.println("第" + (i + 1) + "次排序之后的结果: " + Arrays.toString(arr));
}
}
}
上一篇 | 总目录 | 下一篇 |
---|---|---|
六、排序(中:简单插入、希尔、快排) | 数据结构篇(Java)目录 | 八、查找(上:线性、二分) |