0
点赞
收藏
分享

微信扫一扫

重学常见排序算法

火热如冰 2022-03-30 阅读 38

最常见的有冒(冒泡)择(选择)路(插入)希(希尔)快(快速)归(归并)堆(堆排序)。 这一次把所有的常见的排序熟悉一遍,后面就一劳永逸了。

冒泡算法

连接:https://www.bilibili.com/video/BV1Mi4y1k7QT/

冒泡排序算法的文字描述:

  1. 一次比较数组中相邻两个元素大小,若 a[j] > a[j+1] , 则交换两个元素,两两都比较一遍为一轮排序,结果是让最大的元素排至最后。
  2. 重复以上步骤,指导整个数组有序。

先实现一版最简单。

import java.util.Arrays;
public class BubbleSort {

    /**
     * 用来记录比较的次数。
     * */
    private static int compareCnt = 0 ;

    /**
     * 用来记录交换的次数。
     * */
    private static int swapCnt = 0 ;

    /**
     * 用来记录冒泡的次数的次数。
     * */
    private static int bubleCnt = 0 ;

    public static void sort(int[] array){
        boolean isSwap = Boolean.TRUE;

        for (int i = 0; i < array.length -1 ; i++) {
            bubleCnt++;
            for(int j = 0 ; j < array.length -1 ;j++){
                if(compare(array[j] , array[j+1]) ){
                    swap(array,j,j+1);
                }
            }
        }
    }
    public static boolean compare(int a , int b){
        compareCnt++;
        return a > b ;
    }
    public static void swap(int[] array , int i , int j){
        swapCnt++;
        int swap = 0 ;
        swap = array[i];
        array[i] = array[j];
        array[j] = swap;
    }
    public static void main(String[] args) {
        int[] a = { -1 , -2 ,  8 , 3 ,4 ,2 ,1};

        System.out.println(Arrays.toString(a));
        sort(a);
        System.out.println("compareCnt:" + compareCnt);
        System.out.println("swapCnt:" + swapCnt);
        System.out.println("bubleCnt:" + bubleCnt);
        System.out.println(Arrays.toString(a));
    }
}

执行上面的代码,得到如下结果:

接下来,我想一个问题,我们经过第一躺的结果是将最大的数字排到最后一个位置。就像赶羊,把最大个的羊感到最后的位置。这里有个视频讲解。所以经过 n 次冒泡后,倒数 n 个位置上,都是有序的,这样的话就不需要比较了。所以代码改成下面的样子,

import java.util.Arrays;
public class BubbleSort {

    /**
     * 用来记录比较的次数。
     * */
    private static int compareCnt = 0 ;

    /**
     * 用来记录交换的次数。
     * */
    private static int swapCnt = 0 ;

    /**
     * 用来记录冒泡的次数的次数。
     * */
    private static int bubleCnt = 0 ;

    public static void sort(int[] array){
        boolean isSwap = Boolean.TRUE;

        for (int i = 0; i < array.length -1 ; i++) {
            bubleCnt++;
            // 改了这里
            for(int j = 0 ; j < array.length -1 - i;j++){
                if(compare(array[j] , array[j+1]) ){
                    swap(array,j,j+1);
                }
            }
        }
    }
    public static boolean compare(int a , int b){
        compareCnt++;
        return a > b ;
    }
    public static void swap(int[] array , int i , int j){
        swapCnt++;
        int swap = 0 ;
        swap = array[i];
        array[i] = array[j];
        array[j] = swap;
    }
    public static void main(String[] args) {
        int[] a = { -1 , -2 ,  8 , 3 ,4 ,2 ,1};

        System.out.println(Arrays.toString(a));
        sort(a);
        System.out.println("compareCnt:" + compareCnt);
        System.out.println("swapCnt:" + swapCnt);
        System.out.println("bubleCnt:" + bubleCnt);
        System.out.println(Arrays.toString(a));
    }
}

我们在 j 的右边界做了优化,每次比较移动都回减小 1 ,排好序的部分就不会在进行比较了。下面展示一下执行的效果,可以看到比较的次数减少了。

其实,还有一个特殊的情况,如果在做完一个冒泡之后,正好和原来有序的数列连成片,那是不是就可以减少冒泡的次数了。

public class BubbleSort {

    /**
     * 用来记录比较的次数。
     * */
    private static int compareCnt = 0 ;

    /**
     * 用来记录交换的次数。
     * */
    private static int swapCnt = 0 ;

    /**
     * 用来记录冒泡的次数的次数。
     * */
    private static int bubleCnt = 0 ;

    public static void sort(int[] array){
        boolean isSwap = Boolean.TRUE;
        // 最后一次交换的位置
        int lastSwapPosition = 0 ;

        for (int i = 0; i < array.length -1 ; i++ ) {
            bubleCnt++;
            for(int j = 0 ; j < array.length -1 - i ;j++){
                if(compare(array[j] , array[j+1]) ){
//                    isSwap = Boolean.TRUE;
                    swap(array,j,j+1);
                    lastSwapPosition = j + 1;
                }else {
//                    isSwap = Boolean.FALSE ;
                }
            }
//            i += array.length - lastSwapPosition - 1;
        }
    }
    public static boolean compare(int a , int b){
        compareCnt++;
        return a > b ;
    }
    public static void swap(int[] array , int i , int j){
        swapCnt++;
        int swap = 0 ;
        swap = array[i];
        array[i] = array[j];
        array[j] = swap;
    }
    public static void main(String[] args) {
        int[] a = { -1 , -2 ,  8 ,2 ,1 , 3 , 4};

        System.out.println(Arrays.toString(a));
        sort(a);
        System.out.println("compareCnt:" + compareCnt);
        System.out.println("swapCnt:" + swapCnt);
        System.out.println("bubleCnt:" + bubleCnt);
        System.out.println(Arrays.toString(a));
    }
}

修改后的代码:

public class BubbleSort {

    /**
     * 用来记录比较的次数。
     * */
    private static int compareCnt = 0 ;

    /**
     * 用来记录交换的次数。
     * */
    private static int swapCnt = 0 ;

    /**
     * 用来记录冒泡的次数的次数。
     * */
    private static int bubleCnt = 0 ;

    public static void sort(int[] array){
        boolean isSwap = Boolean.TRUE;
        // 最后一次交换的位置
        int lastSwapPosition = 0 ;

        for (int i = 0; i < array.length -1 ; ) {

//            if(!isSwap){
//               break;
//            }
            bubleCnt++;
            for(int j = 0 ; j < array.length -1 - i ;j++){
                if(compare(array[j] , array[j+1]) ){
//                    isSwap = Boolean.TRUE;
                    swap(array,j,j+1);
                    lastSwapPosition = j + 1;
                }else {
//                    isSwap = Boolean.FALSE ;
                }
            }
            i += array.length - lastSwapPosition - 1;
        }
    }
    public static boolean compare(int a , int b){
        compareCnt++;
        return a > b ;
    }
    public static void swap(int[] array , int i , int j){
        swapCnt++;
        int swap = 0 ;
        swap = array[i];
        array[i] = array[j];
        array[j] = swap;
    }
    public static void main(String[] args) {
        int[] a = { -1 , -2 ,  8 ,2 ,1 , 3 , 4};

        System.out.println(Arrays.toString(a));
        sort(a);
        System.out.println("compareCnt:" + compareCnt);
        System.out.println("swapCnt:" + swapCnt);
        System.out.println("bubleCnt:" + bubleCnt);
        System.out.println(Arrays.toString(a));
    }
}

执行结果如下所示:

这次输入的数组特点是 3 和 4 是有序的,而且可以和 8 连成片。经过优化后,bubleCnt 的次数减少了三次。

我们可以把此数组看成两个不断变化的数组,可能经过几次排序之后,其实无序的数组已经有序,只是程序不知道而已,问题的关键在于让程序知道无序数组已经是有序的,可以设置一个变量来记录在无序数组中是否发生了狡猾,如果没有发生交换 ,则说明无序的数列就是有序的了。就不需要进行后面的冒泡了。

选择排序

连接:https://www.bilibili.com/video/BV18q4y1Y7AE/

过程描述:

代码实现:

import java.util.Arrays;

public class SelectSort {
    private int[] array ;
    public SelectSort(int[] array){
        this.array = array;
    }
    public void sort(){
        int minIndex = 0 ;
        int temp = 0 ;
        for (int i = 0; i < this.array.length - 1 ; i++) {
            temp = array[i];
            minIndex = i+1;
            for(int j = i+1 ; j < array.length ; j++){
                if(temp > array[j]){
                    minIndex = j ;
                    temp = array[j];
                }
            }
            if(temp < array[i]){
                swap(minIndex , i);
            }
        }
    }

    private void swap(int minIndex, int i) {
        int temp = 0 ;
        temp = array[minIndex];
        array[minIndex] = array[i];
        array[i] = temp;
    }

    @Override
    public String toString() {
        return "SelectSort{" +
                "array=" + Arrays.toString(array) +
                '}';
    }

    public static void main(String[] args) {
        SelectSort ss = new SelectSort(new int[]{1, 22, 11, 23, 32, 43});
        System.out.println("the original order:");
        System.out.println(ss.toString());
        ss.sort();
        System.out.println("the ordered array:");
        System.out.println(ss.toString());
    }
}

插入排序

连接:https://www.bilibili.com/video/BV19L411w7XM/
插入排序的过程描述:

代码:

import java.util.Arrays;

public class InsertSort {

    public static void main(String[] args) {
        int[] a = { -1 , -2 ,  8 ,2 ,1 , 18 , 21 , 20 , 3 , 4};
        sort(a);
        System.out.println("swapCnt:" + swapCnt);
    }
    /**
     * 交换的次数
     * */
    private static int swapCnt = 0 ;
    public static void sort(int[] array){
        if(array.length <= 1){
            return;
        }
        int insertPosition = 0 ;
        int temp = 0 ;
        for (int i = 0; i < array.length; i++) {
            System.out.print("index:" + i + "====>");
            temp = array[i];
            insertPosition = findInsertPosition( array , i);
            moveAndInsert(array , insertPosition , i , temp );
            System.out.println(Arrays.toString(array));
        }
    }
    private static void swap(int[] array , int from , int to){
        swapCnt++;
        int temp = array[from];
        array[from] = array[to];
        array[to] = temp;
    }
    /**
     * 移动有序子集,给插入元素挪开地方,并将元素插入到相关的位置
     * */
    private static void moveAndInsert(int[] array, int insertPosition , int bound, int temp) {
        for (int i = bound; i > insertPosition  ; i--) {
              swap(array,i , i-1);
        }
        array[insertPosition] = temp ;
    }


    /**
     * 找到插入的位置
     * */
    private static int findInsertPosition(int[] array, int sortedBound) {
        for (int i = 0; i <= sortedBound; i++) {
            if(array[i] >= array[sortedBound]){
               return i ;
            }
        }
        return 0;
    }
}

希尔排序

希尔排序是堆插入排序的改进,在插入排序中,如果大的元素都在后面的话,移动元素的次数会小很多,但是如果较大元素放到前面,例如放到了第一个位置,则需要移动 n - 1 次移动。如果我们能够又一种方法将降低这样的移动次数,是不是就可以达到优化的效果了。这个方法和跳表的方法差不多,我们先选择一个 interval(步长),使用步长选择出一个子集,然后对子集做插入排序。

如上图所示,通过步长选子集的方式,就把前后的元素直接的放到了一起,这样就能减少移动的次数。

https://www.bilibili.com/video/BV1M44y1K7S1/

希尔排序的代码,我使用链表实现,但是太过复杂,还没有写好,所以就不再这里贴出来了。另外,不会考代码实现。

归并排序

连接:https://www.bilibili.com/video/BV1bP4y1T7je/

定义:

  1. 使用二分法,把集合两个集合,然后再对这两个集合做同样的切分,直到每个集合中只要一个元素
  2. 然后将相邻两个集合合并排序,并将保证有序性
  3. 从下至上,重复 2 的操作

代码:

import java.util.Arrays;

public class MergeSort {
    public static void main(String[] args) {
        int[] A1 = {4 , 6 , 1 , 8 , 3 , -1};
        int[] A2 = { 9 , 17 , 34 , 22 , 90};
        int[] merge = {4 , 6 , 1 , 8 , 3 , -1 , 9 , 17 , 34 , 22 , 90};
        System.out.println(Arrays.toString(sort(merge , 0 , merge.length-1)));
    }
    public static int[] sort(int[] array , int from , int to){
        int[] rs = null ;
        if(from == to){
            rs = new int[1];
            rs[0] = array[from];
            return rs ;
        }
        int mid = (from + to)/2;
        int[] firstHalf = sort(array, mid + 1  , to);
        int[] secordHafl = sort(array , from ,  mid);

        return merge(firstHalf , secordHafl);
    }
    public static int[] merge( int[] A1 , int[] A2){
        int[] rs = new int[A1.length + A2.length];
        int i = 0 ;
        int j = 0 ;
        int a = 0 ;
        while(i < A1.length && j < A2.length){
           if(A1[i] <= A2[j]){
               rs[a++] = A1[i++];
           }else{
               rs[a++] = A2[j++];
           }
        }
        while(i < A1.length){
           rs[a++] = A1[i++];
        }
        while (j < A2.length){
            rs[a++] = A2[j++];
        }
        return rs ;
    }
}

合并排序的优点:

它在合并的时候,会利用原来有序性来,避免不必要的比较和移动

缺点:

需要使用额外的存储

快速排序

连接:

定义:

  1. 任意选择一个元素,令所有大于它的元素都放到它的右面,所有小于它的元素都放到它的左面,
  2. 在选定元素的左边集合中,重复 1 .
    3.在选定元素的右边集合中,重复 1
  3. 重复 1 ~3 的过程。直到递归到最小的子集。再一一返回。

代码:

import java.util.Arrays;

public class QuikSort {
    public static void main(String[] args) {
        int[] A1 = {4 , 6 , 1 , 8 , 3 , -1};
        int[] A2 = { 9 , 17 , 34 , 22 , 90};
        int[] merge = {4 , 6 , 1 , 8 , 3 , -1 , 9 , 17 , 34 , -33 , 1 ,  22 , 90 , -100};
        sort(merge , 0 , merge.length - 1);
        System.out.println(Arrays.toString(merge));;
    }
    public static void swap(int[] array , int from , int to){
        int temp = array[from];
        array[from] = array[to];
        array[to] = temp ;
    }
    public static void sort(int[] array , int from , int to){
         if(from >= to){
             return;
         }
         int i = from + 1;
         int j = to ;
         int key = array[from];
         while(i < j){
             // 从左到右找到第一比 key 小的
             while(key <= array[j] && j > i){
                 j--;
             }
             // 从右到左第一个比 key 大的
             while(key > array[i] && j > i){
                i++;
             }
             if(i<j){
                 swap(array , i , j);
                 i++;
                 j--;
             }
         }
         if(array[i] <= key){
           swap(array , from , i);
         }else{
             swap(array , from , i -1);
         }
         // 接收为 i -1  i 的位置其实有序了,不需要移动了,所以不需要参与到下次的排序了。
         sort(array , from , i - 1 );
         // 同理,开始的位置事 i+1 
         sort(array , i+1 , to);
    }
}
举报

相关推荐

0 条评论