十大排序算法(Java)

深藏阁楼爱情的钟 2022-11-27 05:24 369阅读 0赞

文章目录

  • 十大排序算法(Java)
      • 一、冒泡排序(Bubble Sort)
      • 二、选择排序(Selection Sort)
      • 三、堆排序(Heap Sort)
      • 四、插入排序(Insertion Sort)
      • 五、归并排序(Merge Sort)
      • 六、快速排序(Quick Sort)
      • 七、希尔排序(Shell Sort)
      • 八、计数排序(Counting Sort)
      • 九、基数排序(Radix Sort)
      • 十、桶排序(Bucket Sort)
      • 扩展:十一、史上“最强”排序-休眠排序666(仅供参考,不要用!!)

十大排序算法(Java)

小猴子自定义口诀(瞎编的,哈哈):冒泡两个两个对比,选择最大的排在最后,插入前面有序列表,归并先分区后合并,快速将一列数划分小于pivot在左边大于pivot在右边再依次递归左右子列,希尔分列排序每列排序算法借用插入;以上是基于比较的排序算法

以下不是基于比较的排序算法,(以空间换时间),计数计算每个数出现的次数然后从小到大排序,基数非常适合非负整数个十百分别比较,桶是把每个数分到相应的桶再排序

各个排序图片归纳:

在这里插入图片描述

一、冒泡排序(Bubble Sort)

冒泡排序是一种简单的排序算法。它重复地走访过要排序的数列,一次比较两个元素,如果它们的顺序错误就把它们交换过来。走访数列的工作是重复地进行直到没有再需要交换,也就是说该数列已经排序完成。这个算法的名字由来是因为越小的元素会经由交换慢慢“浮”到数列的顶端。

  1. //原始排序(无优化)
  2. public void bubbleSort(Integer[] array){
  3. for(int end=array.length-1;end>0;end--){
  4. for(int begin=1;begin<=end;begin++){
  5. if(array[begin] < array[begin-1]){
  6. int temp = array[begin];
  7. array[begin] = array[begin-1];
  8. array[begin-1] = temp;
  9. }
  10. }
  11. }
  12. }
  13. //优化方案1(当数组完全有序或者数组在循环少量次数后就有序时)
  14. public void bubbleSort1(Integer[] array){
  15. for(int end=array.length-1;end>0;end--){
  16. boolean sorted = true;
  17. for(int begin=1;begin<=end;begin++){
  18. if(array[begin] < array[begin-1]){
  19. int temp = array[begin];
  20. array[begin] = array[begin-1];
  21. array[begin-1] = temp;
  22. sorted = false;
  23. }
  24. }
  25. if(sorted){
  26. break;
  27. }
  28. }
  29. }
  30. //优化方案2(当数组中后面部分完全有序)
  31. public void bubbleSort2(Integer[] array){
  32. for(int end=array.length-1;end>0;end--){
  33. //sortedIndex的初始值在数组完全有序的时候有用
  34. int sortedIndex = 1;
  35. for(int begin=1;begin<=end;begin++){
  36. if(array[begin] < array[begin-1]){
  37. int temp = array[begin];
  38. array[begin] = array[begin-1];
  39. array[begin-1] = temp;
  40. sortedIndex = begin;
  41. }
  42. }
  43. end = sortedIndex;
  44. }
  45. }

二、选择排序(Selection Sort)

选择排序(Selection-sort)是一种简单直观的排序算法。它的工作原理:首先在未排序序列中找到最小(大)元素,存放到排序序列的起始位置,然后,再从剩余未排序元素中继续寻找最小(大)元素,然后放到已排序序列的末尾。以此类推,直到所有元素均排序完毕。

  1. public void selectionSort(Integer[] arr){
  2. for(int end=arr.length-1;end>0;end--){
  3. int maxIndex = 0;
  4. for(int begin=1;begin<=end;begin++){
  5. if(arr[maxIndex] <= arr[begin]){ //增加其稳定性(=)
  6. maxIndex = begin;
  7. }
  8. }
  9. int temp = arr[maxIndex];
  10. arr[maxIndex] = arr[end];
  11. arr[end] = temp;
  12. }
  13. }

三、堆排序(Heap Sort)

(可以看做对选择排序的优化)

堆排序(Heap-sort)是指利用堆这种数据结构所设计的一种排序算法。堆积是一个近似完全二叉树的结构,并同时满足堆积的性质:即子结点的键值或索引总是小于(或者大于)它的父节点。

  1. //重构代码,写一个父类,其他排序都继承这个抽象类,重写sort方法
  2. public abstract class Sort{
  3. protected Integer[] arr;
  4. protected int cmpCount; //比较的次数
  5. protected int swapCount; //交换的次数
  6. private long time;
  7. public void sort(Integer[] arr){
  8. if(arr == null || arr.length < 2) return;
  9. this.arr = arr;
  10. long begin = System.currentTimeMillis();
  11. sort();
  12. time = System.currentTimeMillis() - begin;//运行时间
  13. }
  14. protected abstract void sort();
  15. //比较:等于0 arr[i] == arr[j]
  16. //小于0 arr[i] < arr[j]
  17. //大于0 arr[i] > arr[j]
  18. protected int cmp(int i,int j){
  19. cmpCount++;
  20. return arr[i] - arr[j];
  21. }
  22. //比较元素大小
  23. protected int compare(Integer v1,Integer v2){
  24. return v1 - v2;
  25. }
  26. //交换
  27. protected void swap(int i,int j){
  28. swapCount++;
  29. int temp = arr[i];
  30. arr[i] = arr[j];
  31. arr[j] = temp;
  32. }
  33. @Override
  34. public String toString(){
  35. String string = getClass().getSimpleName()+" "+"耗时:"+(time/1000.0)+"s("+time+"ms)";
  36. return string;
  37. }
  38. }
  39. //注:上面两个排序的代码可重写,这里我就不写了
  40. //交换堆顶元素与尾元素
  41. //堆的元素数量减1
  42. //对0位置进行1次shiftDown操作
  43. public class HeapSort extends Sort{
  44. private int heapSize;
  45. @Override
  46. protected void sort(){
  47. heapSize = arr.length;
  48. //原地建堆
  49. //heapSize>>1表示把heapSize右移1位,相当于heapSize/2
  50. for(int i = (heapSize >> 1)-1;i >= 0;i--){
  51. shiftDown(i);
  52. }
  53. while(heapSize > 1){
  54. //交换堆顶元素与尾元素
  55. //swap(0,heapSize-1);
  56. //heapSize--;
  57. swap(0,--heapSize);//合并以上两步
  58. //对0位置进行1次shiftDown操作(恢复堆的性质)
  59. shiftDown(0);
  60. }
  61. }
  62. private void shiftDown(int index){
  63. Integer element = arr[index];
  64. int half = heapSize >> 1;
  65. while(index < half){ //index必须是非叶子节点
  66. //默认是左边跟父节点比
  67. int childIndex = (index << 1)+1;
  68. Integer child = arr[childIndex];
  69. //右子节点比左子节点大
  70. int rightIndex = childIndex + 1;
  71. if(rightIndex < heapSize && compare(arr[rightIndex],child) > 0){
  72. child = arr[childIndex = rightIndex];
  73. }
  74. //大于等于子节点
  75. if(compare(element,child) >= 0) break;
  76. arr[index] = child;
  77. index = childIndex;
  78. }
  79. arr[index] = element;
  80. }
  81. }

四、插入排序(Insertion Sort)

类似扑克牌的排序

插入排序(Insertion-Sort)的算法描述是一种简单直观的排序算法。它的工作原理是通过构建有序序列,对于未排序数据,在已排序序列中从后向前扫描,找到相应位置并插入。

  1. public class InsertionSort extends Sort{
  2. @Override
  3. protected void sort(){
  4. for(int begin=1;begin<arr.length;begin++){
  5. int cur = begin;//要把当前begin记录下来,不能改变,所以用cur来代替begin变化
  6. while(cur > 0 && cmp(cur,cur-1) < 0){
  7. swap(cur,cur-1);
  8. cur--;
  9. }
  10. }
  11. }
  12. //优化后的代码1
  13. @Override
  14. protected void sort(){
  15. for(int begin=1;begin<arr.length;begin++){
  16. int cur = begin;//要把当前begin记录下来,不能改变,所以用cur来代替begin变化
  17. Integer v = arr[cur];
  18. while(cur > 0 && cmp(cur,cur-1) < 0){
  19. arr[cur] = arr[cur-1]; //逐个往右移一个位置
  20. cur--;
  21. }
  22. arr[cur] = v; //最后前面空出来的那个位置,v直接插入进去
  23. }
  24. }
  25. //利用二分搜索优化代码2(见下面方法search)
  26. @Override
  27. protected void sort(){
  28. for(int begin=1;begin<arr.length;begin++){
  29. int v = arr[begin];
  30. int searchIndex = search(begin);
  31. //将searchIndex到begin位置的元素往右边挪动一位
  32. //for(int i=begin-1;i>=searchIndex;i--){
  33. // arr[i+1] = arr[i];
  34. //}
  35. for(int i=begin;i>searchIndex;i--){
  36. arr[i] = arr[i-1];
  37. }
  38. arr[searchIndex] = v;
  39. }
  40. }
  41. //利用二分搜索找到index位置元素的待插入位置
  42. //已经排好序数组的区间范围值[0,index)
  43. private int search(int index){
  44. int v = arr[index];
  45. int begin = 0;
  46. int end = index-1; //左闭右开区间
  47. while(begin < end){
  48. int mid = (begin+end) >>1;
  49. if(v <arr[mid]){ //compare(v,arr[mid])<0
  50. end = mid;
  51. }else{
  52. begin = mid+1;
  53. }
  54. }
  55. return begin;
  56. }
  57. }
  58. //写一下二分搜索(可参考学习)
  59. public class BinarySearch{
  60. //查找v在有序数组array中的位置
  61. public static int indexOf(int[] array,int v){ //返回索引
  62. if(array == null ||array.length == 0){
  63. return -1;
  64. }
  65. int begin = 0;
  66. int end = array.length; //左闭右开区间
  67. while(begin < end){
  68. int mid = (begin+end)/2;
  69. if(v<array[mid]){
  70. end=mid;
  71. }else if(v>array[mid]){
  72. begin=mid+1;
  73. }else{
  74. return mid;
  75. }
  76. }
  77. return -1;
  78. }
  79. //查找v在有序数组array中的插入位置,第一个比v大的位置的索引
  80. public static int search(int[] array,int v){
  81. if(array == null ||array.length == 0){
  82. return -1;
  83. }
  84. int begin = 0;
  85. int end = array.length; //左闭右开区间
  86. while(begin < end){
  87. int mid = (begin+end) >>1;
  88. if(v <array[mid]){
  89. end = mid;
  90. }else{
  91. begin = mid+1;
  92. }
  93. }
  94. return begin;
  95. }
  96. }

五、归并排序(Merge Sort)

归并排序是建立在归并操作上的一种有效的排序算法。该算法是采用分治法(Divide and Conquer)的一个非常典型的应用。将已有序的子序列合并,得到完全有序的序列;即先使每个子序列有序,再使子序列段间有序。若将两个有序表合并成一个有序表,称为2-路归并。

  1. public class MergeSort extends Sort{
  2. private Integer[] leftArray;
  3. @Override
  4. protected void sort(){
  5. leftArray = new Integer[arr.length >> 1];
  6. sort(0,arr.length);
  7. }
  8. //对[begin,end)范围内的数据进行归并排序
  9. private void sort(int begin,int end){
  10. if(end - begin < 2) return;//只有一个元素的情况
  11. int mid = (begin+end) >> 1;
  12. sort(begin,mid); //都是左闭右开区间
  13. sort(mid,end); //都是左闭右开区间
  14. merge(begin,mid,end);
  15. }
  16. //合并
  17. //将[begin,end)与[mid,end)范围内的序列合并成一个有序的序列
  18. private void merge(int begin,int mid,int end){
  19. int li = 0,le = mid -begin;
  20. int ri = mid,re = end;
  21. int ai = begin;
  22. //备份左边数组
  23. for(int i=li;i<le;i++){
  24. leftArray[i] = arr[begin+i];
  25. }
  26. while(li<le){ //如果左边还没有结束
  27. if(ri<re && leftArray[li] > arr[ri]){
  28. arr[ai] = arr[ri];
  29. ri++;
  30. ai++;
  31. }else{
  32. arr[ai++] = leftArray[li++];
  33. }
  34. }
  35. }
  36. }

六、快速排序(Quick Sort)

快速排序的基本思想:通过一趟排序将待排记录分隔成独立的两部分,其中一部分记录的关键字均比另一部分的关键字小,则可分别对这两部分记录继续进行排序,以达到整个序列有序。

  1. /*快速排序的本质: 逐渐将每一个元素都转换成轴点元素(递归的过程)*/
  2. public class QuickSort extends Sort{
  3. @Override
  4. protected void sort(){
  5. //sort(0,arr.length);
  6. sort2(0,arr.length-1);
  7. }
  8. //对[begin,end)范围的元素进行快速排序
  9. private void sort2(int begin,int end){
  10. if(end-begin<2) return;
  11. //确定轴点位置
  12. int mid = partition(begin,end);
  13. //对子序列进行快速排序
  14. sort(begin,mid-1);
  15. sort(mid+1,end);
  16. }
  17. private int partition(int begin,int end){
  18. int privot = begin,index = privot+1;
  19. for(int i=index;i<=end;i++){
  20. if(arr[privot] > arr[i]){
  21. swap(i,index);
  22. index++;
  23. }
  24. }
  25. swap(privot,index-1);
  26. return index-1;
  27. }
  28. private void swap(int a,int b){
  29. int temp = arr[a];
  30. arr[a] = arr[b];
  31. arr[b] = temp;
  32. }
  33. //对[begin,end)范围的元素进行快速排序
  34. private void sort(int begin,int end){
  35. if(end-begin<2) return;
  36. //确定轴点位置
  37. int mid = pivotIndex(begin,end);
  38. //对子序列进行快速排序
  39. sort(begin,mid);
  40. sort(mid+1,end);
  41. }
  42. //确定[begin,end)范围的轴点位置,return begin=end的位置pivot
  43. private int pivotIndex(int begin,int end){
  44. //备份begin位置的元素
  45. Integer v = arr[begin]; //arr[pivot] = v
  46. //end指向最后一个元素
  47. end--;
  48. while(begin < end){
  49. while(begin < end){
  50. if(arr[end] > v){
  51. end--;
  52. }else{
  53. arr[begin] = arr[end];
  54. begin++;
  55. break; //只退出当前循环,掉头了(本来是从右到左查找对比,只要arr[end]<v了就掉头,从begin方向往end方向查找)
  56. }
  57. }
  58. while(begin < end){
  59. if(arr[begin] < v){
  60. begin++;
  61. }else{
  62. arr[end] = arr[begin];
  63. end--;
  64. break;
  65. }
  66. }
  67. }
  68. //将轴点元素放入最终的位置
  69. arr[begin] = v;
  70. return begin;//或者end,因为begin=end=pivot轴点元素的位置
  71. }
  72. }

七、希尔排序(Shell Sort)

1959年Shell发明,第一个突破O(n2)的排序算法,是简单插入排序的改进版。它与插入排序的不同之处在于,它会优先比较距离较远的元素。希尔排序又叫缩小增量排序

  1. public class ShellSort extends Sort{
  2. @Override
  3. protected void sort(){
  4. List<Integer> stepSequence = myStepSequence(); //步长序列{8,4,2,1}
  5. for(Integer step:stepSequence){
  6. sort(step);
  7. }
  8. }
  9. //分成step列进行排序
  10. private void sort(int step){
  11. //col:第几列
  12. //对第col列进行排序
  13. for(int col = 0;col < step;col++){
  14. //col,col+step,col+2*step,col+3*step
  15. for(int begin=col+step;begin<arr.length;begin+=step){
  16. int cur = begin;//要把当前begin记录下来,不能改变,所以用cur来代替begin变化
  17. while(cur > 0 && cmp(cur,cur-step) < 0){
  18. swap(cur,cur-step);
  19. cur-=step;
  20. }
  21. }
  22. }
  23. }
  24. private List<Integer> myStepSequence(){
  25. List<Integer> stepSequence = new ArrayList<>();
  26. int step = arr.length;
  27. while((step >> 1) > 0){
  28. stepSequence.add(step);
  29. }
  30. return stepSequence;
  31. }
  32. }

八、计数排序(Counting Sort)

计数排序不是基于比较的排序算法,其核心在于将输入的数据值转化为键存储在额外开辟的数组空间中。 作为一种线性时间复杂度的排序,计数排序要求输入的数据必须是有确定范围的整数

  1. public class CountingSort extends Sort{
  2. @Override
  3. protected void sort(){
  4. //找出最大值
  5. int max = arr[0];
  6. for(int i=1;i<arr.length;i++){
  7. if(arr[i] > max){
  8. max = arr[i];
  9. }
  10. }
  11. //开辟内存空间,存储每个整数出现的次数
  12. int[] counts = new int[1+max];
  13. //统计每个整数出现的次数
  14. for(int i=0;i<arr.length;i++){
  15. counts[arr[i]]++;
  16. }
  17. //根据整数的出现次数,对整数进行排序
  18. int index=0;
  19. for(int i=0;i<counts.length;i++){
  20. while(conuts[i]-- >0){
  21. arr[index++] = i;
  22. }
  23. }
  24. }
  25. }
  26. //优化(内存浪费,无负数,不稳定)
  27. public class CountingSort extends Sort{
  28. @Override
  29. protected void sort(){
  30. //找出最大值、最小值
  31. int max = arr[0];
  32. int min = arr[0];
  33. for(int i=1;i<arr.length;i++){
  34. if(arr[i] > max){
  35. max = arr[i];
  36. }
  37. for(int i=1;i<arr.length;i++){
  38. if(arr[i] < min){
  39. min = arr[i];
  40. }
  41. //开辟内存空间 存储次数
  42. int[] counts = new int[max-min+1];
  43. //统计每个整数出现的次数
  44. for(int i=0;i<arr.length;i++){
  45. counts[arr[i] - min]++;
  46. }
  47. //累加次数
  48. for(int i=1;i<counts.length;i++){
  49. counts[i] += counts[i-1];
  50. }
  51. //从后往前遍历元素,将其放到有序数组中的合适位置
  52. int[] newArr = new int[arr.length];
  53. for(int i=arr.length;i>0;i--){
  54. //--counts[arr[i]-min] arr[i]-min就是这个数,counts[arr[i]-min]就是次数(前面次数和)要先减一才是新数组的索引
  55. newArr[--counts[arr[i]-min]] = arr[i]; //理解
  56. }
  57. //将有序数组赋值到arr
  58. for(int i=0;i<arr.length;i++){
  59. arr[i]=newArr[i];
  60. }
  61. }
  62. }

九、基数排序(Radix Sort)

基数排序是按照低位先排序,然后收集;再按照高位排序,然后再收集;依次类推,直到最高位。有时候有些属性是有优先级顺序的,先按低优先级排序,再按高优先级排序。最后的次序就是高优先级高的在前,高优先级相同的低优先级高的在前。(非常适合非负整数)

  1. public class RadixSort extends Sort{
  2. @Override
  3. protected void sort(){
  4. //找出最大值
  5. int max = arr[0];
  6. for(int i=1;i<arr.length;i++){
  7. if(arr[i] > max){
  8. max = arr[i];
  9. }
  10. }
  11. //max=593 593%10=3 593/10%10=9 593/100%10=5
  12. for(int divider = 1;divider <= max;divider *= 10){
  13. countingSort(divider);
  14. }
  15. }
  16. //基于计数排序
  17. protected void countingSort(int divider){ //divider=1、10、100、1000、...
  18. //开辟内存空间 存储次数
  19. int[] counts = new int[10]; //0-9
  20. //统计每个整数出现的次数
  21. for(int i=0;i<arr.length;i++){
  22. counts[arr[i] / divider % 10]++;
  23. }
  24. //累加次数
  25. for(int i=1;i<counts.length;i++){
  26. counts[i] += counts[i-1];
  27. }
  28. //从后往前遍历元素,将其放到有序数组中的合适位置
  29. int[] newArr = new int[arr.length];
  30. for(int i=arr.length;i>0;i--){
  31. //--counts[arr[i]-min] arr[i]-min就是这个数,counts[arr[i]-min]就是次数(前面次数和)要先减一才是新数组的索引
  32. newArr[--counts[arr[i] / divider % 10] = arr[i]; //理解
  33. }
  34. //将有序数组赋值到arr
  35. for(int i=0;i<arr.length;i++){
  36. arr[i]=newArr[i];
  37. }
  38. }
  39. }

十、桶排序(Bucket Sort)

桶排序是计数排序的升级版。它利用了函数的映射关系,高效与否的关键就在于这个映射函数的确定。桶排序 (Bucket sort)的工作的原理:假设输入数据服从均匀分布,将数据分到有限数量的桶里,每个桶再分别排序(有可能再使用别的排序算法或是以递归方式继续使用桶排序进行排)。(当数是小数时)

  1. public class BucketSort extends Sort{
  2. @Override
  3. protected void sort(){
  4. //桶数组
  5. List<Double>[] buckets = new List[arr.length];
  6. for(int i=0;i<arr.length;i++){
  7. int bucketIndex = (int) (arr[i] * arr.length);
  8. List<Double> bucket = buckets[bucketIndex];
  9. if(bucket == null){
  10. bucket = new LinkedList<>();
  11. buckets[bucketIndex] = bucket;
  12. }
  13. bucket.add(arr[i]);
  14. }
  15. //对每个桶进行排序
  16. int index = 0;
  17. for(int i=0;i<buckets;i++){
  18. if(buckets[i] == null) continue;
  19. buckets[i].sort(null);//java内部的排序方法
  20. for(Double d:buckets[i]){
  21. arr[index++] = d;
  22. }
  23. }
  24. }
  25. }

扩展:十一、史上“最强”排序-休眠排序666(仅供参考,不要用!!)

  1. private static class SortThread extends Thread{
  2. private int value;
  3. public SortThread(int value){
  4. this.value = value;
  5. }
  6. public void run(){
  7. try{
  8. Thread.sleep(value);
  9. System.out.println(value);
  10. }catch(IntereuptedException e){
  11. e.printStackTrace();
  12. }
  13. }
  14. }
  15. public class SortThreadTest{
  16. public static void main(String[] args){
  17. int[] array = [10,100,50,60,30];
  18. for(int i=0;i<array.length;i++){
  19. new SortThread(array[i]).start();
  20. }
  21. }
  22. }

主要是理解某个算法本身的意义和思想,再根据代码联系思考。

这篇博客是自己在学习的时候跟着老师一起敲的,如有错误或不足之处还请多指教,以下是几个参考学习的链接。

可视化算法结构

前端JS实现十大排序算法可参见博文

学习视频bilibili

发表评论

表情:
评论列表 (有 0 条评论,369人围观)

还没有评论,来说两句吧...

相关阅读

    相关 排序算法Java实现

    > 之前写了JavaScript实现的排序算法,因为社会的压迫233333,Java可能也得写写,所以就将这个放在这里来了,算法介绍看之前JavaScript那一篇播客吧,这里