赞
踩
图片名词解释:
n: 数据规模
k: “桶”的个数
In-place: 占用常数内存,不占用额外内存
Out-place: 占用额外内存
稳定:如果a原本在b前面,而a=b,排序之后a仍然在b的前面;
不稳定:如果a原本在b的前面,而a=b,排序之后a可能会出现在b的后面;
时间复杂度: 一个算法执行所耗费的时间。
空间复杂度:运行完一个程序所需内存的大小。
/** * 冒泡排序: * 冒泡排序是一种简单的排序算法。它重复地走访过要排序的数列; * 一次比较两个元素,如果它们的顺序错误就把它们交换过来; * 走访数列的工作是重复的进行,直到没有再需要交换的元素,也就是说该数列已经排序完成。 * @param sortArray * @return */ public static int[] bubbleSort(int[] sortArray){ if(sortArray == null || sortArray.length<2){ return sortArray; } for(int i=0;i<sortArray.length;i++){//每一趟排序从头开始两两比较进行交换,将最大或者最小的元素放到数组末尾已经有序的序列中,需要排序的总趟数等于数组的长度 boolean is_swap = false;//标志本趟排序中是否发生了交换,若没有发生交换则说明序列已经有序,可以直接结束排序 for(int j=0;j<sortArray.length-1-i;j++){//每次从头开始进行两两比较,第i趟排序则表示数组后面的i个元素已经有序,所以j<sortArray.length-1-i if(sortArray[j]>sortArray[j+1]){//每趟排序将最大的元素放到数组末尾,也就是升序排序 int temp = sortArray[j]; sortArray[j] = sortArray[j+1]; sortArray[j+1] = temp; is_swap = true; } } if(!is_swap){//本趟排序没有发生元素交换则整个序列已经有序,直接结束 break; } } return sortArray; }
/** * 选择排序: * 选择排序是一种简单直观的排序算法。 * 首先在未排序序列中找到最小(大)元素,存放到已排序序列的起始位置; * 然后,再从剩余未排序元素中继续寻找最小(大)元素,然后放到已排序序列的末尾。 * 以此类推,直到所有元素均排序完毕。 * @param sortArray * @return */ public static int[] selectionSort(int[] sortArray){ if(sortArray == null || sortArray.length<2){ return sortArray; } for(int i=0;i<sortArray.length;i++){//每一趟排序寻找一个最小的元素放到数组开头已经有序的序列中,排序的总趟数为数组的长度 int minIndex = i;//本趟排序寻找的的最小元素的索引,默认为i,i表示本趟排序最终寻找的最小元素应该插入的位置 for(int j=i+1;j<sortArray.length;j++){//第i趟排序说明数组开头的i个元素已经有序,所以寻找最小的元素时从第i+1个元素开始 if(sortArray[j]<sortArray[minIndex]){//元素的值比当前最小元素的值小时,更新本趟排序寻找的最小元素索引 minIndex = j; } } if(minIndex!=i){//如果最小元素的索引不是i则交换索引为minIndex和i的两个元素 int temp = sortArray[i]; sortArray[i] = sortArray[minIndex]; sortArray[minIndex] = temp; } } return sortArray; }
/** * 插入排序: * 插入排序是一种简单直观的排序算法。 * 它是通过构建有序序列,对于未排序数据,在已排序序列中从后向前扫描,找到相应位置并插入, * 在从后向前扫描过程中,需要反复把已排序元素逐步向后挪位,为最新元素提供插入空间。 * @param sortArray * @return */ public static int[] insertionSort(int[] sortArray){ if(sortArray == null || sortArray.length<2){ return sortArray; } for(int i=0;i<sortArray.length-1;i++){ //假设数组第一个元素是已排序序列,其余元素是未排序序列,所以需要扫描(或者说排序)的总次数是未排序序列的元素个数,即:sortArray.length-1。因此i应该小于sortArray.length-1 //i表示已排序序列的最后一个元素的索引,每次需要从i开始向前扫描 int index = i;//在已经排序的序列中扫描的索引,初始为已排序序列的最后一个元素的索引 int temp = sortArray[index+1];//未排序序列的第一个元素索引,随着排序的进行,未排序序列中的元素将会逐渐插入到已排序序列中,所以未排序序列会越来越短,即:未排序序列的第一个元素索引将会逐渐增大直到数组末尾 while(index>=0 && temp<sortArray[index]){//在已排序序列中从最后一个元素开始向前扫描,需要反复把已排序元素逐步向后挪位,直到找到最新元素应该插入的位置,然后结束 sortArray[index+1] = sortArray[index]; index--; } sortArray[index+1] = temp;//程序执行到这里已经找到了最新元素的插入位置:index+1,将最新元素temp插入该位置 } return sortArray; }
/** * 快速排序: * 通过一趟排序将待排记录按照一个基准值分隔成独立的两部分,其中一部分记录的值均比另一部分的值小 * 对这两部分记录继续进行快速排序,以达到整个序列有序。 * @param sortArray * @param start * @param end * @return */ public static int[] quickSort(int[] sortArray,int start,int end){ if(sortArray == null || sortArray.length<2){ return sortArray; } int index = partition(sortArray, start, end); if(index>start){ quickSort(sortArray, start, index-1); } if(index<end){ quickSort(sortArray, index+1, end); } return sortArray; } /** * 对待排序数组的指定范围按照基准值分隔成两个部分,其中一部分的值均比另一部分的值小,并且返回分隔基准值的索引位置 * @param sortArray * @param start 开始范围 * @param end 结束范围 * @return */ public static int partition(int[] sortArray,int start,int end){ int benchmark_index = start+(int)((end-start+1)*Math.random());//选取的基准值索引,随机选取一个 int index = start;//分隔成的两个部分中左边部分的索引 swapArrayElement(sortArray, benchmark_index, end);//将基准值放到最后一个 for(int i=start;i<=end;i++){//循环遍历进行分隔,只需要将所有比基准值小的元素放到基准值的左边部分,那么右边部分就是比基准值大的所有元素 if(sortArray[i]<=sortArray[end]){//sortArray[end]就是基准值 //当前元素比基准值小,则需要将当前元素放到左边部分, //当前元素等于基准值则表示已经分隔完毕,此时index指向的位置应该是基准值所在的位置,此时需要将基准值放到此位置,通过下面的交换来完成 if(i>index){//当前元素比基准值小并且当前元素的索引值比左边部分的索引大,则需要交换元素到左边部分,左边部分的范围应该是start——>index //交换数组中两个索引对应的元素值 swapArrayElement(sortArray, index, i); } if(i<end){//增加左边部分的索引,若i=end则表示index当前是基准值所在的位置,不需要再自增 index++; } } } return index; } /** * 交换数组中两个元素的值 * @param array * @param i * @param j */ public static void swapArrayElement(int[] array,int i,int j){ int temp = array[i]; array[i] = array[j]; array[j] = temp; }
/** * 希尔排序: * 希尔排序也是一种插入排序,它是简单插入排序经过改进之后的一个更高效的版本,也称为缩小增量排序。 * 希尔排序是把记录按一定增量分组(例如:增量为5,那么索引为0,5,10,15....的这些元素为一组),对每组使用直接插入排序算法排序; * 随着增量逐渐减少,每组包含的元素越来越多,当增量减至1时,所有元素被分成一组,算法便终止。 * @param sortArray * @return */ public static int[] heerSort(int[] sortArray){ if(sortArray == null || sortArray.length<2){ return sortArray; } int increment = sortArray.length/2;//增量 while(increment>0){ for(int i=increment;i<sortArray.length;i++){//这里和直接插入排序区别在于:每次向前扫描和向后挪位的间隔元素由1个变为增量increment个 int index = i-increment; int temp = sortArray[i]; while(index>=0 && temp<sortArray[index]){ sortArray[index+increment] = sortArray[index]; index -= increment; } sortArray[index+increment] = temp; } increment /= 2;//增量每次缩小一半 } return sortArray; }
/** * 计数排序: * 计数排序使用一个额外的数组C对应存放待排序数组中每个元素的频数(出现的次数)。 * 然后根据数组C来将待排序数组的元素排到正确的位置,它只能对整数进行排序。 * @param sortArray * @return */ public static int[] countingSort(int[] sortArray){ if(sortArray == null || sortArray.length<2){ return sortArray; } int min = sortArray[0];//待排数组最小值 int max = sortArray[0];//待排数组最大值 for(int i=0;i<sortArray.length;i++){//查找最小值和最大值 if(sortArray[i]<min){ min = sortArray[i]; } if(sortArray[i]>max){ max = sortArray[i]; } } int[] rateArray = new int[max-min+1];//创建频数数组,大小为待排数组中数据出现的所有可能情况,也即是:max-min+1 Arrays.fill(rateArray,0);//全部填充为0 for(int i=0;i<sortArray.length;i++){//遍历待排数组,统计每个数字出现的次数,每个元素在频数数组中的相对位置为:元素值-最小值 rateArray[sortArray[i]-min]++; } int index = 0; int index_rate = 0; while(index_rate<rateArray.length){//依次取出所有已经排序的元素 if(rateArray[index_rate]!=0){ sortArray[index++] = min+index_rate;//通过相对位置恢复元素的本来值 rateArray[index_rate]--; }else{ index_rate++; } } return sortArray; }
/** * 归并排序: * 将已有序的子序列合并,得到完全有序的序列;即先使每个子序列有序,再使子序列段间有序; * 对于子序列递归采用归并排序,直到子序列元素个数为1 * @param sortArray * @return */ public static int[] mergeSort(int[] sortArray){ if(sortArray == null || sortArray.length<2){ return sortArray; } //将序列拆分为两个子序列 int mid = sortArray.length/2; int[] left = Arrays.copyOfRange(sortArray, 0, mid); int[] right = Arrays.copyOfRange(sortArray, mid, sortArray.length); //合并两个两个子序列,对于两个子序列递归使用归并排序 return merge(mergeSort(left), mergeSort(right)); } /** * 将两个归并排序拆分的两个子序列合并为一个序列 * @param left * @param right * @return */ public static int[] merge(int[] left,int[] right){ int[] result = new int[left.length+right.length];//新建一个数组,大小为两个待合并的数组的长度之和,存放合并结果 for(int index=0,index_left=0,index_right=0;index<result.length;index++){//开始合并 if(index_left>=left.length){//左边的序列已经全部合并到了结果中,则直接将右边的序列合并到结果中 result[index] = right[index_right++]; }else if(index_right>=right.length){//右边的序列已经全部合并到了结果中,则直接将左边的序列合并到结果中 result[index] = left[index_left++]; }else if(left[index_left]<right[index_right]){//判断左边序列和右边序列当前要合并的元素的大小,保证合并的后的结果依然有序 result[index] = left[index_left++]; }else{ result[index] = right[index_right++]; } } return result; }
/** * 基数排序: * 基数排序是分别对每一位(个,十,百...)进行排序, * 每次排序都在前一位已经排序的基础上进行,首先从最低位(个位)开始排序。 * @param sortArray * @return */ public static int[] radixSort(int[] sortArray){ if(sortArray == null || sortArray.length<2){ return sortArray; } int max = sortArray[0]; for(int i=0;i<sortArray.length;i++){//取得待排数组中的最大值 if(sortArray[i]>max){ max = sortArray[i]; } } int maxDigit = 0;//最大值的位数 while(max!=0){//获取最大值的位数 max = max/10; maxDigit++; } List<List<Integer>> bucket = new ArrayList<>(); for(int i=0;i<10;i++){//每一位只可能是0-9,所以只需要建立10个桶 bucket.add(new ArrayList<Integer>()); } int mod = 10; int div = 1; for(int i=0;i<maxDigit;i++,mod*=10,div*=10){//从个位开始针对每一位的大小进行排序,直到最高位 for(int j=0;j<sortArray.length;j++){ int bucketIndex = (sortArray[j]%mod)/div;//获取数据映射的桶的索引,桶的索引就是数据在当前排序的位的值 bucket.get(bucketIndex).add(sortArray[j]); } int index = 0; for(int m=0;m<bucket.size();m++){//收集本次的排序结果并清空桶,方便下一次的排序 for(int n=0;n<bucket.get(m).size();n++){ sortArray[index++] = bucket.get(m).get(n); } bucket.get(m).clear(); } } return sortArray; }
/** * 堆排序: * 利用堆这种数据结构所设计的一种排序算法。堆是一个近似完全二叉树的结构,并同时满足小(大)根堆的性质:即父结点的键值或索引总是小于(或者大于)等于它的子节点。 * 将所有待排序的元素序列构造大根堆,然后将根节点(此时根节点最大)与序列最后一个元素交换位置,此时最后一个元素最大,然后将最后一个元素剔除,也就是放到有序序列中。 * 针对剩下的元素不断重复上述步骤,直到剩下的元素为1 * @param sortArray * @return */ public static int[] heapSort(int[] sortArray){ if(sortArray == null || sortArray.length<2){ return sortArray; } for(int i=sortArray.length/2-1;i>=0;i--){//从最后一个非叶子节点开始向上构造大根堆,最后一个非叶子节点的索引为sortArray.length/2-1 adjustHeap(sortArray, i, sortArray.length); } for(int j=sortArray.length-1;j>0;j--){//经过前面的构造,此时根节点已经是最大的元素 swapArrayElement(sortArray, 0, j);//将根节点和最后一个节点交换位置 adjustHeap(sortArray, 0, j);//对剩下的元素继续构造大根堆 } return sortArray; } /** * 调整该节点使之符合大根堆(该节点的值比左右孩子都大) * @param array * @param adjustNodeIndex 待调整的节点索引 * @param length 调整的范围 */ public static void adjustHeap(int[] array,int adjustNodeIndex,int length){ int maxIndex = adjustNodeIndex;//最大元素的索引,默认为当前调整的节点索引 int leftIndex = 2*adjustNodeIndex+1;//当前节点的左孩子索引 int rightIndex = 2*adjustNodeIndex+2;//当前节点的右孩子索引 if(leftIndex<length && array[leftIndex]>array[maxIndex]){//若左孩子的值比最大元素大,则更新最大元素的索引 maxIndex = leftIndex; } if(rightIndex<length && array[rightIndex]>array[maxIndex]){//若右孩子的值比最大元素大,则更新最大元素的索引 maxIndex = rightIndex; } if(maxIndex!=adjustNodeIndex){//如果父节点不是最大值,调整父节点为最大值并且调整与父节点交换的节点符合大根堆 swapArrayElement(array, maxIndex, adjustNodeIndex); adjustHeap(array, maxIndex, length); } } /** * 交换数组中两个元素的值 * @param array * @param i * @param j */ public static void swapArrayElement(int[] array,int i,int j){ int temp = array[i]; array[i] = array[j]; array[j] = temp; }
/** * 桶排序: * 桶排序是计数排序的升级版。它利用了函数的映射关系,高效与否的关键就在于这个映射函数的确定。 * 桶排序 假设输入数据服从均匀分布,将数据分到有限数量的桶里,每个桶再分别使用别的排序算法排序。 * @param sortArray * @param bucketSize * @return */ public static int[] bucketSort(int[] sortArray,int bucketSize){ if(sortArray == null || sortArray.length<2){ return sortArray; } int min = sortArray[0]; int max = sortArray[0]; for(int i=0;i<sortArray.length;i++){ if(sortArray[i]<min){ min = sortArray[i]; } if(sortArray[i]>max){ max = sortArray[i]; } } int bucketCount = (max-min)/bucketSize+1;//桶的个数 List<List<Integer>> bucket = new ArrayList<>();//每个桶是一个List for(int i=0;i<bucketCount;i++){//初始化桶 bucket.add(new ArrayList<Integer>()); } for(int i=0;i<sortArray.length;i++){//将数据放入每一个桶中 List<Integer> temp_bucket = bucket.get((sortArray[i]-min)/bucketSize); temp_bucket.add(sortArray[i]); } int index = 0; for(int i=0;i<bucketCount;i++){//遍历所有桶分别进行排序,然后将数据拼接起来 List<Integer> temp_bucket = bucket.get(i); if(temp_bucket.size()>0){ int[] temp_bucket_array = new int[temp_bucket.size()]; for(int j=0;j<temp_bucket_array.length;j++){ temp_bucket_array[j] = temp_bucket.get(j); } quickSort(temp_bucket_array, 0, temp_bucket_array.length-1);//每个桶采用快速排序 for(int k=0;k<temp_bucket_array.length;k++){ sortArray[index] = temp_bucket_array[k]; index++; } } } return sortArray; }
package cn.test.sort; import java.util.ArrayList; import java.util.Arrays; import java.util.List; public class SortUtils { /** * 冒泡排序: * 冒泡排序是一种简单的排序算法。它重复地走访过要排序的数列; * 一次比较两个元素,如果它们的顺序错误就把它们交换过来; * 走访数列的工作是重复的进行,直到没有再需要交换的元素,也就是说该数列已经排序完成。 * @param sortArray * @return */ public static int[] bubbleSort(int[] sortArray){ if(sortArray == null || sortArray.length<2){ return sortArray; } for(int i=0;i<sortArray.length;i++){//每一趟排序从头开始两两比较进行交换,将最大或者最小的元素放到数组末尾已经有序的序列中,需要排序的总趟数等于数组的长度 boolean is_swap = false;//标志本趟排序中是否发生了交换,若没有发生交换则说明序列已经有序,可以直接结束排序 for(int j=0;j<sortArray.length-1-i;j++){//每次从头开始进行两两比较,第i趟排序则表示数组后面的i个元素已经有序,所以j<sortArray.length-1-i if(sortArray[j]>sortArray[j+1]){//每趟排序将最大的元素放到数组末尾,也就是升序排序 int temp = sortArray[j]; sortArray[j] = sortArray[j+1]; sortArray[j+1] = temp; is_swap = true; } } if(!is_swap){//本趟排序没有发生元素交换则整个序列已经有序,直接结束 break; } } return sortArray; } /** * 选择排序: * 选择排序是一种简单直观的排序算法。 * 首先在未排序序列中找到最小(大)元素,存放到已排序序列的起始位置; * 然后,再从剩余未排序元素中继续寻找最小(大)元素,然后放到已排序序列的末尾。 * 以此类推,直到所有元素均排序完毕。 * @param sortArray * @return */ public static int[] selectionSort(int[] sortArray){ if(sortArray == null || sortArray.length<2){ return sortArray; } for(int i=0;i<sortArray.length;i++){//每一趟排序寻找一个最小的元素放到数组开头已经有序的序列中,排序的总趟数为数组的长度 int minIndex = i;//本趟排序寻找的的最小元素的索引,默认为i,i表示本趟排序最终寻找的最小元素应该插入的位置 for(int j=i+1;j<sortArray.length;j++){//第i趟排序说明数组开头的i个元素已经有序,所以寻找最小的元素时从第i+1个元素开始 if(sortArray[j]<sortArray[minIndex]){//元素的值比当前最小元素的值小时,更新本趟排序寻找的最小元素索引 minIndex = j; } } if(minIndex!=i){//如果最小元素的索引不是i则交换索引为minIndex和i的两个元素 int temp = sortArray[i]; sortArray[i] = sortArray[minIndex]; sortArray[minIndex] = temp; } } return sortArray; } /** * 插入排序: * 插入排序是一种简单直观的排序算法。 * 它是通过构建有序序列,对于未排序数据,在已排序序列中从后向前扫描,找到相应位置并插入, * 在从后向前扫描过程中,需要反复把已排序元素逐步向后挪位,为最新元素提供插入空间。 * @param sortArray * @return */ public static int[] insertionSort(int[] sortArray){ if(sortArray == null || sortArray.length<2){ return sortArray; } for(int i=0;i<sortArray.length-1;i++){ //假设数组第一个元素是已排序序列,其余元素是未排序序列,所以需要扫描(或者说排序)的总次数是未排序序列的元素个数,即:sortArray.length-1。因此i应该小于sortArray.length-1 //i表示已排序序列的最后一个元素的索引,每次需要从i开始向前扫描 int index = i;//在已经排序的序列中扫描的索引,初始为已排序序列的最后一个元素的索引 int temp = sortArray[index+1];//未排序序列的第一个元素索引,随着排序的进行,未排序序列中的元素将会逐渐插入到已排序序列中,所以未排序序列会越来越短,即:未排序序列的第一个元素索引将会逐渐增大直到数组末尾 while(index>=0 && temp<sortArray[index]){//在已排序序列中从最后一个元素开始向前扫描,需要反复把已排序元素逐步向后挪位,直到找到最新元素应该插入的位置,然后结束 sortArray[index+1] = sortArray[index]; index--; } sortArray[index+1] = temp;//程序执行到这里已经找到了最新元素的插入位置:index+1,将最新元素temp插入该位置 } return sortArray; } /** * 希尔排序: * 希尔排序也是一种插入排序,它是简单插入排序经过改进之后的一个更高效的版本,也称为缩小增量排序。 * 希尔排序是把记录按一定增量分组(例如:增量为5,那么索引为0,5,10,15....的这些元素为一组),对每组使用直接插入排序算法排序; * 随着增量逐渐减少,每组包含的元素越来越多,当增量减至1时,所有元素被分成一组,算法便终止。 * @param sortArray * @return */ public static int[] heerSort(int[] sortArray){ if(sortArray == null || sortArray.length<2){ return sortArray; } int increment = sortArray.length/2;//增量 while(increment>0){ for(int i=increment;i<sortArray.length;i++){//这里和直接插入排序区别在于:每次向前扫描和向后挪位的间隔元素由1个变为增量increment个 int index = i-increment; int temp = sortArray[i]; while(index>=0 && temp<sortArray[index]){ sortArray[index+increment] = sortArray[index]; index -= increment; } sortArray[index+increment] = temp; } increment /= 2;//增量每次缩小一半 } return sortArray; } /** * 归并排序: * 将已有序的子序列合并,得到完全有序的序列;即先使每个子序列有序,再使子序列段间有序; * 对于子序列递归采用归并排序,直到子序列元素个数为1 * @param sortArray * @return */ public static int[] mergeSort(int[] sortArray){ if(sortArray == null || sortArray.length<2){ return sortArray; } //将序列拆分为两个子序列 int mid = sortArray.length/2; int[] left = Arrays.copyOfRange(sortArray, 0, mid); int[] right = Arrays.copyOfRange(sortArray, mid, sortArray.length); //合并两个两个子序列,对于两个子序列递归使用归并排序 return merge(mergeSort(left), mergeSort(right)); } /** * 将两个归并排序拆分的两个子序列合并为一个序列 * @param left * @param right * @return */ public static int[] merge(int[] left,int[] right){ int[] result = new int[left.length+right.length];//新建一个数组,大小为两个待合并的数组的长度之和,存放合并结果 for(int index=0,index_left=0,index_right=0;index<result.length;index++){//开始合并 if(index_left>=left.length){//左边的序列已经全部合并到了结果中,则直接将右边的序列合并到结果中 result[index] = right[index_right++]; }else if(index_right>=right.length){//右边的序列已经全部合并到了结果中,则直接将左边的序列合并到结果中 result[index] = left[index_left++]; }else if(left[index_left]<right[index_right]){//判断左边序列和右边序列当前要合并的元素的大小,保证合并的后的结果依然有序 result[index] = left[index_left++]; }else{ result[index] = right[index_right++]; } } return result; } /** * 快速排序: * 通过一趟排序将待排记录按照一个基准值分隔成独立的两部分,其中一部分记录的值均比另一部分的值小 * 对这两部分记录继续进行快速排序,以达到整个序列有序。 * @param sortArray * @param start * @param end * @return */ public static int[] quickSort(int[] sortArray,int start,int end){ if(sortArray == null || sortArray.length<2){ return sortArray; } int index = partition(sortArray, start, end); if(index>start){ quickSort(sortArray, start, index-1); } if(index<end){ quickSort(sortArray, index+1, end); } return sortArray; } /** * 对待排序数组的指定范围按照基准值分隔成两个部分,其中一部分的值均比另一部分的值小,并且返回分隔基准值的索引位置 * @param sortArray * @param start 开始范围 * @param end 结束范围 * @return */ public static int partition(int[] sortArray,int start,int end){ int benchmark_index = start+(int)((end-start+1)*Math.random());//选取的基准值索引,随机选取一个 int index = start;//分隔成的两个部分中左边部分的索引 swapArrayElement(sortArray, benchmark_index, end);//将基准值放到最后一个 for(int i=start;i<=end;i++){//循环遍历进行分隔,只需要将所有比基准值小的元素放到基准值的左边部分,那么右边部分就是比基准值大的所有元素 if(sortArray[i]<=sortArray[end]){//sortArray[end]就是基准值 //当前元素比基准值小,则需要将当前元素放到左边部分, //当前元素等于基准值则表示已经分隔完毕,此时index指向的位置应该是基准值所在的位置,此时需要将基准值放到此位置,通过下面的交换来完成 if(i>index){//当前元素比基准值小并且当前元素的索引值比左边部分的索引大,则需要交换元素到左边部分,左边部分的范围应该是start——>index //交换数组中两个索引对应的元素值 swapArrayElement(sortArray, index, i); } if(i<end){//增加左边部分的索引,若i=end则表示index当前是基准值所在的位置,不需要再自增 index++; } } } return index; } /** * 堆排序: * 利用堆这种数据结构所设计的一种排序算法。堆是一个近似完全二叉树的结构,并同时满足小(大)根堆的性质:即父结点的键值或索引总是小于(或者大于)等于它的子节点。 * 将所有待排序的元素序列构造大根堆,然后将根节点(此时根节点最大)与序列最后一个元素交换位置,此时最后一个元素最大,然后将最后一个元素剔除,也就是放到有序序列中。 * 针对剩下的元素不断重复上述步骤,直到剩下的元素为1 * @param sortArray * @return */ public static int[] heapSort(int[] sortArray){ if(sortArray == null || sortArray.length<2){ return sortArray; } for(int i=sortArray.length/2-1;i>=0;i--){//从最后一个非叶子节点开始向上构造大根堆,最后一个非叶子节点的索引为sortArray.length/2-1 adjustHeap(sortArray, i, sortArray.length); } for(int j=sortArray.length-1;j>0;j--){//经过前面的构造,此时根节点已经是最大的元素 swapArrayElement(sortArray, 0, j);//将根节点和最后一个节点交换位置 adjustHeap(sortArray, 0, j);//对剩下的元素继续构造大根堆 } return sortArray; } /** * 调整该节点使之符合大根堆(该节点的值比左右孩子都大) * @param array * @param adjustNodeIndex 待调整的节点索引 * @param length 调整的范围 */ public static void adjustHeap(int[] array,int adjustNodeIndex,int length){ int maxIndex = adjustNodeIndex;//最大元素的索引,默认为当前调整的节点索引 int leftIndex = 2*adjustNodeIndex+1;//当前节点的左孩子索引 int rightIndex = 2*adjustNodeIndex+2;//当前节点的右孩子索引 if(leftIndex<length && array[leftIndex]>array[maxIndex]){//若左孩子的值比最大元素大,则更新最大元素的索引 maxIndex = leftIndex; } if(rightIndex<length && array[rightIndex]>array[maxIndex]){//若右孩子的值比最大元素大,则更新最大元素的索引 maxIndex = rightIndex; } if(maxIndex!=adjustNodeIndex){//如果父节点不是最大值,调整父节点为最大值并且调整与父节点交换的节点符合大根堆 swapArrayElement(array, maxIndex, adjustNodeIndex); adjustHeap(array, maxIndex, length); } } /** * 计数排序: * 计数排序使用一个额外的数组C对应存放待排序数组中每个元素的频数(出现的次数)。 * 然后根据数组C来将待排序数组的元素排到正确的位置,它只能对整数进行排序。 * @param sortArray * @return */ public static int[] countingSort(int[] sortArray){ if(sortArray == null || sortArray.length<2){ return sortArray; } int min = sortArray[0];//待排数组最小值 int max = sortArray[0];//待排数组最大值 for(int i=0;i<sortArray.length;i++){//查找最小值和最大值 if(sortArray[i]<min){ min = sortArray[i]; } if(sortArray[i]>max){ max = sortArray[i]; } } int[] rateArray = new int[max-min+1];//创建频数数组,大小为待排数组中数据出现的所有可能情况,也即是:max-min+1 Arrays.fill(rateArray,0);//全部填充为0 for(int i=0;i<sortArray.length;i++){//遍历待排数组,统计每个数字出现的次数,每个元素在频数数组中的相对位置为:元素值-最小值 rateArray[sortArray[i]-min]++; } int index = 0; int index_rate = 0; while(index_rate<rateArray.length){//依次取出所有已经排序的元素 if(rateArray[index_rate]!=0){ sortArray[index++] = min+index_rate;//通过相对位置恢复元素的本来值 rateArray[index_rate]--; }else{ index_rate++; } } return sortArray; } /** * 桶排序: * 桶排序是计数排序的升级版。它利用了函数的映射关系,高效与否的关键就在于这个映射函数的确定。 * 桶排序 假设输入数据服从均匀分布,将数据分到有限数量的桶里,每个桶再分别使用别的排序算法排序。 * @param sortArray * @param bucketSize * @return */ public static int[] bucketSort(int[] sortArray,int bucketSize){ if(sortArray == null || sortArray.length<2){ return sortArray; } int min = sortArray[0]; int max = sortArray[0]; for(int i=0;i<sortArray.length;i++){ if(sortArray[i]<min){ min = sortArray[i]; } if(sortArray[i]>max){ max = sortArray[i]; } } int bucketCount = (max-min)/bucketSize+1;//桶的个数 List<List<Integer>> bucket = new ArrayList<>();//每个桶是一个List for(int i=0;i<bucketCount;i++){//初始化桶 bucket.add(new ArrayList<Integer>()); } for(int i=0;i<sortArray.length;i++){//将数据放入每一个桶中 List<Integer> temp_bucket = bucket.get((sortArray[i]-min)/bucketSize); temp_bucket.add(sortArray[i]); } int index = 0; for(int i=0;i<bucketCount;i++){//遍历所有桶分别进行排序,然后将数据拼接起来 List<Integer> temp_bucket = bucket.get(i); if(temp_bucket.size()>0){ int[] temp_bucket_array = new int[temp_bucket.size()]; for(int j=0;j<temp_bucket_array.length;j++){ temp_bucket_array[j] = temp_bucket.get(j); } quickSort(temp_bucket_array, 0, temp_bucket_array.length-1);//每个桶采用快速排序 for(int k=0;k<temp_bucket_array.length;k++){ sortArray[index] = temp_bucket_array[k]; index++; } } } return sortArray; } /** * 基数排序: * 基数排序是分别对每一位(个,十,百...)进行排序, * 每次排序都在前一位已经排序的基础上进行,首先从最低位(个位)开始排序。 * @param sortArray * @return */ public static int[] radixSort(int[] sortArray){ if(sortArray == null || sortArray.length<2){ return sortArray; } int max = sortArray[0]; for(int i=0;i<sortArray.length;i++){//取得待排数组中的最大值 if(sortArray[i]>max){ max = sortArray[i]; } } int maxDigit = 0;//最大值的位数 while(max!=0){//获取最大值的位数 max = max/10; maxDigit++; } List<List<Integer>> bucket = new ArrayList<>(); for(int i=0;i<10;i++){//每一位只可能是0-9,所以只需要建立10个桶 bucket.add(new ArrayList<Integer>()); } int mod = 10; int div = 1; for(int i=0;i<maxDigit;i++,mod*=10,div*=10){//从个位开始针对每一位的大小进行排序,直到最高位 for(int j=0;j<sortArray.length;j++){ int bucketIndex = (sortArray[j]%mod)/div;//获取数据映射的桶的索引,桶的索引就是数据在当前排序的位的值 bucket.get(bucketIndex).add(sortArray[j]); } int index = 0; for(int m=0;m<bucket.size();m++){//收集本次的排序结果并清空桶,方便下一次的排序 for(int n=0;n<bucket.get(m).size();n++){ sortArray[index++] = bucket.get(m).get(n); } bucket.get(m).clear(); } } return sortArray; } /** * 交换数组中两个元素的值 * @param array * @param i * @param j */ public static void swapArrayElement(int[] array,int i,int j){ int temp = array[i]; array[i] = array[j]; array[j] = temp; } /** * 打印数组 * @param array */ public static void PrintArray(int[] array){ StringBuilder sb = new StringBuilder("["); for(int i=0;i<array.length;i++){ sb.append(array[i]); if(i!=array.length-1){ sb.append(","); } } sb.append("]"); System.out.println(sb); } public static void main(String[] args) { int sortArray[] = new int[]{20,5,3,9,6,7,500,36,1,36,500,20}; bubbleSort(sortArray); //selectionSort(sortArray); //insertionSort(sortArray); //heerSort(sortArray); //sortArray = mergeSort(sortArray); //quickSort(sortArray, 0, sortArray.length-1); //heapSort(sortArray); //countingSort(sortArray); //bucketSort(sortArray, 50); //radixSort(sortArray); PrintArray(sortArray); } }
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。