算法_排序算法(冒泡排序、选择排序、插入排序、希尔排序、快速排序、归并排序、基数排序、堆排序)

拼搏现实的明天。 2023-07-10 14:28 112阅读 0赞

文章目录

    • 冒泡排序
    • 选择排序
    • 插入排序
    • 希尔排序
    • 快速排序
    • 归并排序
    • 基数排序
    • 堆排序
    • 常用排序算法总结和对比

冒泡排序

  1. 一共进行 arr.length-1次循环
  2. 每轮中两两比较,前者比后者大交换,将最大值排到末尾,不参与下一轮排序
  3. 如果发现在某轮排序中,没有发生一次交换, 可以提前结束冒泡排序

    /**

    • 冒泡排序
    • @param arr
      */
      private static void BubbleSort(int[] arr) {

      //控制比较轮数,两两比较,需要arr.length-1轮
      for (int i = 0; i < arr.length - 1; i++) {

      1. boolean flag = true;
      2. //每轮中两两比较,前者比后者大交换
      3. for (int j = 0; j < arr.length - 1 - i; j++) {
      4. if (arr[j + 1] < arr[j]) {
      5. int temp = arr[j];
      6. arr[j] = arr[j + 1];
      7. arr[j + 1] = temp;
      8. flag = false;
      9. }
      10. }
      11. //一轮中都没有出现交换,退出
      12. if (flag) {
      13. break;
      14. }

      }
      }

选择排序

在n个数中找到最小(大)的放到最前(后)面,再在剩下的n-1个数中找到最小的放到第二个位置,以此类推,直到数组中只剩下一个元素。

  1. /**
  2. * 选择排序
  3. * @param arr
  4. */
  5. private static void selectSort(int[] arr) {
  6. //控制比较轮数,两两比较,需要arr.length-1轮
  7. for (int i = 0; i < arr.length - 1; i++) {
  8. int min = i;
  9. //记录最小值索引
  10. for (int j = i + 1; j < arr.length; j++) {
  11. if (arr[j] < arr[min]) {
  12. min = j;
  13. }
  14. }
  15. //与当前轮首位交换
  16. if (i != min) {
  17. int temp = arr[i];
  18. arr[i] = arr[min];
  19. arr[min] = temp;
  20. }
  21. }
  22. }

插入排序

把n个待排序的元素看成为一个有序表和一个无序表,开始时有序表中只包含一个元素,无序表中包含有n-1个元素,排序过程中每次从无序表中取出第一个元素,把它的排序码依次与有序表元素的排序码进行比较,将它插入到有序表中的适当位置,使之成为新的有序表。

  1. /**
  2. * 插入排序
  3. * @param arr
  4. */
  5. private static void insertSort(int[] arr) {
  6. for (int i = 1; i < arr.length; i++) {
  7. //待插入的数
  8. int insertVal = arr[i];
  9. //待插入索引
  10. int insertIndex = i - 1;
  11. // 给insertVal 找到插入的位置
  12. while (insertIndex >= 0 && insertVal < arr[insertIndex]) {
  13. arr[insertIndex + 1] = arr[insertIndex];
  14. insertIndex--;
  15. }
  16. // 当退出while循环时,说明插入的位置找到, insertIndex + 1
  17. if (insertIndex + 1 != i) {
  18. arr[insertIndex + 1] = insertVal;
  19. }
  20. }
  21. }

希尔排序

希尔排序是把记录按下标的一定增量分组,对每组使用直接插入排序算法排序;随着增量逐渐减少,每组包含的关键词越来越多,当增量减至1时,整个文件恰被分成一组,算法便终止。

  1. /**
  2. * 希尔排序
  3. * @param arr
  4. */
  5. private static void shellSort(int[] arr) {
  6. // 增量gap, 并逐步的缩小增量
  7. for (int gap = arr.length / 2; gap > 0; gap /= 2) {
  8. //gap版插入排序
  9. for (int i = gap; i < arr.length; i++) {
  10. int insertVal = arr[i];
  11. int insertIndex = i - gap;
  12. while (insertIndex >= 0 && insertVal < arr[insertIndex]) {
  13. arr[insertIndex + gap] = arr[insertIndex];
  14. insertIndex -= gap;
  15. }
  16. if (i != insertIndex) {
  17. arr[insertIndex + gap] = insertVal;
  18. }
  19. }
  20. }
  21. }

快速排序

通过一趟排序将要排序的数据分割成独立的两部分,其中一部分的所有数据都比另外一部分的所有数据都要小,然后再按此方法对这两部分数据分别进行快速排序,整个排序过程可以递归进行,以此达到整个数据变成有序序列。

  1. /**
  2. * 快速排序
  3. * @param arr
  4. * @param low
  5. * @param high
  6. */
  7. private static void quickSort(int[] arr, int low, int high) {
  8. if (low < high) {
  9. int pivot = getPivot(arr, low, high);
  10. quickSort(arr, low, pivot - 1);
  11. quickSort(arr, pivot + 1, high);
  12. }
  13. }
  14. /**
  15. * 枢轴
  16. * @param arr
  17. * @param low
  18. * @param high
  19. * @return
  20. */
  21. private static int getPivot(int[] arr, int low, int high) {
  22. int pivot = arr[low];
  23. while (low < high) {
  24. //如果枢轴定义为低位端,从高位端先遍历
  25. while (low < high && arr[high] >= pivot) {
  26. high--;
  27. }
  28. arr[low] = arr[high];
  29. while (low < high && arr[low] <= pivot) {
  30. low++;
  31. }
  32. arr[high] = arr[low];
  33. }
  34. //此时low=high,即为枢轴
  35. arr[low] = pivot;
  36. return low;
  37. }

归并排序

采用经典的分治(divide-and-conquer)策略(分治法将问题分(divide)成一些小的问题然后递归求解,而治(conquer)的阶段则将分的阶段得到的各答案”修补”在一起,即分而治之)。

  1. /**
  2. * 递归拆分+合并
  3. * @param arr
  4. * @param start
  5. * @param end
  6. * @param temp
  7. */
  8. private static void mergeSort(int[] arr, int start, int end, int[] temp) {
  9. if (start < end) {
  10. //中间索引
  11. int mid = (start + end) / 2;
  12. //向左递归分解
  13. mergeSort(arr, start, mid, temp);
  14. //向右递归分解
  15. mergeSort(arr, mid + 1, end, temp);
  16. //退栈合并
  17. merge(arr, start, end, temp);
  18. }
  19. }
  20. /**
  21. * 合并
  22. * @param arr
  23. * @param start
  24. * @param end
  25. * @param temp
  26. */
  27. private static void merge(int[] arr, int start, int end, int[] temp) {
  28. int mid = (start + end) / 2;
  29. int left = start;
  30. int right = mid + 1;
  31. int cur = 0;
  32. //左右比较,有序添加到temp数组
  33. while (left <= mid && right <= end) {
  34. temp[cur++] = arr[left] <= arr[right] ? arr[left++] : arr[right++];
  35. }
  36. //添加剩余数据
  37. while (left <= mid) {
  38. temp[cur++] = arr[left++];
  39. }
  40. while (right <= end) {
  41. temp[cur++] = arr[right++];
  42. }
  43. //temp数组复制到原数组
  44. left = start;
  45. cur = 0;
  46. while (left <= end) {
  47. arr[left++] = temp[cur++];
  48. }
  49. }

基数排序

  • 基数排序(radix sort)属于“分配式排序”(distribution sort),又称“桶子法”(bucket sort)或bin sort,顾名思义,它是通过键值的各个位的值,将要排序的元素分配至某些“桶”中,达到排序的作用。
  • 基数排序法是属于稳定性的排序,基数排序法的是效率高的稳定性排序法
  • 基数排序(Radix Sort)是桶排序的扩展
  • 将所有待比较数值统一为同样的数位长度,数位较短的数前面补零。然后,从最低位开始,依次进行一次排序。这样从最低位排序一直到最高位排序完成以后, 数列就变成一个有序序列。

    /**

    • 基数排序
    • @param arr
      */
      private static void radixSort(int[] arr) {

      //查找最大数
      int max = arr[0];
      for (int i : arr) {

      1. max = max > i ? max : i;

      }
      //得到最大位数
      int maxLength = (max + “”).length();
      //定义二维桶数组
      int[][] bucket = new int[10][arr.length];
      //定义数组保存每个桶中的元素个数
      int[] bucketECount = new int[10];
      //放入桶
      for (int i = 0; i < maxLength; i++) {

      1. for (int j = 0; j < arr.length; j++) {
      2. //取出对应位的值,按个十百...的顺序
      3. int digit = (int) (arr[j] / Math.pow(10, i) % 10);
      4. bucket[digit][bucketECount[digit]++] = arr[j];
      5. }

      }
      //取出
      int index = 0;
      for (int i = 0; i < bucket.length; i++) {

      1. if (bucketECount[i] != 0) {
      2. for (int j = 0; j < bucketECount[i]; j++) {
      3. arr[index++] = bucket[i][j];
      4. }
      5. }
      6. bucketECount[i] = 0;

      }
      }

堆排序

  • 堆是具有以下性质的完全二叉树:每个结点的值都大于或等于其左右孩子结点的值,称为大顶堆, 注意 : 没有要求结点的左孩子的值和右孩子的值的大小关系。
  • 每个结点的值都小于或等于其左右孩子结点的值,称为小顶堆
  • 大顶堆特点:arr[i] >= arr[2*i+1] && arr[i] >= arr[2*i+2] , i 对应第几个节点,i从0开始编号
  • 小顶堆:arr[i] <= arr[2*i+1] && arr[i] <= arr[2*i+2]
  • 升序采用大顶堆,降序采用小顶堆

    /**

    • 堆排序
    • @param arr
      */
      private static void heapSort(int[] arr) {

      //将无序序列构成一个大顶堆
      //完全二叉树最后一个非叶子节点为arr.length / 2 - 1
      for (int i = arr.length / 2 - 1; i >= 0; i—) {

      1. adjustHeap(arr, i, arr.length);

      }
      //首尾元素交换,排除最大元素后并重构
      for (int i = arr.length - 1; i > 0; i—) {

      1. int temp = arr[i];
      2. arr[i] = arr[0];
      3. arr[0] = temp;
      4. adjustHeap(arr, 0, i);

      }
      }

    /**

    • 将一个顺序存储二叉树(数组)调整为大顶堆
    • @param arr
    • @param i
    • @param length
      */
      private static void adjustHeap(int[] arr, int i, int length) {

      int temp = arr[i];
      //i的左节点2i+1,右节点2i+2
      for (int k = 2 i + 1; k < length; k = k 2 + 1) {

      1. //挑出左右节点较大者
      2. if (k + 1 < length && arr[k] < arr[k + 1]) {
      3. k++;
      4. }
      5. //与待交换节点对比,谁大谁当父节点
      6. if (arr[k] > temp) {
      7. arr[i] = arr[k];
      8. i = k;
      9. } else {
      10. break;
      11. }

      }
      arr[i] = temp;
      }

常用排序算法总结和对比

在这里插入图片描述

发表评论

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

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

相关阅读