【java】七大排序算法总结(常看常新 应该没啥bug了

排序方法最好情况最坏情况平均情况辅助空间
插入直接插入排序(稳定)nn^2n^2O(1)
插入希尔排序nn^2n1/n3O(1)
交换冒泡排序(稳定)nn^2n^2O(1)
交换快速排序nlognn^2nlognO(nlogn)
选择直接选择排序n^2n^2n^2O(1)
选择堆排序nlognnlognnlognO(n^2)
归并排序(稳定)nlognnlognnlognO(n)
七大排序算法总结

通用swap代码:

private static final ThreadLocalRandom random=ThreadLocalRandom.current();
private static void swap(int[] arr, int i, int min) {
    int temp=arr[i];
    arr[i]=arr[min];
    arr[min]=temp;
}
  1. 直接选择排序:
    1. 速记 思想:前i个有序,剩余的选出最小的数与第i+1个交换(i=0…)

    2. 代码1:(一堆里面找最小的一换)

  /**
       * 直接选择排序
       */
      public static void selectionSort(int[] arr) {
          //剩余的选出来与第i个交换 i=0
          for (int i = 0; i < arr.length-1; i++) {
              //已经有序的集合[0,i] 未排序的集合[i+1,n]
              int min=i;
              for (int j = i+1; j < arr.length; j++) {
                  if(arr[j]<arr[min]){
                      min=j;
                  }
              }
              //此时min存储了最小值元素的下标
              swap(arr,i,min);
      
          }
      
      }
      
  1. 代码2:双向选择排序
  /**
       * 双向选择排序 每次选出最小的放前面 最大的放后面
       * @param arr
       */
      public static void selectionSortOP(int[] arr){
          int low=0,high=arr.length-1;
          //有序区间 [0,low+1]
          while(low<high){
              int min=low,max=low;
              for (int i = low+1; i <= high; i++) {
                  if(arr[min]>arr[i]){
                      min=i;
                  }
                  if(arr[max]<arr[i]){
                      max=i;
                  }
              }
              //max存储无序区间最大值 min存储最小值
              swap(arr,low,min);
              if(low==max){
                  //max在首(low)位 交换最小之后会覆盖max的位置 所以使用刚好交换后min位置的最大值找到max
                  max=min;
              }
              swap(arr,max,high);
              low++;
              high--;
          }
      }
  1. 直接插入排序:
    1. 速记 思想:先排好前两位,后面一个和前面i个比

    2. 代码1:

/**
       * 直接插入排序
         * 每次选择无序区间的第一个元素 插入到有序区间的合适位置
         * @param arr
         */
        public static void insertionSortBase(int[] arr){
            //有序区间 [0,1) 默认第一个元素有序
            for (int i = 1; i < arr.length ; i++) {
                for (int j = i; j>0&&arr[j]<arr[j-1] ; j--) {
                    swap(arr,j,j-1);
    //                if(arr[j]<arr[j-1]){
    //                    swap(arr,j,j-1);
    //                }
    //                else{
    //                    //j位置对应元素已经到达相应位置
    //                    break;
    //                }
                }
            }
        }
  1. 代码2:折半插入排序
    /**
       * 插入数据时采用二分法查找插入位置
       * @param arr
       */
      public static void insertSortBS(int[] arr){
          for (int i = 1; i < arr.length; i++) {
              //无序区间第一个值
              int val=arr[i];
              //有序区间[0,i);
              int low=0;
              int high=i;
              while(low<high){
                  int mid =(low+high)>>1;
                  if(val<=arr[mid]){
                      high=mid;
                  }else{
                      low=mid+1;
                  }
              }
              //数据后移
              for(int j=i;j>low;j--){
                  arr[j]=arr[j-1];
              }
              arr[low]=val;
      
          }
      }
      
  1. 希尔排序:
    1. 思想:gap=length/2作为第一个增量 ,将待排序的数据按照gap分组,距离为gap的数组放在同一组,然后不断缩小增量gap=gap/2,当gap=1时,整个数组接近有序,调用普通插入方法同意排序。

    2. 代码:

 /**
       * 希尔排序
       * @param arr
       */
      public static void shellSort(int[] arr){
          int gap=arr.length>>1;
          while(gap>1){
              insertSortGap(arr,gap);
              gap/=2;
          }
          //整个数组的插入排序
          insertSortGap(arr,1);
      }
      
      private static void insertSortGap(int[] arr, int gap) {
          for(int i=gap;i<arr.length;i++){
              for(int j=i;j-gap>=0&&arr[j-gap]>arr[j];j=j-gap){
                  swap(arr,j,j-gap);
              }
          }
      }
  1. 归并排序:
    1. 思想:归并 -》先拆了才能并 所以归并排序在这里分为两步

      1. 将原数组不断拆分直至每个拆分后的数组只剩一个元素,拆分结束
      2. 将拆分后的数组不断合并 (2排序2->4 4排序4->8) 直至整个数组有序
    2. 辅助代码:当r-l<=15时,在数组arr[l…r]上使用插入排序

 /**
       * 在数组arr[l..r]上使用插入排序
       * @param arr
       * @param l
       * @param r
       */
      private static void insertBase(int[] arr, int l, int r) {
          //有序区间[l..i)
          //无序区间[i..r]
          for (int i = l+1; i <=r ; i++) {
              for (int j = i; j >l&&arr[j]<arr[j-1]; j--) {
                  swap(arr,j,j-1);
              }
          }
      }   
  1. 代码1:
/**
*  归并排序
* @param arr
*/
	public static void mergeSort(int[] arr){
		  mergeSortInternal(arr,0,arr.length-1);
	}
/**
* 在arr[l..r]进行归并排序
* @param arr
*/
	private static void mergeSortInternal(int[] arr, int l, int r) {
		if(r-l<=15){
		    insertBase(arr,l,r);
		    return;
		}
		int mid=l+((r-l)>>1);
		//再拆分的两个数组上使用归并排序
		//先排序左区间
		mergeSortInternal(arr,l,mid);
		//再排续又区间
		mergeSortInternal(arr,mid+1,r);
		//当两个小区间乱序才合并
		if(arr[mid]>arr[mid+1]){
		    merge(arr,l,mid,r);
		}
	}
	
	  /**
	   * 将已经有序的两个小区间合并成一个大的有序数组
	   * @param arr
	   * @param l
	   * @param mid
	   * @param r
	   */
	  private static void merge(int[] arr, int l, int mid, int r) {
	      //开辟一个数组
	      int[] tep=new int[r-l+1];
	      for (int i = l; i <=r ; i++) {
	          tep[i-l]=arr[i];
	      }
	      //遍历元素组,选择作曲兼和有趣间的最小值写入数组
	      //i左半有序区间的第一个索引
	      int i=l;
	      //j右半区间的第一个索引
	      int j=mid+1;
	      for (int k = l; k <=r; k++) {
	          if(i>mid){
	              //此时左半区间已经完全处理完毕,将有版区间所有制写入元素组
	              arr[k]=tep[j-l];
	              j++;
	          }else if(j>r){
	              //此时有伴区间已经处理完毕,作伴写入数组
	              arr[k]=tep[i-l];
	              i++;
	          }else if(tep[i-l]<=tep[j-l]){
	              arr[k]=tep[i-l];
	              i++;
	          }else{
	              arr[k]=tep[j-l];
	              j++;
	          }
	      }
	  }
  1. 代码2:非递归版本
    /**
      
       * 归并排序的非递归版本
       * @param arr
         */
         public static void mergeSortNonRecursion(int[] arr){
         for(int a=1;a<=arr.length;a+=a+a){
             //一个一个两个两个sigesige合并 所以是i+=a+a
             for (int i = 0; i + a<arr.length ; i+=a+a) {
                 merge(arr,i,i+a-1,Math.min(i+2*a-1,arr.length-1));
             }
         }
    }

  1. 快速排序:
    1. 速记 思想:
      1. 关键词:分治 基准
      2. 【】5【】递归

    2. 辅助代码:当r-l<=15时,在数组arr[l…r]上使用插入排序

 /**
       * 在数组arr[l..r]上使用插入排序
       * @param arr
       * @param l
       * @param r
       */
      private static void insertBase(int[] arr, int l, int r) {
         //有序区间[l..i)
          //无序区间[i..r]
          for (int i = l+1; i <=r ; i++) {
              for (int j = i; j >l&&arr[j]<arr[j-1]; j--) {
                  swap(arr,j,j-1);
              }
          }
      }
  1. 代码1:
     public static void quickSort1(int[] arr){
         quickSortInternal(arr,0,arr.length-1);
     }
  /**
       * 基础快速排序
       * @param arr
       * @param l
       * @param r
       */
      public static void quickSortInternal(int[] arr,int l,int r){
          //递归终止时 小数组使用插入排序
          if(r-l<=15){
              insertBase(arr,l,r);
              return;
          }
          int p=partition(arr,l,r);
          //在小于基准的区间进行排序
          quickSortInternal(arr,l,p-1);
          quickSortInternal(arr,p+1,r);
      }
      
      /**
       * 在arr[l..r]上选取基准值,将数组划分为<v,>=v的区间 返回基准值对应的下标
       * @param arr
       * @param l
       * @param r
       * @return
       */
      private static int partition(int[] arr, int l, int r) {
          int randomIndex=random.nextInt(l,r);
          swap(arr,l,randomIndex);
          int v=arr[l];
          //[l+1...j]<  [j+1...i)>=
          int j=l;
          //i是当前要处理元素的下标
          for (int i = l+1; i <=r; i++) {
              if(arr[i]<v){
                  swap(arr,j+1,i);
                  j++;
              }
          }
          swap(arr,l,j);
          return j;
      }
  1. 代码2:双路快排
     public static void quickSort2(int[] arr){
         quickSortInternal2(arr,0,arr.length-1);
     }
     
     private static void quickSortInternal2(int[] arr, int l, int r) {
         if(r-l<=15){
             insertBase(arr,l,r);
             return;
         }
         int p=partition2(arr,l,r);
         //在小于基准的区间进行排序
         quickSortInternal2(arr,l,p-1);
         quickSortInternal2(arr,p+1,r);
     }
     
     private static int partition2(int[] arr, int l, int r) {
         int randomIndex=random.nextInt(l,r);
         swap(arr,l,randomIndex);
         int v=arr[l];
     
         int i=l+1;
         int j=r;
         while(true){
             //j扫描第一个大于等于v的元素
             while(i<=r&&arr[i]<v){
                 i++;
             }
             while(j>=l+1&&arr[j]>v){
                 j--;
             }
             if(i>j){
                 break;
             }
             swap(arr,i,j);
             i++;
             j--;
         }
         //j落在最后一个小于等于v的元素
         swap(arr,l,j);
         return j;
     }
  1. 代码3:三路快排
   public static void quickSort3(int[] arr){
       quickSortInternal3(arr,0,arr.length-1);
   }
   
   private static void quickSortInternal3(int[] arr, int l, int r) {
       if(r-l<=15){
           insertBase(arr,l,r);
           return;
       }
       int randomIndex=random.nextInt();
       swap(arr,l,randomIndex);
       int v=arr[l];
       //arr[l+1...lt]<v;
       int lt=l;
       //arr[gt..r]>v
       int gt=r+1;
       //lt+1..gt ==
       int i=l+1;
       while(i<gt){
           if(arr[i]<v){
               swap(arr,lt+1,i);
               i++;
               lt++;
           }else if(arr[i]>v){
               swap(arr,gt-1,i);
               gt--;
           }else{
               i++;
           }
       }
       swap(arr,l,lt);
       //[l,lt-1]<v
       //[gt,r]>v
       quickSortInternal3(arr,l,lt-1);
       quickSortInternal3(arr,gt,r);
   
   }
  1. 堆排序:
    1. 思想:利用堆进行排序的方法。其基本思想为:将待排序列构造成一个大顶堆(或小顶堆),整个序列的最大值(或最小值)就是堆顶的根结点,将根节点的值和堆数组的末尾元素交换,此时末尾元素就是最大值(或最小值),然后将剩余的n-1个序列重新构造成一个堆,这样就会得到n个元素中的次大值(或次小值),如此反复执行,最终得到一个有序序列。

    2. 代码:

  /**
    * 堆排序
     * @param arr
     */
    public static void heapSort(int[] arr){
        //将任意数组调整为最大堆
        for (int i = (arr.length-1-1)/2; i >=0 ; i--) {
            siftDown(arr,i,arr.length);
        }
    //拿走一个大的之后重新开始调整
        for (int i = arr.length-1; i >0 ; i--) {
            swap(arr,0,i);
            siftDown(arr,0,i);
        }
    }
    
    private static void siftDown(int[] arr, int i, int n) {
        //仍存在子树
        while(2*i+1<n){//存在左子树
            int j=2*i+1;
            if(j+1<n&&arr[j+1]>arr[j]){//存在右子树且右子树大于左子树
                j=j+1;
            }
            if(arr[i]>arr[j]){
                break;
            }else{
                swap(arr,i,j);
                i=j;
            }
        }
    }
  1. 冒泡排序:
    1. 思想:从第一个元素开始,相邻元素进行比较,大的放后面,第一次排序后,倒数第一个元素排好有序,接着进行第二轮排序,倒数第二个元素也有序…

    2. 代码:

   /**
       * 冒泡排序
       * @param arr
       */
      public static void bubbleSort(int[] arr){
          for (int i = 0; i < arr.length-1; i++) {
              boolean b=false;
              for (int j = 0; j < arr.length-1-i; j++) {
                  if(arr[j]>arr[j+1]){
                      b=true;
                      swap(arr,j,j+1);
                  }
              }
              if(!b){
                  break;
              }
      
          }
      
      }

SortHelper

  /**
    * 排序的辅助类
    */
   public class SortHelper {
       //生成随机数的类(线程不安全)
       //通过方法去得对象
       private static final ThreadLocalRandom random=ThreadLocalRandom.current();
   
       /**
        * 生成一个具有n个数的随机数 取值范围 l r
        * @return
        */
       public  static int[] generateRandomArray(int n,int L,int R){
           int[] ret=new int[n];
           for (int i = 0; i < n; i++) {
               ret[i]=random.nextInt(L,R);
           }
           return ret;
       }
       public static int[] generateSortedArray(int n,int times){
           //n个完全有序的数组
           int[] ret=new int[n];
           for (int i = 0; i < n; i++) {
               ret[i]=i;
           }
           //交换任意位置的数字times次
           for (int i = 0; i < times; i++) {
               int index1=random.nextInt(0,n-1);
               int index2=random.nextInt(0,n-1);
               swap(ret,index1,index2);
           }
           return ret;
       }
       //如果排序后的数组不是有序 
       public static boolean isSorted(int[] arr){
           for (int i = 0; i < arr.length-1; i++) {
               if (arr[i] > arr[i + 1]) {
                   System.err.println("排序算法有误");
                   return false;
               }
           }
           return true;
       }
   
       /**
        * 根据排序名称来调用相应的排序算法来对arr做排序处理
        * 反射 在运行过程中动态获取方法或属性
        * @param sortName
        * @param arr
        */
       public static void testSort(String sortName,int[] arr) throws Exception{
           //获取class对象
           Class<SevenSort> cls=SevenSort.class;
           Method method=cls.getDeclaredMethod(sortName,int[].class);
           long start=System.nanoTime();
           //调用这个排序算法
           method.invoke(null,arr);
           long end=System.nanoTime();
           if(isSorted(arr)){
               System.out.println(sortName+"排序完成,共耗时:"+(end-start)/1000000+"ms");
           }
   
       }
       public static int[] arrCopy(int[] arr){
           return Arrays.copyOf(arr,arr.length);
   
       }
       private static void swap(int[] ret, int index1, int index2) {
           int temp = ret[index1];
           ret[index1] = ret[index2];
           ret[index2] = temp;
       }
   }
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值