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

快来打我* 2023-09-23 18:25 212阅读 0赞

目录

  • 一.常见排序类型
  • 二.排序详解
    • 1.冒泡排序
    • 2.选择排序
    • 3.插入排序
    • 4.希尔排序
    • 5.快速排序
    • 6.归并排序
    • 7.基数排序(桶排序)
    • 8.堆排序
  • 三.排序算法比较

一.常见排序类型

  • 插入排序:插入排序、希尔排序
  • 选择排序:选择排序、堆排序
  • 交换排序:冒泡排序、快速排序
  • 归并排序
  • 基数排序(又叫桶排序)

二.排序详解

1.冒泡排序

(1)思路图解

从头开始比较相邻元素的值(就是从下标较小的元素开始),使值较大的元素逐渐从前移向后部,就像水里的气泡一样,越来越大,向上冒。最终得到从小到大的一个序列。

在这里插入图片描述
在这里插入图片描述

(2)算法实现(java)

  1. /**
  2. * 冒泡排序
  3. * author:xinxin
  4. * 时间复杂度是(n*n)
  5. */
  6. public class GuLouSort {
  7. public static void main(String[] args) {
  8. int[] arr = {
  9. 16,28,5,66,88}; //初始化数组
  10. int temp = 0; //临时变量
  11. for (int i = 0; i < arr.length-1; i++){
  12. //代表大循环 第几趟
  13. for (int j = 0; j < arr.length-1-i; j++){
  14. //代表小循环
  15. if (arr[j] > arr[j+1]){
  16. //如果前面的值大于前面的值,那就交换位置
  17. temp = arr[j];
  18. arr[j] = arr[j+1];
  19. arr[j+1] = temp;
  20. }
  21. }
  22. System.out.println("第"+(i+1)+"趟,排序后的结果");
  23. System.out.println(Arrays.toString(arr));
  24. }
  25. }
  26. }

运行结果
在这里插入图片描述

2.选择排序

(1)思路图解

先从arr[0] ~ arr[n-1]找到一个最小值,和arr[0]交换,在从arr[1] ~ arr[n-1]中找到最小值,和arr[1]交换,以此类推,最终得到一个从小到大排列的序列。

在这里插入图片描述
在这里插入图片描述

(2)算法实现(java)

  1. /**
  2. * 选择排序
  3. * author:xinxin
  4. * 时间复杂度是(n*n)
  5. */
  6. public class SelectSort {
  7. public static void main(String[] args) {
  8. int[] arr = {
  9. 16,6,88,2}; //初始化数组
  10. for (int i = 0; i < arr.length-1; i++) {
  11. //代表第几轮循环
  12. int minIndex = i; //假定最小下标
  13. int minArr = arr[i]; //假定最小的值
  14. for (int j = i+1; j < arr.length; j++) {
  15. //代表每轮循环中去查找最小的值
  16. if (minArr > arr[j]){
  17. //如果假定的最小值比他后面的值要大,记录下标和值(这个地方控制是从大到小排序还是从小到大排序)
  18. minIndex = j; //记录下标
  19. minArr = arr[j]; //记录值
  20. }
  21. }
  22. //交换值
  23. if (minIndex != i){
  24. arr[minIndex] = arr[i];
  25. arr[i] = minArr;
  26. }
  27. System.out.println("第"+(i+1)+"轮结果为");
  28. System.out.println(Arrays.toString(arr));
  29. }
  30. }
  31. }

运行结果
在这里插入图片描述

3.插入排序

(1)思路图解

n个元素,分成一个有序序列和无序序列,开始有序序列只有一个元素,无序序列包含n-1个元素,排序时每次从无序序列取出第一个元素,和有序序列的元素的排序码进行比较,然后放到合适的位置,最后形成新的有序序列。

在这里插入图片描述
在这里插入图片描述

(2)算法实现(java)

  1. /**
  2. * 插入排序
  3. * author:xinxin
  4. */
  5. public class InsertSort {
  6. public static void main(String[] args) {
  7. int[] arr = {
  8. 18, 6, 26, 15, 21,10};//初始化数组
  9. for (int i = 1; i < arr.length; i++) {
  10. int currValue = arr[i]; //要插入的元素,从arr[1]开始,因为arr[0]是有序序列第一个元素
  11. int beforeIndex = i-1; //要插入元素前面的元素的下标
  12. while(beforeIndex >= 0 && currValue < arr[beforeIndex]){
  13. //判断要插入元素的位置,并且没有找到插入位置(从这里可以改变从大到小排序还是从小到大)
  14. arr[beforeIndex + 1] = arr[beforeIndex]; //arr[beforeIndex]后移
  15. beforeIndex--;
  16. }
  17. //当退出while循环,找到插入位置
  18. arr[beforeIndex + 1] = currValue;
  19. System.out.println("第"+i+"次插入结果");
  20. System.out.println(Arrays.toString(arr));
  21. }
  22. }
  23. }

运行结果
在这里插入图片描述

4.希尔排序

(1)思路图解

希尔排序也是一种插入排序,但是更加高效,也称为速效增量排序。
按照一定的增量(步长)进行分组,然后对每组进行插入排序,随着增量减少,每组的元素就越多,当增量减至1时,元素被分为一组,算法结束。

在这里插入图片描述
在这里插入图片描述

(2)算法实现(交换法)

  1. /**
  2. * 希尔排序(交换法)
  3. * author:xinxin
  4. */
  5. public class ShellSort {
  6. public static void main(String[] args) {
  7. int[] arr = {
  8. 8, 9, 1, 7, 2, 3, 5, 4, 6, 0};//初始数组
  9. int temp = 0;
  10. int count = 0;
  11. //增量
  12. for (int gap = arr.length/2; gap > 0; gap=gap/2){
  13. //遍历组中所有元素,gap为步长
  14. for (int i = gap; i < arr.length; i++){
  15. for (int j = i - gap; j >= 0; j = j-gap){
  16. //如果当前元素大于加上步长的元素,那就交换位置
  17. if (arr[j] > arr[j+gap]){
  18. temp = arr[j];
  19. arr[j] = arr[j+gap];
  20. arr[j+gap] = temp;
  21. }
  22. }
  23. }
  24. System.out.println("希尔排序(交换法)第"+(++count)+"轮的结果");
  25. System.out.println(Arrays.toString(arr));
  26. }
  27. }
  28. }

运行结果(交换法)
在这里插入图片描述

(3)算法实现(移动法)

  1. /**
  2. * 希尔排序(移动法)
  3. * author:xinxin
  4. */
  5. public class ShellSort2 {
  6. public static void main(String[] args) {
  7. int[] arr = {
  8. 8, 9, 1, 7, 2, 3, 5, 4, 6, 0};//初始数组
  9. int count = 0;
  10. for (int gap = arr.length/2; gap > 0; gap = gap/2){
  11. //从第gap元素开始进行插入排序
  12. for (int i = gap; i<arr.length; i++){
  13. int j = i;
  14. int temp = arr[j];
  15. if (arr[i] < arr[j - gap]){
  16. while(j - gap >= 0 && temp < arr[j - gap]){
  17. //移动
  18. arr[j] = arr[j - gap];
  19. j = j - gap;
  20. }
  21. arr[j] = temp;
  22. }
  23. }
  24. System.out.println("希尔排序(移动法)第"+(++count)+"轮的结果");
  25. System.out.println(Arrays.toString(arr));
  26. }
  27. }
  28. }

运行结果(移动法)
在这里插入图片描述

5.快速排序

(1)思路图解

快速排序是对冒泡排序的一种改进。
基本思想是:把需要排序的元素分成两部分,一部分比另一部分都要小,然后进行快速排序,然后递归,最终得到有序序列。

在这里插入图片描述
这个图是以第一个元素为中间值分割。(黄色的就相当于中间值,然后把比大小分成两组)
在这里插入图片描述

(2)算法实现(java)

  1. /**
  2. * 快速排序
  3. * author:xinxin
  4. */
  5. public class QuikeSort {
  6. public static void main(String[] args) {
  7. int[] arr = {
  8. 1, 66, 2, 28, 0, 18};
  9. quickeSotr(arr,0,arr.length-1);
  10. System.out.println("快速排序结果为"+ Arrays.toString(arr));
  11. }
  12. public static void quickeSotr(int arr[],int left,int right){
  13. int l = left;//左下标
  14. int r = right;//右下标
  15. int center = arr[(l + r)/2];//中间值
  16. int temp = 0;//临时变量
  17. //找到比center值小的放左边,比center值大的放右边
  18. while(l < r){
  19. //找到比center小的值,才退出
  20. while (arr[l] < center){
  21. l = l + 1;
  22. }
  23. //找到比center大的值,才退出
  24. while (arr[r] > center){
  25. r = r - 1;
  26. }
  27. //证明左边都是小于center的数,右边都是大于center的数
  28. if (l >= r){
  29. break;
  30. }
  31. //交换变量
  32. temp = arr[l];
  33. arr[l] = arr[r];
  34. arr[r] = temp;
  35. //前移
  36. if (arr[l] == center){
  37. r = r - 1;
  38. }
  39. //后移
  40. if (arr[r] == center){
  41. l = l + 1;
  42. }
  43. }
  44. //如果相等必须进行下面操作,不然会栈溢出
  45. if (l == r){
  46. l = l + 1;
  47. r = r - 1;
  48. }
  49. //向左递归
  50. if (left < r){
  51. quickeSotr(arr,left,r);
  52. }
  53. //向右递归
  54. if (right > l){
  55. quickeSotr(arr,l,right);
  56. }
  57. }
  58. }

运行结果
在这里插入图片描述

6.归并排序

(1)思路图解

利用归并的思想实现排序,采用分治策略:将问题分成小的问题然后递归求解,而治的阶段将分的阶段得到的结果放一起组合,即分而治之。

下面治的阶段相当于 [4,5,7,8]和[1,2,3,6]各有指针去移动比较两个数组元素大小(和快速排序移动指针一样),然后放入新的数组。

在这里插入图片描述
在这里插入图片描述

(2)算法实现(java)

  1. public class MergeSort {
  2. public static void main(String[] args) {
  3. int[] arr = {
  4. 8, 4, 5, 7, 1, 3, 6, 2};
  5. int[] temp = new int[arr.length];
  6. mergeSort(arr,0,arr.length-1,temp);
  7. System.out.println("归并排序结果为"+ Arrays.toString(arr));
  8. }
  9. /**
  10. * 分 + 合
  11. * @param arr
  12. * @param left
  13. * @param right
  14. * @param temp
  15. */
  16. public static void mergeSort(int[] arr, int left, int right, int[] temp) {
  17. if (left < right){
  18. int center = (left + right)/2;//中间索引
  19. mergeSort(arr,left,center,temp);//左递归
  20. mergeSort(arr,center + 1,right,temp);//右递归
  21. merge(arr,left,center,right,temp);//合并(治)
  22. }
  23. }
  24. /**
  25. * 合并的方法
  26. *
  27. * @param arr 原数组
  28. * @param left 左下标
  29. * @param center 中间值
  30. * @param right 右下标
  31. * @param temp 中转数组
  32. */
  33. public static void merge(int[] arr, int left, int center, int right, int[] temp){
  34. int i = left;//左边序列初始索引
  35. int j = center + 1;//右边序列初始索引
  36. int t = 0;//temp数组初始下标
  37. //把左右两边的有序序列按规则填充到temp中
  38. while(i <= center && j <= right){
  39. //当左边序列元素小于右边序列元素,把左边序列元素放进temp数组中
  40. if (arr[i] < arr[j]){
  41. temp[t] = arr[i];
  42. i = i + 1;
  43. t = t + 1;
  44. }else{
  45. //反之,将右边序列元素放进temp数组中
  46. temp[t] = arr[j];
  47. j = j + 1;
  48. t = t + 1;
  49. }
  50. }
  51. //左边序列剩余元素放进temp数组
  52. while(i <= center){
  53. temp[t] = arr[i];
  54. i = i + 1;
  55. t = t + 1;
  56. }
  57. //右边序列剩余元素放进temp数组
  58. while(j <= right){
  59. temp[t] = arr[j];
  60. j = j + 1;
  61. t = t + 1;
  62. }
  63. //将temp拷贝到arr,有可能不是全部拷贝
  64. t = 0;
  65. int tempLeft = left;
  66. System.out.println(" ");
  67. System.out.println("tempLeft="+tempLeft+" right="+right);
  68. while (tempLeft < right){
  69. arr[tempLeft] = temp[t];
  70. tempLeft = tempLeft + 1;
  71. t = t + 1;
  72. }
  73. }
  74. }

运行结果
在这里插入图片描述

7.基数排序(桶排序)

(1)思路图解

将整数按位数切割成不同数字,然后按每个位数分别比较。
将所有待比较数值统一为同样的数位长度,数位较短的数前面补零。然后从最低位开始,依次进行依次排序。这样从最低到最高位排序完成之后,就得到一个有序序列。

在这里插入图片描述
(2)算法实现(java)

  1. /**
  2. * 基数排序(桶排序)
  3. * author:xinxin
  4. */
  5. public class BucketSort {
  6. public static void main(String[] args) {
  7. int[] arr = {
  8. 3,44,38,5,47,15,36,26,27,2,46,4,19,50,48};
  9. //找到最大的数
  10. int max = arr[0];
  11. for (int k = 1; k < arr.length; k++){
  12. if (max < arr[k]){
  13. max = arr[k];
  14. }
  15. }
  16. //最大的数位数是几位
  17. int maxLength = (max + "").length();
  18. //表示10个桶,每个一维数组都是一个桶
  19. int[][] bucket = new int[10][arr.length];
  20. //记录每个桶放入数据的个数
  21. int[] qty = new int[10];
  22. for (int l = 0,n = 1; l < maxLength; l++,n = n * 10) {
  23. //取对应位数数放进桶(第一次个位,第二次十位,第三次百位以此类推)
  24. for (int i = 0; i < arr.length; i++) {
  25. int bits = arr[i] / n % 10;
  26. //放到这个桶第一个位置,第二次放在第二个位置
  27. bucket[bits][qty[bits]] = arr[i];
  28. qty[bits]++;
  29. }
  30. //取出桶的数据放回原数组
  31. int index = 0;
  32. for (int j = 0; j < qty.length; j++) {
  33. if (qty[j] != 0){
  34. for (int y = 0; y < qty[j]; y++){
  35. arr[index++] = bucket[j][y];
  36. }
  37. }
  38. //需要把桶归零
  39. qty[j] = 0;
  40. }
  41. System.out.println("第"+(l+1)+"轮,对位数的处理");
  42. System.out.println(Arrays.toString(arr));
  43. }
  44. }
  45. }

运行结果
在这里插入图片描述

8.堆排序

(1)思路图解

将待排序序列构成一个大顶堆,最大值就是堆的根节点,将其与末尾元素交换,此时末尾就是最大值,然后剩余的元素重新构成堆,然后反复执行,最终形成一个有序序列。

在这里插入图片描述
(2)算法实现(java)

  1. /**
  2. * 堆排序
  3. * author:xinxin
  4. */
  5. public class HeapSort {
  6. public static void main(String[] args) {
  7. int[] arr = {
  8. 5,2,7,3,6,1,4};
  9. int temp = 0;
  10. //使数组形成大顶堆
  11. for (int i = arr.length / 2 - 1; i >= 0; i--) {
  12. bigHeap(arr,i,arr.length);
  13. }
  14. //堆顶元素与末尾元素交换,使最大元素放在数组末端
  15. for (int j = arr.length-1; j > 0; j--) {
  16. temp = arr[j];
  17. arr[j] = arr[0];
  18. arr[0] = temp;
  19. bigHeap(arr,0,j);
  20. }
  21. System.out.println("堆排序结果");
  22. System.out.println(Arrays.toString(arr));
  23. }
  24. /**
  25. * 使待排序数组形成大顶堆
  26. * @param arr 初始数组
  27. * @param i 非叶子节点在数组中的下标
  28. * @param length 对多少个元素进行调整,length在减少
  29. */
  30. public static void bigHeap(int[] arr, int i, int length){
  31. int temp = arr[i];
  32. //i * 2 + 1是 i 的左子节点
  33. for (int k = i * 2 + 1; k < length; k = k * 2 + 1){
  34. //左子节点小于右子节点
  35. if(k + 1 < length && arr[k] < arr[k+1]){
  36. //指向右子节点
  37. k++;
  38. }
  39. //子节点大于根节点
  40. if (arr[k] > temp){
  41. arr[i] = arr[k];
  42. i = k;
  43. }else{
  44. break;
  45. }
  46. //把temp放在调整后的位置
  47. arr[i] = temp;
  48. }
  49. }
  50. }

运行结果
在这里插入图片描述

三.排序算法比较

shell排序是希尔排序在这里插入图片描述

  • 稳定

a原本在b前面,而a=b,排序之后a还在b前面。

  • 不稳定

a原本在b前面,而a=b,排序之后a可能在b后面。

  • 时间复杂度

算法执行需要的时间。

  • 空间复杂度

算法运行需要的内存的大小。

发表评论

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

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

相关阅读