当前位置:   article > 正文

常见七种排序算法对比(超全!!!)_排序算法比较

排序算法比较


在这里插入图片描述

在这里插入图片描述
怎么判断是不是稳定的排序呢?
如果当前这个序列,在排序的过程中,没有发生跳跃式的交换,那么我们就认为这个排序是稳定的排序。

一, 直接插入排序

特点:效率低,容易实现。
原理:将数组分为两部分,将后部分元素逐一插入前部分有序元素的适当位置
时间复杂度:最好 当数据有序时O(n) 最坏 O(n^2)
空间复杂度 O(1)
稳定的排序

public static void insertSort(int[] array){
        for(int i=1;i<array.length;i++){
            int tmp=array[i];//后部分第一个数
            int j=i-1;
            for(;j>=0;j--){
            if(array[j]>tmp){
                array[j+1]=array[j];
            }else {
                break;
            }

            }
            array[j+1]=tmp;
        }
    }

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16

二,希尔排序

原理:希尔排序(Shell Sort)是插入排序的一种。是针对直接插入排序算法的改进。
先选定一个整数,把待排序文件中所有记录分成个组,所有距离为的记录分在同一组内,并对每一组内的记录进行排序。然后,取,重复上述分组和排序的工作。当到达=1时,所有记录在统一组内排好序。
时间复杂度:最好O(n) 最坏O(n^2)
空间复杂度 O(1)
不稳定的排序
在这里插入图片描述
保证gap是素数,最后一个是1就可以了


public static void shell(int[] array ,int gap) {
    for(int i=gap;i<array.length;i++){
        int tmp=array[i];
    int j=i-gap;
    for(;j>=0;j=j-gap){
        if(array[j]>tmp){
            array[j+gap]=array[j];
        }else{
            break;
        }
    }
    array[j+gap]=tmp;
    }
    }
    
    public static void shellSort(int[] array){
    int[] drr={5,3,1};//增量数组
    for(int i=0;i<drr.length;i++){
    shell(array,drr[i]);
    }
  }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22

三、选择排序

时间复杂度:O(n^2)
空间复杂度 O(1)
不稳定的排序

特点:效率低,容易实现。
原理:就是每次遍历一趟,找出最小的数,放到最前端,直到全部待排序的数据元素排完。 (这里说的是最前,是指无序的队列中的最前)

public static void selectSort(int[] array) {
        for (int i = 0; i < array.length-1; i++) {
            for (int j = i+1; j < array.length; j++) {
                if(array[j] < array[i]) {
                    int tmp = array[j];
                    array[j] = array[i];
                    array[i] = tmp;
                }
            }
        }
    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11

四,堆排序

原理:是选择排序的一种。可以利用数组的特点快速定位指定索引的元素。堆分为大根堆和小根堆,是完全二叉树。大根堆的要求是每个节点的值都不大于其父节点的值。在数组的非降序排序中,需要使用的就是大根堆,因为根据大根堆的要求可知,最大的值一定在堆顶。
时间复杂度:O(logn)
空间复杂度 O(1)
不稳定的排序

public class Priority {
  //从小到大排序
  public static void heapSort(int[] array){
    crateBigHeap(array);
    int end=array.length-1;
    while(end>0){
      int tmp=array[0];
      array[0]=array[end];
      array[end]=tmp;
      adjustDown(array,0,end);
      end--;
    }
  }
  public static void adjustDown(int[] array,int parent, int len) {
    int child = 2 * parent + 1;
    //child<len 说明有左孩子
    while (child < len) {
      // child + 1 < len 判断是 当前是否 有右孩子
      if (child + 1 < len && array[child] < array[child + 1]) {
        child++;//
      }
      // child 下标 一定是 左右孩子的最大值下标
      if (array[child] > array[parent]) {
        int tmp = array[child];
        array[child] = array[parent];
        array[parent] = tmp;
        parent = child;
        child = 2 * parent + 1;
      } else {
        //因为是从最后一棵树开始调整的  只要我们 找到了这个
        // this.elem[child] <= this.elem[parent]   后续就不需要循环了
        //后面的都已经是大根堆了
        break;
      }
    }
  }

  public static void crateBigHeap(int[] array) {
    for (int i = (array.length - 1 - 1) / 2; i >= 0; i--) {
      adjustDown(array,i, array.length);
    }
  }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42

五,冒泡排序

最常见的排序实现起来很简单,不过实际应用很少,复杂度O(n²)。
原理:如果假设长度为n的数组array,按照从小到大排序,首先从数组的第一个元素开始到数组最后一个元素为止,对数组中相邻的两个元素进行比较,如果位于数组左端的元素大于数组右端的元素,则交换这两个元素在数组中的位置,此时数组最右端的元素即为该数组中所有元素的最大值。接着对该数组剩下的n-1个元素进行冒泡排序,直到整个数组有序排列。算法的时间复杂度为O(n^2)。

比较相邻的元素。如果第一个比第二个大,就交换他们两个。
对每一对相邻元素做同样的工作,从开始第一对到结尾的最后一对。在这一点,最后的元素应该会是最大的数。
针对所有的元素重复以上的步骤,除了最后一个。
持续每次对越来越少的元素重复上面的步骤,直到没有任何一对数字需要比较。

时间复杂度:O(n^2)
空间复杂度 O(1)
稳定的排序

 public static void BubbleSort(int []array){
        for(int i=1;i<array.length;i++) {   
            for(int j=0;j<array.length-1;j++) {
                if(array[j]>array[j+1]) {
                   int temp=array[j];
                    array[j]=array[j+1];
                    array[j+1]=temp;
                }
            }
            for(int i=0;i<array.length;i++) {
                System.out.print(array[i]+" ");
            }
            System.out.println();
        }
    } 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15

六,快速排序

特点:高效,时间复杂度最好为O(nlogn)最坏O(n^2)。
空间复杂度:O(logn)
不稳定的排序
原理:1. 从待排序区间选择一个数,作为基准值(pivot)
2. 遍历整个待排序区间,将比基准值小的(可以包含相等的)放到基准值的左边,将比基准值大的(可以包含相等的)放到基准值的右边;
3. 采用分治思想,对左右两个小区间按照同样的方式处理,直到小区间的长度 == 1,代表已经有序,或者小区间的长度 == 0,代表没有数据。

分治思想什么时候效率最高?每次把待排序序列均匀的划分为若干份
在这里插入图片描述

//快速排序
    public static int pivot(int[] array,int start,int end) {
        int tmp = array[start];
        while (start < end) {
            while (start<end&&array[end]>tmp) {
                end--;
            }
            //把数据赋值给start
            array[start]=array[end];
            while (start<end&&array[start]<tmp) {
                start++;
            }
            //把start下标的值给end
            array[end]=array[start];
        }
        array[start] = tmp;
        return start;
    }
    public static void quick(int [] array,int low,int high){
        if(low<high){
           int piv= pivot(array,low,high);
           quick(array,low,piv-1);
           quick(array,piv+1,high);
        }
    }
    public static void main(String[] args) {
        int[] array={12,32,1,3,4,5,45,6,88,77};
        System.out.println(Arrays.toString(array));
        quick(array,0,array.length-1);
        System.out.println(Arrays.toString(array));
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32

如何优化快排呢?

随机选取基准法
做法:随机找到后边的一个下标,然后和Low下标的数据进行交换,然后以low下标交换的值作为基准

三数取中法优化快排

//快速排序
    public static int pivot(int[] array,int start,int end) {
        int tmp = array[start];
        while (start < end) {
            while (start<end&&array[end]>tmp) {
                end--;
            }
            //把数据赋值给start
            array[start]=array[end];
            while (start<end&&array[start]<tmp) {
                start++;
            }
            //把start下标的值给end
            array[end]=array[start];
        }
        array[start] = tmp;
        return start;
    }
    public static void swap(int[] array,int k,int i){
        int tmp=array[k];
        array[k]=array[i];
        array[i]=tmp;
    }
    //快速排序的优化
    public static void medianOfThree(int [] array,int low,int high){
        int mid=(low+high)/2;
        //array[mid]<=array[low]<=array[high]

        if(array[low]<array[mid]){
            //array[mid] <= array[low]
            swap(array,mid,low);
        }
        if(array[low]>array[high]){
            //array[low] <= array[high]
            swap(array,high,low);
        }
        if(array[mid]<array[high]){
            //array[mid] <= array[high]
            swap(array,mid,high);
        }
    }
    public static void quick(int [] array,int low,int high){
        if(low<high){
            medianOfThree(array,low,high);
           int piv= pivot(array,low,high);
           quick(array,low,piv-1);
           quick(array,piv+1,high);
        }
    }
    public static void main(String[] args) {
        int[] array={12,32,1,3,4,5,45,6,88,77};
        System.out.println(Arrays.toString(array));
        quick(array,0,array.length-1);
        System.out.println(Arrays.toString(array));
    }
}

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57

七,归并排序

原理:采用分治法的一个非常典型的应用。将已有序的子序列合并,得到完全有序的序列;即先使每个子序列有序,再使子序列段间有序。若将两个有序表合并成一个有序表,称为二路归并。归并排序是一种稳定的排序方法。
在这里插入图片描述

//合并
public static void merge(int[] array,int start,int mid,int end) {
        int s1 = start;
        int s2 = mid+1;

        int[] tmp = new int[end-start+1];
        int k = 0;//tmp数组的下标

        while (s1 <= mid && s2 <= end) {
            if(array[s1] <= array[s2]) {
                tmp[k++] = array[s1++];
            }else{
                tmp[k++] = array[s2++];
            }
        }
        //有可能第一个段还有数据 有可能第2个段也有数据
        while (s1 <= mid) {
            tmp[k++] = array[s1++];
        }
        while (s2 <= end){
            tmp[k++] = array[s2++];
        }

        for (int i = 0; i < tmp.length; i++) {
            array[i+start] = tmp[i];
        }
    }
//分解
    public static void mergeSortInternal(int[] array,int low,int high) {
        if(low >= high) return;
        int mid = (low+high)/2;
        mergeSortInternal(array,low,mid);
        mergeSortInternal(array,mid+1,high);
        //合并的操作
        merge(array,low,mid,high);
    }
public static void mergeSort(int[] array){
        mergeSortinternal(array,0,array.length-1);
    }
    
    public static void main(String[] args) {
        int[] array={12,32,1,3,4,5,45,6,88,77};
        System.out.println(Arrays.toString(array));
        mergeSort(array);
        System.out.println(Arrays.toString(array));
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/花生_TL007/article/detail/411829
推荐阅读
相关标签
  

闽ICP备14008679号