上篇文章实现了冒泡,选择,插入,希尔,归并,上一篇:常见排序算法合集1
这一篇则实现了剩下的快速,堆排,计数,桶排,基数
话不多说,直接上代码,代码中都有注释,相信不难理解:
package day01;
import java.util.Random;
public class AlgTest {
static Random r=new Random();
static int[] array=new int[10];
public static void main(String[] args) {
for(int i=0;i<10;i++) {
array[i]=r.nextInt(100);
}
//调用各大算法来排序
printArr(array);
//快速排序
//int left=0,right=array.length-1;
//QuickSort(array,left,right);
//堆排序
//HeapSort(array);
//计数排序
//array=CountSort(array);
//桶排序
//array=BucketSort(array);
//基数排序
array=RadixSort(array);
printArr(array);
}
/*
* 循环打印,用于检验
*/
private static void printArr(int[] arr) {
int length=arr.length;
for(int i=0;i<arr.length;i++) {
System.out.print(arr[i]+" ");
}
System.out.println();
}
/*
* 快速排序:averageTime(O(nlogn) bestTime(O(nlogn)) wrostTime(O(n^2)) space(O(logn)) 不稳定 不需要外部空间
*原理:设置一个基准值,将小于基准值的元素放左边,大于基准值的元素放右边,然后有对两边的数据进行上述相同的操作!
*/
private static void QuickSort(int[] arr, int left, int right) {
if(left>right) return;
int base=arr[left],i=left,j=right;
while(i!=j) {
//首先从右向左查找
while(arr[j]>=base && i<j) {j--;}
//然后从左向右查找
while(arr[i]<=base && i<j) {i++;}
//如果i<j,则交换两个数的位置
if(i<j) {
int temp=arr[i];
arr[i]=arr[j];
arr[j]=temp;
}
}
//将基准值和中间值交换
arr[left]=arr[i];
arr[i]=base;
//基准值前后的两个区继续上述步骤
QuickSort(arr, left, i-1);
QuickSort(arr, i+1, right);
}
/*
* 堆排序 averageTime(O(nlogn) bestTime(O(nlogn)) wrostTime(O(nlogn)) space(O(1)) 不稳定 不需要外部空间
*原理:以完全二叉树的形式存储数据,同时要求父结点的值总是小于/大于子节点
*代码理解:对堆排序时,我们从堆尾开始,已经排好序的元素不用再管,将堆头元素置换到堆尾后把另外的元素进行堆调整,重复此步骤!
*/
private static void HeapSort(int[] arr) {
//构造初始堆
int length=arr.length;
for(int i=length/2-1;i>=0;i--) {
HeadAdjust(arr,length,i);
}
//对堆进行排序,让最大值放在堆尾,每进行一次都需要进行堆调整
for(int i=length-1;i>=1;i--) {
//将堆头和当前位置进行更换,因为每次调整后堆头都是未排序的最大值
int temp=arr[0];
arr[0]=arr[i];
arr[i]=temp;
//对前面的元素进行堆调整
HeadAdjust(arr, i, 0);
}
}
//进行堆调整
private static void HeadAdjust(int[] arr, int length, int i) {
//temp代表当前父节点的值,index代表temp的左儿子
int k=i,temp=arr[i],index=2*k+1;
while(index<length) {
//如果存在右儿子
if(index+1<length) {
//如果右儿子的值大于左儿子,则将操作对象转移到右儿子
if(arr[index]<arr[index+1]) {
index++;
}
}
//如果儿子(左儿子和右儿子中较大值)大于父节点,就将该儿子变为父节点
if(arr[index]>temp) {
arr[k]=arr[index];
k=index;
index=2*k+1;
}else break;
}
//将原本父节点的值赋给当前的结点
arr[k]=temp;
}
/*
* 计数排序 averageTime(O(n+k) bestTime(O(n+k)) wrostTime(O(n+k)) space(O(k)) 稳定 需要外部空间
* 原理:使用辅助数组,里面的值表示原数组中对应元素出现的次数。
* 改进:辅助数组的值表示原数组中对应元素在新数组中对应的下标。
*/
private static int[] CountSort(int[] arr) {
int length=arr.length;
//获取数组的最大值
int max=getMax(arr)+1;
//array数组中下标i的元素对应原数组中值为i的个数
int[] array=new int[max];
//用来保存新数组
int[] Arr=new int[length];
for(int i=0;i<length;i++) {
array[arr[i]]++;
}
//更改记录数组array里面的值,array的值不再对应原数组中元素的个数,而是对应在新数组中对应的位置+1
for(int i=1;i<max;i++) {
array[i]+=array[i-1];
}
//倒序储存
for(int i=length-1;i>=0;i--) {
Arr[--array[arr[i]]]=arr[i];
}
return Arr;
}
private static int getMax(int[] arr) {
int max=arr[0],length=arr.length;
for(int i=1;i<length;i++) {
if(arr[i]<0) throw new RuntimeException("数组必须都为正整数");
if(arr[i]>max) max=arr[i];
}
return max;
}
/*
* 桶排序 averageTime(O(n+k) bestTime(O(n+k)) wrostTime(O(n^2)) space(O(n+k)) 稳定 需要外部空间
* 原理:设置若干个桶,将数组中的元素放进某些桶中,对含有数据的桶进行内部排序,然后将所有的桶合并
*/
private static int[] BucketSort(int[] arr) {
//设定桶的数量为10,这里我们根据十位数来进行区别
int Bucket=10,length=arr.length;
Integer[][] Arr=new Integer[Bucket][length];
for(int i=0;i<length;i++) {
int bucket=arr[i]/Bucket;
for(int j=0;j<length;j++) {
if(null==Arr[bucket][j]) {
Arr[bucket][j]=arr[i];
break;
}
}
}
//桶排序
for(int i=0;i<Arr.length;i++) {
//使用插入排序
for(int j=1;j<Arr[i].length;j++) {
if(null == Arr[i][j]) break;
int k=j-1,temp=Arr[i][j];
for(;k>=0 && Arr[i][k]>temp;k--) {
Arr[i][k+1]=Arr[i][k];
}
Arr[i][k+1]=temp;
}
}
//合并所有的桶
for(int i=0,index=0;i<Arr.length;i++) {
for(int j=0;j<Arr[i].length;j++) {
if(null != Arr[i][j]) {
arr[index++]=Arr[i][j];
}
}
}
return arr;
}
/*
* 基数排序 averageTime(O(n*k) bestTime(O(n*k)) wrostTime(O(n*k)) space(O(n+k)) 稳定 需要外部空间
* 原理:首先获得数组中元素的最大位数,然后对数组进行位数的计数排序,从个位一直到最大位,最后将所有的桶拼接即可
*/
private static int[] RadixSort(int[] arr) {
int max=getMaxBit(arr);
int length=arr.length;
int bucket[]=new int[length];
//对每一位进行计数排序
for(int i=1;i<=max;i++) {
//count用来记录每一位上的元素个数
int count[]=new int[length];
for(int j=0;j<length;j++) {
int num=getFigure(arr[j],i,max);
count[num]++;
}
//同计数排序一样进行优化
for(int j=1;j<length;j++) {
count[j]+=count[j-1];
}
//将每个桶中的元素进行倒叙存储,原理同计数排序
for(int j=length-1;j>=0;j--) {
int num=getFigure(arr[j],i,max);
bucket[--count[num]]=arr[j];
}
//这里必须放在循环里面,因为排序的效果是迭代的,前者会影响后者!
for(int j=0;j<length;j++) {
arr[j]=bucket[j];
}
}
return arr;
}
//获取元素在某个位数上的数字
private static int getFigure(int number, int i,int max) {
//index数组形式为{1,10,100,...,10^(max-1)}
int[] index=new int[max];
index[0]=1;
int j=1;
while(max>1) {
index[j]=index[j-1]*10;
max--;j++;
}
return (number/index[i-1])%10;
}
//获得数组中最大的位数
private static int getMaxBit(int[] arr) {
int max=arr[0],length=arr.length;
for(int i=1;i<length;i++) {
if(arr[i]<0) throw new RuntimeException("数组必须都为正整数");
if(arr[i]>max) max=arr[i];
}
int index=0;
while(max>0) {
index++;
max/=10;
}
return index;
}
}
好了,十大常用算法都已经实现了,最重要的是要理解它的思想!