排序大的分类可以分为两种:内排序和外排序。在排序过程中,全部记录存放在内存,则称为内排序,如果排序过程中需要使用外存,则称为外排序。下面的排序工具类都是属于内排序。
/*** 九种常用排序算法工具类:* 插入排序:直接插入o(N^2),二分插入O(N^2),希尔排序:依赖增量d* 选择排序:简单选择o(N^2),堆排序O(NlogN)* 交换排序:冒泡排序o(N^2),快速排序(随机化可到达O(NlogN))* 归并排序O(NlogN)* 基数排序O(d(n+r)),d为位数,r为基数** 排序算法的选择* 1.数据规模较小* (1)待排序列基本序的情况下,可以选择直接插入排序;* (2)对稳定性不作要求宜用简单选择排序,对稳定性有要求宜用插入或冒泡* 2.数据规模不是很大* (1)完全可以用内存空间,序列杂乱无序,对稳定性没有要求,快速排序,此时要付出log(N)的额外空间。* (2)序列本身可能有序,对稳定性有要求,空间允许下,宜用归并排序* 3.数据规模很大* (1)对稳定性有求,则可考虑归并排序。* (2)对稳定性没要求,宜用堆排序* 4.序列初始基本有序(正序)* 宜用直接插入,冒泡*/public class SortUtil {/***********************************插入排序:直接插入,二分插入,希尔排序**********************//*** 插入排序改进版,找到位置最后才插入* @param a* @return*/public static int[] insertSort(int[] a) {//假设第一个记录为已经待排序好的记录,那么要比较a.length-1个记录,所以外层循环是a.length-1次//这里可以直接i=1从第二位开始处理也一样for(int i=1;i<a.length;i++) {//缓存待排序的记录int temp = a[i];int j;//这个是待插入的位置-1//跟前面已排序的记录做对比,找到合适的位置,建议从后往前面比,若是比最后一位小,则向前移动一位,否则就直接找到该位置for(j=i-1;j>=0;j--) {//如果当前待排序的数比这一个排序的数大,则跳出循环,否则交换位置if(temp>a[j]) {break;}else {//把当前记录往后面移动一位a[j+1]=a[j];}}a[j+1]=temp;}return a;}/*** 二分插入排序* @param a* @return*/public static int[] divideInsertSort(int[] a) {//这里可以直接i=1从第二位开始处理也一样for(int i=1;i<a.length;i++) {//缓存待排序的记录int temp = a[i];//下面用二分法来在前面排好序的位置中找到插入的位置//找到前面已排序的起点int left = 0;//找到已排序的终点int right = i-1;//中间位置坐标int mid = 0;//如果左边小于右边,就表明数据大于1,可以用二分法while(left<=right) {//这个如果是偶数,比如4,则第二位当做中间值,若是5则第三位mid = (right+left)/2;//对比当前排序的数目和该数据的大小、if(temp>a[mid]) {//继续右边比较left = mid+1;}else {//继续左边比较right = mid-1;}}//循环结束后,left值和right值一定相同,并且比最一次比较的mid向左或者右偏移一位,所以位置就是left插入//此时必须把已排序好的从left开始往后移动//这里找到位置后,需要把该位置往后面的都右移for (int j = i-1; j >= left; j--) {a[j+1] = a[j];}if(left != i){a[left] = temp;}}return a;}/*** 希尔排序* @param a* @return*/public static int[] shellSort(int[] a) {//主要是取增量dint d = a.length;while(true) {d=d/2;//开始进行分组插入排序,对d组进行排序,到最后d=1 就是一组排序for(int x=0;x<d;x++) {//每一组分别进行直接插入排序for(int i=x+d;i<a.length;i=i+d) {//缓存待排序的记录int temp = a[i];int j;//这个是待插入的位置-1//跟前面已排序的记录做对比,找到合适的位置,建议从后往前面比,若是比最后一位小,则向前移动一位,否则就直接找到该位置for(j=i-d;j>=0;j=j-d) {//如果当前待排序的数比这一个排序的数大,则跳出循环,否则交换位置if(temp>a[j]) {break;}else {//把当前记录往后面移动一位a[j+d]=a[j];}}a[j+d]=temp;}}//数组长度可能为1,那么这个值可能为0if(d == 1||d==0){System.out.println(d);break;}}return a;}/***************************选择排序:简单选择,堆排序******************************//*** 简单选择排序* @param a* @return*/public static int[] choiceSort(int[] a) {//这里每一个数都要做比较for (int i = 0; i < a.length; i++) {//假设第一个数是最小的数int min =a[i];int n=i; //最小数的索引//从后面找出最小的数,以及最小的数的位置for(int j=i+1;j<a.length;j++) {if(a[j]<min) {//最小数的值min = a[j];//最小数的位置n=j;}}//把当前的值和最小数的位置那个值替换a[n]=a[i];a[i]=min;}return a;}/*** 堆排序* @param a* @return*/public static int[] heapSort(int[] a) {int arrayLength=a.length;//循环建堆for(int i=0;i<arrayLength-1;i++){//建堆buildMaxHeap(a,arrayLength-1-i);//交换堆顶和最后一个元素swap(a,0,arrayLength-1-i);}return a;}//对data数组从0到lastIndex建大顶堆private static void buildMaxHeap(int[] data, int lastIndex){//从lastIndex处节点(最后一个节点)的父节点开始for(int i=(lastIndex-1)/2;i>=0;i--){//k保存正在判断的节点int k=i;//如果当前k节点的子节点存在while(k*2+1<=lastIndex){//k节点的左子节点的索引int biggerIndex=2*k+1;//如果biggerIndex小于lastIndex,即biggerIndex+1代表的k节点的右子节点存在if(biggerIndex<lastIndex){//若果右子节点的值较大if(data[biggerIndex]<data[biggerIndex+1]){//biggerIndex总是记录较大子节点的索引biggerIndex++;}}//如果k节点的值小于其较大的子节点的值if(data[k]<data[biggerIndex]){//交换他们swap(data,k,biggerIndex);//将biggerIndex赋予k,开始while循环的下一次循环,重新保证k节点的值大于其左右子节点的值k=biggerIndex;}else{break;}}}}//交换private static void swap(int[] data, int i, int j) {int tmp=data[i];data[i]=data[j];data[j]=tmp;}/*****************************交换排序:冒泡排序,快速排序************************//*** 冒泡排序* @param a* @return*/public static int[] bubbleSort(int[] a) {//1、两两比较,如果前者比后者者大则交换位置//2、每遍历一圈最大的数就会冒到最后,则确定了本轮比较中的最大值放到最后不动//3、循环1、2直至遍历完所有for (int i = 0; i < a.length-1; i++) {//外循环循环n-1次for (int j = 1; j < a.length-i; j++) {//内循环每一次要比较n-i次if(a[j-1]>a[j]){int temp=a[j-1];a[j-1]=a[j];a[j]=temp;}}}return a;}/*** 快速排序* @param a* @return*/public static int[] quickSort(int[] a) {if(a.length>0) {quickSort(a,0,a.length-1);}return a;}private static void quickSort(int[] a, int low, int high) {if(low<high) {//选择基准元素int middle = getMiddle(a,low,high);quickSort(a, 0, middle-1);quickSort(a, middle+1, high);}}private static int getMiddle(int[] a, int low, int high) {//假设第一个是基准元素int temp = a[low];while(low<high) {//找到比基准元素小的位置while(low<high&&a[high]>=temp) {high--;}a[low] = a[high];//当队首元素小于等于tmp时,向前挪动low指针while(low<high && a[low]<=temp){low++;}a[high] = a[low];}a[low] = temp;return low;}/**************************归并排序***********************//*** 归并排序* @param a* @param left* @param right*/public static int[] mergeSort(int[] a) {return mergeSort(a, 0, a.length-1);}private static int[] mergeSort(int[] a, int left, int right) {if(left<right){int middle = (left+right)/2;//对左边进行递归mergeSort(a, left, middle);//对右边进行递归mergeSort(a, middle+1, right);//合并merge(a,left,middle,right);}return a;}private static void merge(int[] a, int left, int middle, int right) {int[] tmpArr = new int[a.length];int mid = middle+1; //右边的起始位置int tmp = left;int third = left;while(left<=middle && mid<=right){//从两个数组中选取较小的数放入中间数组if(a[left]<=a[mid]){tmpArr[third++] = a[left++];}else{tmpArr[third++] = a[mid++];}}//将剩余的部分放入中间数组while(left<=middle){tmpArr[third++] = a[left++];}while(mid<=right){tmpArr[third++] = a[mid++];}//将中间数组复制回原数组while(tmp<=right){a[tmp] = tmpArr[tmp++];}}/************************基数排序************************//*** 基数排序* @param array* @return*/public static int[] radixSort(int[] a) {//找到最大数,确定要排序几趟int max = 0;for (int i = 0; i < a.length; i++) {if(a[i]<0) {System.out.println("只能比较正整数:"+a[i]);return a;}if(max<a[i]){max = a[i];}}//判断位数int times = 0;while(max>0){max = max/10;times++;}//建立十个队列List<ArrayList> queue = new ArrayList<ArrayList>();for (int i = 0; i < 10; i++) {ArrayList queue1 = new ArrayList();queue.add(queue1);}//进行times次分配和收集for (int i = 0; i < times; i++) {//分配for (int j = 0; j < a.length; j++) {//每一次比较都把奇数相同的放在同一个队列中,Math.pow(a,b) a的b次方//如果i=2表示取第二位,那么就%100取余 然后/10取取除int x = a[j]%(int)Math.pow(10, i+1)/(int)Math.pow(10, i);//直接去奇数位置寻找对应的列表ArrayList queue2 = queue.get(x);queue2.add(a[j]);queue.set(x,queue2);}//收集int count = 0;for (int j = 0; j < 10; j++) {while(queue.get(j).size()>0){ArrayList<Integer> queue3 = queue.get(j);a[count] = queue3.get(0);queue3.remove(0);count++;}}}return a;}public static void main(String[] args) {System.out.println("-------------用对数器来测是算法的正确性------------");int testTims = 500000;//测试次数int maxSize = 20;//最大测试容量int maxNum = 100;//最大测试数据boolean euqals = true;for (int i = 0; i < testTims; i++) {//原始数组int[] arr1 = generateRandomArray(maxSize,maxNum);int[] arr2 = copyArray(arr1);//这两个数组除了int[] arr3 = copyArray(arr1);//这两个数组除了//数值相同内存地址完全没关系,请看copyArray()方法实现//insertSort(arr2);//divideInsertSort(arr2);//shellSort(arr2);//choiceSort(arr2);//heapSort(arr2);//bubbleSort(arr2);//quickSort(arr2);//mergeSort(arr2);//这个只能比较正整数radixSort(arr2);rightSort(arr3);//用java.util.Arrays包的排序算法排序if (!isEquals(arr2,arr3)) {//比较是否相同printArray(arr1);//再次打印,程序员自己看看有没有毛病printArray(arr2);//再次打印,程序员自己看看有没有毛病printArray(arr3);//再次打印,程序员自己看看有没有毛病euqals = false;//一旦有不一样的值就设为false;break;}}System.out.println(euqals ? "Success:恭喜你!没毛病!" : "Error:抱歉,有错误" );//没错就恭喜,有错就报错}//这是一个绝对正确但是复杂度不好的方法public static void rightSort(int[] arr) {Arrays.sort(arr);}//这是一个随机样本产生器public static int[] generateRandomArray(int maxSize, int maxNum) {int[] arr = new int[(int) ((maxSize+1) * Math.random())];for (int i = 0; i < arr.length; i++) {arr[i] = (int) (Math.random()*(maxNum+1)) - (int)(Math.random()*maxNum);}return arr;}//这个是对比算法a和b方法是否相同的方法public static boolean isEquals(int[] arr1, int[] arr2) {if (arr1.length != arr2.length) {return false;}if (arr1 != null && arr2 == null || arr1 == null && arr2 != null) {return false;}for (int i = 0; i < arr1.length; i++) {if (arr1[i] != arr2[i]) {return false;}}return true;}//复制当前数组的一个样本public static int[] copyArray(int[] arr) {if (arr == null) {return null;}int[] newArray = new int[arr.length];for (int i = 0; i < arr.length; i++) {newArray[i] = arr[i];}return newArray;}//打印数组public static void printArray(int[] arr) {if(arr == null) {return;}for (int i = 0; i < arr.length; i++) {System.out.print(arr[i]+" ");}System.out.println();}}
