排序算法汇总
2021/10/21 20:10:14
本文主要是介绍排序算法汇总,对大家解决编程问题具有一定的参考价值,需要的程序猿们随着小编来一起学习吧!
十大排序算法
package sort; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; public class AlgorithmOfSort { /** * 冒泡排序 * 在排序过程中对元素两两比较,越小的元素会通过交换不断的排到数组的最前面 * O(n^2) 稳定 * @param array * @return */ public int[] bubbleSort(int[] array){ if(array.length==0){ return new int[]{}; } //总共需要排序array.length-1次 for(int i=0;i<array.length;i++){ //这个排序的方法就是不断的将较小的数字不断的向前排放,第i次排序会将第i小的数字放在最前面 for(int j=array.length-1;j>i;j--){ if(array[j]<array[j-1]){ int temp = array[j-1]; array[j-1] = array[j]; array[j] = temp; } } } return array; } /** * 简单选择排序 * 在每一次遍历时,找到当前为遍历数组中的最小的值,记录其下标,然后,将这个最小的值和当前的array[i]交换, * 下一次遍历时就可以从i+1开始,0-i数组的部分就是已经排序完成的内容了 * O(n^2) 不稳定 * @param array * @return */ public int[] selectionSort(int[] array){ if(array.length==0){ return new int[]{}; } //总共需要找到array.length-1个最小值,不断的排序在最前列 for(int i=0;i<array.length;i++){ int minIndex = i; for(int j=i+1;j<array.length;j++){ if(array[j]<array[minIndex]){ minIndex = j; } } if(minIndex!=i){ int temp = array[minIndex]; array[minIndex] = array[i]; array[i] = temp; } } return array; } /** * 直接插入排序 * array[0]为最初的有序序列,不断的将后面的每一个元素加入原先的有序队列中,然后形成一个新的有序队列 * O(n^2) 稳定 * @param array * @return */ public int[] insertSort(int[] array){ if(array.length==0){ return new int[]{}; } for(int i=0;i<array.length-1;i++){ //current保存这次排序中需要往有序队列中插入的元素,所有后续移动的不用担心这个值会丢失 int current = array[i+1]; int index = i; while(index>=0&¤t<array[index]){ //不断的把有序序列中比当前current大的数字向后逐个移动 array[index+1] = array[index]; index--; } //因为在不满足while条件的时候,index会多减一次,多以这里需要+1 array[index+1] = current; } return array; } /** * 希尔排序 * 又称缩小增量排序,会优先比较距离较远的元素,会按照增量分组, * 然后对每一组使用插入排序,当增量减为1,整个文件恰好被分为一组 * 8 9 1 7 2 3 5 4 6 0 * gap = length/2 = 5; * 8 9 1 7 2 3 5 4 6 0 * 分组结果为:(1)(2)(3)(4)(5)(1)(2)(3)(4)(5) * 排序结果为: 3 5 1 6 0 8 9 4 7 2 * (1)(2)(3)(4)(5)(1)(2)(3)(4)(5) * gap = gap/2 = 2; * 3 5 1 6 0 8 9 4 7 2 * 分组结果为:(1)(2)(1)(2)(1)(2)(1)(2)(1)(2) * 排序结果为:0 2 1 4 3 5 7 6 9 8 * gap = gap/2 = 1; * 排序结果为:0 1 2 3 4 5 6 7 8 9 * O(n^1.3) 最坏O(n^2) 不稳定 * @param array * @return */ public int[] shellSort(int[] array){ if(array.length==0){ return new int[]{}; } //这是一个比较常见的增量,但是并非最优 int gap = array.length/2; while(gap>0){ for(int i = gap;i<array.length-gap;i++){ int index = i-gap; int current = array[i]; //进行直接插入排序,将普通的直接插入排序中的1变为gap即可 while(index>=0&¤t<array[index]){ array[index+gap] = array[index]; index-=gap; } array[index+gap] = current; } gap/=2; } return array; } /** * 归并排序 * 将已有的有序序列不断合并,最终得到一个完全的有序序列 * O(nlog2n) 稳定 * @param array * @return */ public int[] mergeSort(int[] array){ if(array.length==0){ return new int[]{}; } if(array.length<2){ return array; } int mid = array.length/2; //不断的将长序列分割为左右序列,然后使最小的左右序列分别有序,然后在向上合并 int[] left = Arrays.copyOfRange(array,0,mid); int[] right = Arrays.copyOfRange(array,mid,array.length); return merge(mergeSort(left),mergeSort(right)); } public int[] merge(int[] left, int[] right){ int[] res = new int[left.length+right.length]; for(int index=0, i=0, j=0;index<res.length;){ if(i>=left.length){ res[index++] = right[j++]; }else if(j>=right.length){ res[index++] = left[i++]; }else if(left[i]<right[j]){ res[index++] = left[i++]; }else{ res[index++] = right[j++]; } } return res; } /** * 快速排序 * 在待排序序列中选择一个分隔元素(关键字),将待排序序列中所有小于等于关键字的元素移动到关键字的左边, * 其余的移动到关键字的右边,然后将左右两个部分当做两个新的待排序序列,重复上述步骤 * O(nlong2n) 最坏O(n^2) 不稳定 * @param array * @return */ public void quickSort(int[] array, int low, int high){ if(array.length==0){ return; } if(low<high){ int privotpos = partition(array,low,high); quickSort(array,low,privotpos-1); quickSort(array,privotpos+1,high); } } /** * 序列划分 * 设待排序序列包含元素array[low] array[low+1] ... array[high] * 选择array[low]作为关键字,设置两个游动下标i = low ,j = high+1 * 同过i的下标找到一个大于关键字的元素,通过j的下标找到一个小于关键字的元素 * 如果i>=j 交换array[low] array[j] 否则交换array[i] array[j] * @param array * @return */ public int partition(int[] array, int low, int high){ int i = low, j = high+1; do{ do{ i++; }while(i<=high&&array[i]<array[low]); do{ j--; }while(array[j]>array[low]); if(i<j){ int temp = array[i]; array[i] = array[j]; array[j] = temp; } }while(i<j); int temp = array[j]; array[j]= array[low]; array[low] = temp; //这个j就是分隔元素的下标 return j; } /** * 堆排序 * 将一个无序的序列构建成一个堆,然后构建成大顶堆 * 将堆顶元素与末尾元素交换,将最大元素放到数组的末端 * 重新调整结构,使其满足堆的定义,然后继续交换堆顶元素和末尾元素 * 最终得到的就是一个升序的序列 * O(nlog2n) 不稳定 * @param array * @return */ public void heapSort(int[] array){ //构建大顶堆 //这里构建大堆顶是从最后一个非叶子节点开始的 for(int i = array.length/2-1;i>=0;i--){ adjustHeap(array,i,array.length); } for(int i = array.length-1;i>0;i--){ int temp = array[0]; array[0] = array[i]; array[i] = temp; adjustHeap(array,0,i); } } void adjustHeap(int[] array, int i, int length){ int temp = array[i]; //从i节点的左孩子开始,下一个就是下一层了,所以这里k*2+1 for(int k = i*2+1;k<length;k = k*2+1){ //如果i节点的左子节点小于右子结点, 那么就让k指向右子结点 if(k+1<length&&array[k]<array[k+1]){ k++; } //如果子节点的值大于父节点的值,那么就把这个较大的值赋给父节点 if(array[k]>temp){ array[i] = array[k]; i = k; }else{ //如果当前节点(也就是父节点)的值没有变化,也就是没有被他的子节点取代,又因为这个调整是从传入的节点向下处理, // 所以只有当这个节点的值发生了变化,也就是说一个更大的值被放在了更高的层次,这是才有必要往下面继续 break; } } array[i] = temp; } /** * 计数排序 * 确定数组中的最大值和最小值,然后开辟一个新的数组,大小为最大值和最小值的差值+1, * 用来记录数组中每一个数字出现的次数,这时在记录的时候,因为count数组经过空间优化, * 所以原数组中的值在作为count数组的下标时应该在减去最小值 * 但是当数组的最大值和最小值差值过大时,不建议使用计数排序,会浪费过多的空间 * O(n+k) 稳定 * @param array * @return */ public int[] countingSort(int[] array){ if(array.length==0){ return new int[]{}; } int max = array[0], min = array[0]; for(int i=1;i<array.length;i++){ max = Math.max(max,array[i]); min = Math.min(min,array[i]); } //这个数组用来保存原数组中每一个元素出现的次数 int[] count = new int[max-min+1]; for(int n:array){ //因为对count数组做出优化,优化了空间,所以这里array[i]对应的下标应该是array[i]-min,这样每一个值才能被转换为count数组的下标 count[n-min]++; } int index = 0, i = 0; while(index<array.length){ if(count[i]!=0){ //实际应该写在array(也就是排序后的数组)中的数字应该是count当前的下标+最小值 array[index++] = min+i; count[i]--; }else{ i++; } } return array; } /** * 桶排序 * 也就是计数排序的拓展版本,每个桶都表示着一个范围而非计数排序中的一个定值 * 桶排序是常见排序算法中最快的,大多数情况下比快速排序和归并排序还要快 * 桶排序非常的消耗空间,是典型的以空间换时间(可以说是最耗费内存的排序算法) * 桶排序需要尽量保证元素分散均匀,否则当所有数据集中在同一个桶中时,桶排序失效。 * O(n+k) 最坏O(n^2) 稳定 * @param array */ public void bucketSort(int[] array){ if(array.length==0){ return; } int max = array[0], min = array[0]; for(int i=1;i<array.length;i++){ max = Math.max(max,array[i]); min = Math.min(min,array[i]); } int bucketNum = (max-min)/array.length+1; ArrayList<ArrayList<Integer>> bucketArr = new ArrayList<>(bucketNum); for(int i=0;i<bucketNum;i++){ bucketArr.add(new ArrayList<Integer>()); } for(int n:array){ bucketArr.get((n-min)/array.length).add(n); } for(int i=0;i<bucketNum;i++){ Collections.sort(bucketArr.get(i)); } int index = 0; for(ArrayList<Integer> list:bucketArr){ for(int n:list){ array[index++] = n; } } } /** * 基数排序 * 基数排序就是先找到最大值,确定数组中最大值的位数,然后把所有的数字都补齐为相同的位数, * 然后先按照最低位排序,然后按照次低位,等最高位排完序之后,数组自然变得有序了 * {21,56,88,195,354,1,35,12,6,7} * 按照个位:21,1,12,354,195,35,56,6,7,88 * 按照十位:1,6,7,12,21,35,354,56,88,195 * 按照百位:1,6,7,12,21,35,56,88,195,354 * O(n*k) 稳定 * @param array */ public void radixSort(int[] array){ if(array.length==0){ return; } int max = array[0]; for(int i=1;i<array.length;i++){ max = Math.max(max,array[i]); } //从个位开始不断的排序 for(int exp = 1;max/exp>0;exp*=10){ //这里十个bucket用来记录对应数字的对应位的数字为0~9的出现的次数 int[] bucket = new int[10]; int[] temp = new int[array.length]; //记录了每个桶中的数据数量 for(int n:array){ bucket[(n/exp)%10]++; } for(int i=1;i<10;i++){ //可以由此确定放置数据的位置 bucket[i] += bucket[i-1]; } for(int i=array.length-1;i>=0;i--){ //array[i]/exp%10 获得了 当前数字按照个位十位百位排序的顺序 所对应位的数字 //bucket现在就类似一个前缀和数组一样,存放着对应位数字应该放置的位置,因为bucket之前统计的是出现的次数,所以存放的实际位置就应该是bucket[array[i]/exp%10]-1 temp[bucket[(array[i]/exp)%10]-1] = array[i]; bucket[array[i]/exp%10]--; } //将数据在放回到array中 for(int i=0;i<array.length;i++){ array[i] = temp[i]; } } } }
这篇关于排序算法汇总的文章就介绍到这儿,希望我们推荐的文章对大家有所帮助,也希望大家多多支持为之网!
- 2024-12-26大厂数据结构与算法教程:入门级详解
- 2024-12-26大厂算法与数据结构教程:新手入门指南
- 2024-12-26Python编程入门指南
- 2024-12-26数据结构高级教程:新手入门及初级提升指南
- 2024-12-26并查集入门教程:从零开始学会并查集
- 2024-12-26大厂数据结构与算法入门指南
- 2024-12-26大厂算法与数据结构入门教程
- 2024-12-26二叉树入门教程:轻松掌握基础概念与操作
- 2024-12-26初学者指南:轻松掌握链表
- 2024-12-26平衡树入门教程:轻松理解与应用