目录
五、快速排序
- 快速排序(Quicksort)是对冒泡排序法的一种改进。
1.基本思想:
- 先从数列中取出一个数作为基准数。
- 分区过程,将比这个数大的数全放到它的右边,小于或等于它的数全放到它的左边。
- 再对左右区间重复第二步,直到各区间只有一个数。
注:图中思想与代码思想一致,但基准值不同。
2.代码实现:
public static void quickSort(int[] arr,int left,int right){
int l = left; //左下标
int r = right; //右下标
int pivot = arr[(left + right)/2]; //中轴值
int temp = 0; //用于交换的临时变量
//while循环的目的是为了让比pivot值小的数放在左边,大的放在右边(从小到大排序)
while (l < r){ //即选取左边的所有值
//目的:在pivot的左边一直找大于或者等于pivot的值,才退出
while (arr[l] < pivot){
l += 1;
}
//目的:在pivot的右边一直找小于或者等于pivot的值,才退出
while (arr[r] > pivot) {
r -= 1;
}
//找到两边的两个数之后,交换
temp = arr[l];
arr[l] = arr[r];
arr[r] = temp;
//数据分配至两边两边同时寻找结束指标:即为左右两边开始产生交叉时
if(l>=r){
break;
}
//如果交换完后发现arr[l] = pivot值,此时让r--(前移)
if(arr[l] == pivot){
r--;
}
//如果交换完后发现arr[r] = pivot值,此时让l++(后移)
if(arr[r] == pivot){
l++;
}
}
//如果 l == r(即两个都已经到达中间数据),必须 l++,r--,否则出现栈溢出(此时已经为了左递归和右递归铺垫了)
if( l == r){
l+=1;
r-=1;
}
//向左递归
if (left < r){
quickSort(arr,left,r);
}
//向右递归
if (right > l){
quickSort(arr,l,right);
}
//左右排序结束
}
3.测试时间
因为快速排序的速度太快,为了领运行时间更加明显,所以跟以往不同,这次arr中随机放入8000000个数据,得到结果:
六、归并排序
- 归并排序(MERGE-SORT)是利用归并的思想实现的排序方法,该算法采用经典的分治(divid-and-conquer)策略(分治法将问题分成一些小的问题然后递归求解,而治的阶段则将分的阶段得到的个答案“修补”再一起,即分而治之)。
1.图示
2.代码实现
//合并方法
/**
*
* @param arr:待排序的原始数组
* @param left:左边有序序列的初始索引
* @param mid:中间索引
* @param right:最后边的索引
* @param temp:中转数组
*/
public static void merge(int[] arr,int left,int mid,int right,int[] temp){
int l_l = left; //i:左边有序序列的初始索引
int r_l = mid + 1 ; //中间值+1,即右边部分的最左边的索引
int t = 0; //指向temp数组的当前索引
//1.先把左右两边(有序)按照规则填充到temp数组
// 直到左右两边有一边已经处理完毕为止
while (l_l <= mid && r_l <= right ){ //继续填充
//如果说左边有序序列当先的元素小于右边有序序列的当前元素,则将左边的当前元素填充至temp中
//然后 l和 t 均后移,(t则做好要填充的准备,l则进行比较)
if(arr[l_l] <= arr[r_l]){
temp[t] = arr[l_l];
t++;
l_l++;
}else { //反之,则将右边有序序列的当前元素填充至temp中
temp[t] = arr[r_l];
t++;
r_l++;
}
}
//2.把有剩余数据的一边的数据依次全部填充到temp中
while (l_l <= mid){ //说明左边有序序列还有剩余的元素,则需要全部填充到temp中
temp[t] = arr[l_l];
t++;
l_l++;
}
while (r_l <= right){
temp[t] = arr[r_l];
t++;
r_l++;
}
//3.将temp数组重新填充至arr
//注意:并不是每次上面的代码执行完毕之后拷贝的都是所有的元素
t = 0;
int tempLeft = left;
while (tempLeft <= right){ //第一次合并时,tempL
arr[tempLeft] = temp[t];
t++;
tempLeft++;
}
}
//分 + 和的方法
public static void mergeSort(int[] arr,int left,int right,int[] temp){
if (left<right){
int mid = (left+right) / 2; //中间索引
//向左递归进行分解
mergeSort(arr,left,mid,temp);
//向右递归分解
mergeSort(arr,mid+1,right,temp);
//每分解一次就合并一次
merge(arr,left,mid,right,temp);
}
}
public static void main(String[] args) {
int[] arr = {8,4,5,7,1,3,6,2};
int[] temp = new int[arr.length];
mergeSort(arr,0,arr.length-1,temp);
System.out.println("归并排序后:" + Arrays.toString(arr));
}
七、基数排序
1) 基数排序(radixsort) 属于“分配式排序”(distributionsort) ,又称“桶子法”(bucket sort)或binsort, 顾名思义,它是通过键值的各个位的值,将要排序的元素分配至某些“桶”中,达到排序的作用
2)基数排序法是属于稳定性的排序,基数排序法的是效率高的稳定性排序法
3)基数排序(Radix Sort)是桶排序的扩展
4)基数排序是1887年赫尔曼何乐礼发明的。它是这样实现的:将整数按位数切割成不同的数字,然后按每个位数分别比较。
1.基本思想
将所有待比较数值统一为同样的数位长度,数位较短的数前面补零。然后,从最低位开始,依次进行一次排序。这样从最低位排序一直到最高位排序完成以后,数列就变成一一个有序序列。
arr = {321,1,10,60,577,743,127}
2.代码实现
package com.xx.Sort;
import java.util.Arrays;
public class RadixSort {
public static void main(String[] args) {
int[] arr = {321, 1, 10, 60, 577, 743, 127};
radixSorx(arr);
}
public static void radixSorx(int[] arr) {
//第一轮排序(针对每个元素的各位进行排序处理)
//定义一个二维数组,表示十个桶,每个桶就是一个一维数组
//说明:为了防止在放入数据的时候,数据溢出,所以每个以为数组的桶的长度定为要排序的数组长度
//基数排序是一种空间换时间的经典算法
int[][] bucket = new int[10][arr.length];
//为了记录10个桶中实际存放了多少个数据,我们定义一个以为数组来记录各个同每次放入的数据
int[] bucketElementCounts = new int[10];
//第一轮
for (int i = 0; i < arr.length; i++) {
//取出每个元素的个位
int digitOfElement = arr[i] % 10;
//放入每个桶中
bucket[digitOfElement][bucketElementCounts[digitOfElement]] = arr[i];
bucketElementCounts[digitOfElement]++;
}
//按照桶的顺序(一维数组的下标依次取出,放入原来数组)
int index = 0;
for (int k = 0;k < bucketElementCounts.length;k++){
//如果桶中有数据,我们才放入到原数组
if(bucketElementCounts[k] != 0){
//循环取出第k桶的数据
for (int l = 0;l < bucketElementCounts[k];l++){
//取出元素放入到arr中
arr[index++] = bucket[k][l];
}
}
//第一轮结束过后必须将每个bucketElementCounts置0
bucketElementCounts[k] = 0;
}
System.out.println("第一轮对个位的排序处理:" + Arrays.toString(arr));
//第二轮处理
for (int i = 0; i < arr.length; i++) {
//取出每个元素的十位
int digitOfElement = (arr[i] / 10) % 10; //得到十位
//放入每个桶中
bucket[digitOfElement][bucketElementCounts[digitOfElement]] = arr[i];
bucketElementCounts[digitOfElement]++;
}
index = 0;
//按照桶的顺序(一维数组的下标依次取出,放入原来数组)
for (int k = 0;k < bucketElementCounts.length;k++){
//如果桶中有数据,我们才放入到原数组
if(bucketElementCounts[k] != 0){
//循环取出第k桶的数据
for (int l = 0;l < bucketElementCounts[k];l++){
//取出元素放入到arr中
arr[index++] = bucket[k][l];
}
}
bucketElementCounts[k] = 0;
}
System.out.println("第二轮对个位的排序处理:" + Arrays.toString(arr));
//第三轮
for (int i = 0; i < arr.length; i++) {
//取出每个元素的百位
int digitOfElement = (arr[i] / 100) % 10; //得到十位
//放入每个桶中
bucket[digitOfElement][bucketElementCounts[digitOfElement]] = arr[i];
bucketElementCounts[digitOfElement]++;
}
index = 0;
//按照桶的顺序(一维数组的下标依次取出,放入原来数组)
for (int k = 0;k < bucketElementCounts.length;k++){
//如果桶中有数据,我们才放入到原数组
if(bucketElementCounts[k] != 0){
//循环取出第k桶的数据
for (int l = 0;l < bucketElementCounts[k];l++){
//取出元素放入到arr中
arr[index++] = bucket[k][l];
}
}
}
System.out.println("第二轮对个位的排序处理:" + Arrays.toString(arr));
}
}
代码冗余,进行改进:
public static void radixSorx_(int[] arr) {
int[][] bucket = new int[10][arr.length];
int[] bucketElementCounts = new int[10];
int digitOfElement = 0;
//首先判断数组中最大的数是几位数,用以判断进行几轮放入桶中排序
int max = arr[0]; //假设最大的数就是arr[0]
for (int i = 0; i < arr.length; i++) {
if(max <= arr[i]){
max = arr[i];
}
}
int maxLength = (max+" ").length() - 1;
//第一轮
for (int j = 0 , n = 1; j < maxLength; j++,n *= 10) {
for (int i = 0; i < arr.length; i++) {
//取出每个元素的个位
digitOfElement = arr[i] / n % 10;
//放入每个桶中
bucket[digitOfElement][bucketElementCounts[digitOfElement]] = arr[i];
bucketElementCounts[digitOfElement]++;
}
//按照桶的顺序(一维数组的下标依次取出,放入原来数组)
int index = 0;
for (int k = 0; k < bucketElementCounts.length; k++) {
//如果桶中有数据,我们才放入到原数组
if (bucketElementCounts[k] != 0) {
//循环取出第k桶的数据
for (int l = 0; l < bucketElementCounts[k]; l++) {
//取出元素放入到arr中
arr[index++] = bucket[k][l];
}
}
//第一轮结束过后必须将每个bucketElementCounts置0
bucketElementCounts[k] = 0;
}
System.out.println("基数排序第 " + (j+1) + "轮处理:" + Arrays.toString(arr));
}
}
注:数组中含有复数时,则不适用基数排序
八、排序算法时间复杂度比较