速过8大排序算法(内含冰冰美图 :D)下

349 阅读2分钟

万里长江共饮水,此生也算亲过嘴

下篇的排序算法相对于上篇都是较为困难的,需要一些时间静下来看

快速排序

冒泡排序的一种改进,通过一趟排序将要排序的数据分割成独立的两部分,其中一部分的所有数据都比另外一部分的所有数据都要小

import java.util.Arrays;

public class QuickSort {
    public static void main(String[] args) {
        int[] arr = {3,2,5,5,2,7};
        System.out.println(Arrays.toString(sort(arr,0,arr.length-1)));
    }

    public static int[] sort(int[] arr, int left, int right) {
        if (left < right) {
            // 获取中轴元素的下标
            int mid = partition(arr, left, right);
            // 对数组进行分割
            arr = sort(arr, left, mid - 1);
            arr = sort(arr, mid + 1, right);
        }
        return arr;
    }

    public static int partition(int[] arr, int left, int right) {
        //设置基准点,就是最左边的点
        int pivot = arr[left];
        int i = left + 1;
        int j = right;

        while (true) {
            //从左往右走,直到当前数大于基准点
            while (i <= j && arr[i] <= pivot) i++;
            while (i <= j && arr[j] > pivot) j--;
            //是否有交换的条件
            if (i > j) {
                break;
            }
            int temp = arr[i];
            arr[i] = arr[j];
            arr[j] = temp;

            i++;
            j--;
            if (i > j) {
                break;
            }
        }

        arr[left] = arr[j];
        // 将中轴元素置于有序位置
        arr[j] = pivot;

        return j;

    }
}

归并排序

将一一些小的问题分解成多个小的问题,通过解决小的问题从而解决大的问题

在下述案例中,主要的思想就是先将粒度不断细化,然后再进行比较,由于大粒度之间的比较是在小粒度的比较之后进行的,所以可以保证每个大粒度之间都是排好序的.这就是要求小粒度之间要有比较,排好序. divide阶段

merge阶段(最后一次的示意图)

import java.util.Arrays;

public class MergeSort {
    public static void main(String[] args) {
        int[] arr = {8,4,5,7,1,3,6,2};
        int[] temp = new int[arr.length];
        divide(arr, 0, arr.length - 1, temp);
        System.out.println(Arrays.toString(arr));
    }

    public static void divide(int[] arr,int left,int right,int[] temp){
        if(left < right){
            int middle = (left + right) / 2;
            divide(arr, left, middle, temp);
            divide(arr, middle + 1, right, temp);
            merge(arr, left, middle, right, temp);
        }

    }


    /**
     * @param arr
     * @param left
     * @param middle
     * @param right
     * @param temp
     */
    public static void merge(int arr[], int left, int middle, int right, int[] temp) {
        int i = left;
        int j = middle + 1;
        int index = 0;

        while(i<=middle && j<=right){
            if(arr[i]<=arr[j]){
                temp[index] = arr[i];
                index++;
                i++;
            }else{
                temp[index] = arr[j];
                index++;
                j++;
            }
        }

        //上边的结束条件是有一边已经全部添加到temp了,判断哪边为空
        while (i <= middle){
            temp[index] =arr[i];
            index++;
            i++;
        }

        while (j <= right){
            temp[index] =arr[j];
            index++;
            j++;
        }

        //到了这里后,temp就是一个已经在[left,right]范围内排好序的数组,赋值给arr,让其数组有序
        index = 0;
        int tempLeft = left;
        while(tempLeft <= right){
            arr[tempLeft] = temp[index];
            tempLeft++;
            index++;
        }
    }
}

基排序

分配式排序或叫桶排序,看图比较直观 下面是尚硅谷关于桶排序的示意图

import java.util.Arrays;

public class RadixSort {
    public static void main(String[] args) {
        int[] arr = {2, 6, 1, 7, 11, 7, 66, 2, 7, 9};
        sort(arr);
        System.out.println(Arrays.toString(arr));
    }

    public static void sort(int arr[]) {
        int max = arr[0];
        for (int i = 1; i < arr.length; i++) {
            if (arr[i] > max) {
                max = arr[i];
            }
        }
        String tem = max + "";
        //获取到最大的长度
        int maxDigit = tem.length();

        //定义二位数组桶,表示10个桶,每个桶应该容量为arr.length,防止溢出
        int[][] bucket = new int[10][arr.length];


        //定义一个每个桶存放个数的数组,用来
        int[] bucketElementCounts = new int[10];

        for (int i = 0, n = 1; i < maxDigit; i++, n *= 10) {
            for (int j = 0; j < arr.length; j++) {
                int digitElementData = arr[j] / n % 10;
                //放入到对应的桶
                bucket[digitElementData][bucketElementCounts[digitElementData]] = arr[j];
                bucketElementCounts[digitElementData]++;
            }
            //按照这个桶的排序进行拿取顺序
            int index = 0;

            //遍历每一个桶,按照从0开始往后拿取
            for (int k = 0; k < bucketElementCounts.length; k++) {
                //如果桶中有数据,就放入数组中
                if (bucketElementCounts[k] != 0) {
                    for(int z=0;z<bucketElementCounts[k];z++){
                        //取出元素放到arr
                        arr[index++] = bucket[k][z];
                    }
                }
                bucketElementCounts[k] = 0;
            }
        }
    }
}

堆排序

需要了解大顶堆和小顶堆,根据大顶堆和小顶堆的性质从而获得升序或者降序的数组

大顶堆:父节点大于两个子结点 小顶堆:父节点小于两个子结点

重要公式

  • 大顶堆:arr[i] >= arr[2i+1] && arr[i] >= arr[2i+2]
  • 小顶堆:arr[i] <= arr[2i+1] && arr[i] <= arr[2i+2]
  • 最后一个非叶子结点: (n-1)/2

堆排序的基本思想是:

  1. 将待排序序列构造成一个大顶堆
  2. 此时,整个序列的最大值就是堆顶的根节点。
  3. 将其与末尾元素进行交换,此时末尾就为最大值。
  4. 然后将剩余 n-1 个元素重新构造成一个堆,这样会得到 n 个元素的次小值。如此反复执行,便能得到一个有序序列了。 可以看到在构建大顶堆的过程中,元素的个数逐渐减少,最后就得到一个有序序列了.

public class HeapSort {
    public static void main(String[] args) {
        int[] arr ={2,6,1,7,11,7,66,2,7,9,-1};
        MinHeap_Sort(arr,arr.length);
        System.out.println(Arrays.toString(arr));
    }

    //构建最小堆
    public static void MakeMinHeap(int a[], int n){
        for(int i=(n-1)/2 ; i>=0 ; i--){
            MinHeapFixdown(a,i,n);
        }
    }
    //从i节点开始调整,n为节点总数 从0开始计算 i节点的子节点为 2*i+1, 2*i+2
    public static void MinHeapFixdown(int a[],int i,int n){

        int j = 2*i+1; //子节点
        int temp = 0;

        while(j<n){
            //首先判断是否有左右子树,如果没有的话是不会进这个循环的
            //在左右子节点中寻找最小的
            if(j+1<n && a[j+1]<a[j]){
                j++;
            }


            if (a[i] <= a[j]) {
                //证明当前的父节点确实是最小的了
                break;
            }

            //较大节点下移
            //说明父节点不是最小的,所以需要交换
            temp = a[i];
            a[i] = a[j];
            a[j] = temp;

            //如果交换了位置,可能导致底部的又混乱了,需要在排序一次
            i = j;
            j = 2*i+1;
        }
    }
    public static void MinHeap_Sort(int a[],int n){
        int temp = 0;
        MakeMinHeap(a,n);

        for(int i=n-1;i>0;i--){
            temp = a[0];
            a[0] = a[i];
            a[i] = temp;
            MinHeapFixdown(a,0,i);
        }
    }


}