几种常用排序算法总结与优化

本文详细介绍了多种排序算法,包括冒泡排序、选择排序、插入排序、希尔排序、归并排序、快速排序、堆排序、桶排序、基数排序和计数排序。讨论了每种排序算法的时间复杂度、空间复杂度以及稳定性,并提到了优化策略,如二分查找插入排序、希尔排序的改进和快速排序的双轴切分。对于特定场景,如数据范围小、元素分布均匀,某些排序算法如计数排序和基数排序能提供线性时间复杂度。文章最后总结了各种排序算法的优缺点,强调了在实际应用中选择合适的排序算法的重要性。
摘要由CSDN通过智能技术生成


本文对几种常用排序进行总结
基本知识:

  1. 排序稳定性
  2. 排序的时间复杂度、空间复杂度

冒泡排序(BubbleSort)

参考:https://juejin.im/post/5cacbbe8e51d456e500f7cd0

  1. 思路:
    它重复地走访要排序的数列,一次比较两个元素,如果第二个数字小,就把交换两个数。
    它是简单的选择排序。
  2. 时间复杂度:时间复杂度最好的情况为O(n),最坏的情况是O(n^2)
  3. 空间复杂度:O(1)
  4. 算法稳定性:稳定
    冒泡排序就是把小的元素往前调或者把大的元素往后调。比较是相邻的两个元素比较,交换也发生在这两个元素之间。所以,如果两个元素相等,是不会再交换的;如果两个相等的元素没有相邻,那么即使通过前面的两两交换把两个相邻起来,这时候也不会交换,所以相同元素的前后顺序并没有改变,所以冒泡排序是一种稳定排序算法。
  5. 优化:
    (1)数组整体有序:设置标志位,明显如果有一趟没有发生交换(flag = false),说明排序已经完成
    (2)数组局部有序:记录一轮下来标记的最后位置,下次从头部遍历到这个位置就Ok
    (3)双向冒泡,一轮走访,把最大值放到最右边,同时最小值放到最左边
    也可以把上述优化两两组合
 //冒泡排序的经典写法, 从前往后,大数下沉
    public void BubbleSort(int[] array){
        for (int end = array.length-1; end >0; end--) { //外层循环使用end来控制内层循环中极值最终上浮到的位置
        // 内层循环用来两两比较并交换
            for (int i = 0; i <end ; i++) { 
                if(array[i]>array[i+1]){
                    exchange(array,i,i+1);
                }
            }
        }
    }
//优化一:处理在排序过程中数组整体已经有序的情况
//假设排序ar[]={1,2,3,4,5,6,7,8,10,9}这组数据,按照上面的排序方式,第一趟排序后将10和9交换已经有序,接下来的8趟排序就是多余的,可以在交换的地方加一个标记,如果那一趟排序没有交换元素,说明这组数据已经有序,不用再继续下去。
 public void BubbleArrayImprove1(int[] array){
        for (int end = array.length-1; end >0; end--) {
            boolean isSorted =false;
            for (int i = 0; i <end ; i++) {
                if(array[i]>array[i+1]){
                    exchange(array,i,i+1);
                    isSorted = true;
                }
            }
            if(isSorted==false){
                return;
            }
        }
    }
//优化二:处理局部有序,在遍历过程中可以记下最后一次发生交换事件的位置, 下次的内层循环就到这个位置终止, 可以节约多余的比较操作.
 public void BubbleArrayImprove2(int[] array){
      int end = array.length-1;//记录这一轮循环最后一次发生交换操作的位置
      while(end>0){
          int tempEnd = end;//设置这一轮循环结束的位置,因为end可能会变,所以一定把本次循环的end先存下来
          for (int i = 0; i <tempEnd ; i++) {//这里不能写end
              if(array[i]>array[i+1]){
                  exchange(array,i,i+1);
                  end = i;//设置(更新)最后一次发生了交换操作的位置
              }
          }
          // 若这一轮没有发生交换,则证明数组已经有序,直接返回即可
          if(end == tempEnd) return ;
      }
    }
    //优化三:双向冒泡
     public void BubbleArrayImprove3(int[] array){
        int start = 0;
        int end = array.length-1;
        while(start<end){
            for (int i =start; i < end ; i++) {
                if(array[i]>array[i+1])exchange(array,i,i+1);
            }
            end--;
            for (int i = end; i >0; i--) {
                if(array[i]<array[i-1]){
                    exchange(array,i,i-1);
                }
            }
            start++;
        }
    }

选择排序

  1. 思路
    在第一趟遍历N个数据,找出其中最小的数值与第一个元素交换,第二趟遍历剩下的N-1个数据,找出其中最小的数值与第二个元素交换…第N-1趟遍历剩下的2个数据,找出其中最小的数值与第N-1个元素交换,至此选择排序完成。
  2. 时间复杂度O(n^2)
    元素移动次数很少,当表有序时移动次数为0,但比较的次数与表的次序无关,所以时间复杂度始终为O(n^2)
  3. 空间复杂度:**O(1)**原地排序算法
  4. 稳定性:不稳定
    例如5,5,3,第一趟就把第一个5跟3进行交换,两个5的相对位置就变了
  5. 优化:
    每次找到最小值和最大值,放到收尾对应位置,虽然时间复杂度不变,但是运行时间变短了。
//最基本的选择排序
 public static void selectedSort(int[] arr){
        for (int i = 0; i <arr.length-1 ; i++) {
            int min = i;//记录要交换的元素的位置
            for (int j = i+1; j < arr.length ; j++) {
               if(arr[j]<arr[min]){
                   min = j;
               }
            }
           exchange(arr,i,min);
        }
    }
 //   优化一:
     public static void selectedSortBetter(int[] arr){
        for (int i = 0,j=arr.length-1; i < arr.length&&j>i; i++,j--) {
            int minIndex = i;
            int maxIndex = j;
            for(int k = i;k<=j;k++){//不能跟之前一样从i的后一位开始找,如果这样的话,如果i恰好是最大的位,就遗漏了
                if(arr[k]<arr[minIndex])minIndex = k;
                if(arr[k]>arr[maxIndex]) maxIndex = k;
            }
            exchange(arr,minIndex,i);//先交换最小值
            if(maxIndex == i){//检查一下,maxIndex是否是i,如果是,要改,因为i已经换位置了
                maxIndex = minIndex;
            }
            exchange(arr,maxIndex,j);
        }
    }

插入排序(InsertionSort)

  1. 思想
    每一步将一个元素,按照其关键字的大小插入到它前面已经排序的子序列中,依此重复,直到插入全部元素!对于基本有序的数组比复杂算法还好用,比冒泡和排序都快
  2. 时间复杂度
    a. 最好情况:序列是升序排列,在这种情况下,需要进行的比较操作需(n-1)次。后移赋值操作为0次。即O(n)
    b. 最坏情况:序列是降序排列,那么此时需要进行的比较共有n(n-1)/2次。后移赋值操作是比较操作的次数加上 (n-1)次。即O(n^2)
    c. 平均时间复杂度:O(n^2)
  3. 空间复杂度O(1)
  4. 稳定性:稳定
    插入排序是在一个已经有序的小序列的基础上,一次插入一个元素。如果碰见一个和插入元素相等的,那么插入元素把想插入的元素放在相等元素的后面。所以,相等元素的前后顺序没有改变,算法稳定
  5. 优化
    (1)二分查找插入排序:因为在一个有序区中查找一个插入位置,所以可使用二分查找,减少元素比较次数提高效率。
    (2) 希尔排序:如果序列本来就是升序或部分元素升序,那么比较+
  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值