十大排序

(5)归并排序

递归代码代码来自

    public int[] sort(int[] sourceArray) throws Exception {
        // 对 arr 进行拷贝,不改变参数内容
        int[] arr = Arrays.copyOf(sourceArray, sourceArray.length);
        //当只有一个元素的时候,返回
        if (arr.length < 2) {
            //此时分成了只有一个元素的数组,
            //System.out.println( arr[0]);
            return arr;
        }
        //向下取整
        int middle = (int) Math.floor(arr.length / 2);
        //一分为二
        //左边部分
        int[] left = Arrays.copyOfRange(arr, 0, middle);
        //右边部分
        int[] right = Arrays.copyOfRange(arr, middle, arr.length);
        //将左边和右边,分别“重复”进行sort这个操作之后,合并(merge中排序)
        return merge( sort( left ), sort( right ) );
        //归并的第一个核心思想,分治进行合并
    }

    protected int[] merge(int[] left, int[] right) {
        //装两个数组的新数组
        int[] result = new int[left.length + right.length];
        int i = 0;
        //新的数组,下面进行的操作就是,依次挑选两数组种较小的开头元素(挑完就去掉那个元素),
        //按顺序放入新数组
        //这里有一个前提,就是这两个数组都已经排序好了(为什么?因为其中的一个数组,也是通过两个均只有一个元素的数组,进行如下排序得来的)
        while (left.length > 0 && right.length > 0) {
            //如果左数组第一个元素小于右数组的第一个元素
            if (left[0] <= right[0]) {
                //每次将left的第一个数组元素赋值给result
                result[i++] = left[0];
                //然后left得到“去掉“第一个元素的新数组
                left = Arrays.copyOfRange(left, 1, left.length);
            } else {
                //如果左数组第一个元素大小于右数组的第一个元素
                //每次将right的第一个数组元素赋值给result
                result[i++] = right[0];
                //然后right得到“去掉“第一个元素的新数组
                right = Arrays.copyOfRange(right, 1, right.length);
            }
            //这样就解释了归并排序的另一核心思想,排序
            //新数组每次挑选两个数组中较小的开头元素(挑选完之后,那个数组去掉被挑选的数,然后让下一个数成为开头元素)
            //这样导致新数组有序(两个数组中的元素全部放到了新数组中,这些元素有序的放在了新数组中)
        }
        
        //后面收尾工作,因为上面的比较中,可能有一个数组有所剩余
        //原因: 一个数组中的元素都比另外一个数组中的元素大(极端例子)
        
        while (left.length > 0) {
            result[i++] = left[0];
            left = Arrays.copyOfRange(left, 1, left.length);
        }

        while (right.length > 0) {
            result[i++] = right[0];
            right = Arrays.copyOfRange(right, 1, right.length);
        }

        return result;
    }

优质代码代码来自

    static void merge(int[] arr, int start, int end) {
        if (start == end) return;
        int mid = (start + end) / 2;
        merge(arr, start, mid);
        merge(arr, mid + 1, end);
        int[] temp = new int[end - start + 1];
        int i = start, j = mid + 1, k = 0;
        while(i <= mid && j <= end)
            temp[k++] = arr[i] < arr[j] ? arr[i++] : arr[j++];
        while(i <= mid)
            temp[k++] = arr[i++];
        while(j <= end)
            temp[k++] = arr[j++];
        //System.arraycopy(temp, 0, arr, start, end - start + 1);
        // 下面代码和上面代码实现的是一样的
        for (int i1 = 0; i1 < temp.length; i1++) {
            arr[start + i1] = temp[i1];
        }
    }

(7)堆排序

三个核心思想,
(1)构造大根堆 buildMaxHeap

    // 构造大根堆
    // 这里最“起码”能将最大的数字上浮至堆顶,
    public void buildMaxHeap(int[] a, int heapSize) {
        for (int i = heapSize / 2; i >= 0; --i) {
            maxHeapify(a, i, heapSize);
        }
    }

为什么说“起码”能将最大的数字上浮至堆顶,又为什么是 heapSize/2 : 0 (从heapSize / 2 到 0
看完后面应该知道为什么是heapSize / 2 : 0, 而不是 0 : heapSize / 2)

注意画红线的点,maxHeapify会有解释。

从heapSize / 2 到 0,兼顾了所有节点(没划红线的不就没有兼顾吗),往下看maxHeapify操作。

(2)maxHeapify

    // 当前index和它的左右子树进行的操作。
    // (1)挑出,当前左节点left(left = 2 * i + 1)指向的数字,当前右节点right( right = 2 * i + 2)指向的数字,中较大的,
    //     与largest( = index )进行交换
    // (2)然后让largest( = max(left,right))指向的元素和其左右子树进行交换,重复(1),直到当前指向没有左右子树,或者左右子树都比当前指向的数字小
    public void maxHeapify(int[] a, int i, int heapSize) {
        int l = i * 2 + 1, r = i * 2 + 2, largest = i;
        // (1)挑出,当前左节点l指向的数字,当前右节点r指向的数字,中较大的,
        //     与largest进行交换
        // (2)然后让largest指向的元素和heapSize指向的元素进行比较
        //     进行(1),直到
        if (l < heapSize && a[l] > a[largest]) {
            largest = l;
        }
        if (r < heapSize && a[r] > a[largest]) {
            largest = r;
        }
        if (largest != i) {
            swap(a, i, largest);
            maxHeapify(a, largest, heapSize);
        }
    }

先来看一个简单的下沉,上浮。

当前指向了4,4会和左右节点进行比较,一直进行下沉(满足条件一直下沉),数字大的节点会上浮

解释

这样一来,如果满足条件,就会一直下沉,数字大的上浮。

这两张图应该就能说明兼顾了叶子节点(因为当前节点会和左右节点进行比较)。

(3)heapSort 使用一次(1) 循环使用(2)
将堆顶数字和heapSize指向的数字(从代码中能看出它是在变的)进行交换。首先从(1)(2)知道了堆顶的数字一定的最大的(这里讲的是升序)。
交换之后,最大的数字(没排序的数字里面算最大)就放在了heapSize个位置。重复这个步骤,就是在进行排序。

    public int [] heapSort(int[] nums) {
        int heapSize = nums.length;
        // 先简单构造一份大根堆
        buildMaxHeap(nums, heapSize);
        for (int i = nums.length - 1; i >= 0; --i) {
            // 每次用堆顶元素和“最后”一个数字进行交换(第一次堆顶和倒数一个数字进行交换,
            // 第二次堆顶和倒数第二个数字交换,因为倒数第一个数字已经在最终位置了)
            swap(nums, 0, i);
            --heapSize;
            maxHeapify(nums, 0, heapSize);
        }
        return nums;
    }

完整代码

    public int [] heapSort(int[] nums) {
        int heapSize = nums.length;
        // 先简单构造一份大根堆
        buildMaxHeap(nums, heapSize);
        for (int i = nums.length - 1; i >= 0; --i) {
            // 每次用堆顶元素和“最后”一个数字进行交换(第一次堆顶和倒数一个数字进行交换,
            // 第二次堆顶和倒数第二个数字交换,因为倒数第一个数字已经在最终位置了)
            swap(nums, 0, i);
            --heapSize;
            maxHeapify(nums, 0, heapSize);
        }
        return nums;
    }

    // 构造大根堆
    public void buildMaxHeap(int[] a, int heapSize) {
        for (int i = heapSize / 2; i >= 0; --i) {
            // 这里最“起码”能将最大的数字上浮至堆顶,
            maxHeapify(a, i, heapSize);
        }
    }

    // 当前index和它的左右子树进行的操作。
    // (1)挑出,当前左节点left(left = 2 * i + 1)指向的数字,当前右节点right( right = 2 * i + 2)指向的数字,中较大的,
    //     与largest( = index )进行交换
    // (2)然后让largest( = max(left,right))指向的元素和其左右子树进行交换,重复(1),直到当前指向没有左右子树,或者左右子树都比当前指向的数字小
    public void maxHeapify(int[] a, int i, int heapSize) {
        int l = i * 2 + 1, r = i * 2 + 2, largest = i;
        // (1)挑出,当前左节点l指向的数字,当前右节点r指向的数字,中较大的,
        //     与largest进行交换
        // (2)然后让largest指向的元素和heapSize指向的元素进行比较
        //     进行(1),直到
        if (l < heapSize && a[l] > a[largest]) {
            largest = l;
        }
        if (r < heapSize && a[r] > a[largest]) {
            largest = r;
        }
        if (largest != i) {
            swap(a, i, largest);
            maxHeapify(a, largest, heapSize);
        }
    }

(8)计数排序

package 十大排序;

public class CountingSort {

    public int[] sort(int []A) {
        // 获得最大和最小的值
        int maxValue = A[0];
        int minValue = A[0];
        for (int i = 0; i < A.length; i++) {
            maxValue = Math.max(maxValue, A[i]);
            minValue = Math.min(minValue, A[i]);
        }
        return countingSort(A, minValue, maxValue);
    }

    public int getMaxValue(int []A) {
        int maxNum = A[0];
        for (int i = 0;i < A.length; i++) {
            maxNum = Math.max(A[i], maxNum);
        }
        return maxNum;
    }

    public int []countingSort(int []A, int minValue, int maxValue) {
        int bucketPosLen = maxValue + 1;
        int bucketNegLen = -1 * minValue + 1;

        int []bucketPos = new int[bucketPosLen];
        int []bucketNeg = new int[bucketNegLen];

        for (int value : A) {
            if (value >= 0) {
                bucketPos[value]++;
            } else{
                bucketNeg[-1 * value]++;
            }
        }

        int sortIndex = 0;

        // 开始排序从0至最大的数字进行排序,如果有那个数字,则数字出现的次数减少1

        for (int i = minValue; i < 0; i++) {
            while (bucketNeg[-1 * i] > 0) {
                A[sortIndex++] = i;
                bucketNeg[-1 * i]--;
            }
        }

        for (int i = 0; i < bucketPosLen; i++) {
            while (bucketPos[i] > 0) {
                A[sortIndex++] = i;
                bucketPos[i]--;
            }
        }
        return A;
    }

    public static void main(String[] args) {
        int []A = {-1, 3 , 1, 3, 1, 9 , 3 , -100, 8288};
        A = new CountingSort().sort(A);
        for (int i = 0; i < A.length; i++) {
            System.out.print(A[i] + " ");
        }
    }
}

(9)桶排序

(10)基数排序

package 十大排序;


import java.util.Arrays;

public class RadixSort {

    public void sort(int []A) {
        // 获得最大数字的位数
        int maxDigit = getNumLength(getMaxDigit(A));
        radixSort(A, maxDigit);
    }

    // 获取最高位数
    public int getMaxDigit(int []A) {
        int maxValue = A[0];
        for (int i = 1; i < A.length; i++) {
            maxValue = maxValue > A[i] ? maxValue : A[i];
        }
        return maxValue;
    }

    // 获取数字的长度
    public int getNumLength(long num) {
        int sum = 0;
        while (num > 0) {
            num /= 10;
            sum++;
        }
        // 判断sum为零的情况
        return sum == 0 ? 1 : sum;
    }

    public void radixSort(int []A, int maxDigit) {
        // 取模用的
        int mod = 10;
        // 结合mod,用来取得对应位置上的数字
        int dev = 1;

        for (int i = 0; i < maxDigit; i++, dev *= 10, mod *= 10) {
            // 考虑为负数的情况,这里拓展为一倍队列数,其中【0-9】对应负数,
            // 【10-19】对应为正数(bucket + 10)

            int [][] counter = new int[mod * 2][0];
            for (int j = 0; j < A.length; j++) {
                // 这里采用的是最低位,有的方法可能采取最高位(从高至低,从低至高)
                int bucket = ((A[j] % mod) / dev) + mod;
                // 根据当前数字上的对应的位上的数字,将数字放入相应的桶中
                // 比如34,现在计算到了第一位,于是将34放到13对应的桶中,下一次计算到了第二位,于是将34放到14对应的桶中
                counter[bucket] = arrayAppend(counter[bucket], A[j]);
                // 在当前行数组后面添加元素
                // 可能有的数字特立独行,特别长,但是大部分数字都很短,但是这样并不影响,想想为什么?
                // 因为那些短的数字已经排好了序,再取出来也是哪个顺序,所以并不会有所影响
            }

            int pos = 0;

            // 查看桶的变化
            for (int j = 0; j < counter.length; j++) {
                System.out.print(j + " ");
                for (int k = 0; k < counter[j].length; k++) {
                    System.out.print(counter[j][k] + " ");
                }
                System.out.println();
            }

            // 从桶将数据取出来,然后放到原数组中,
            for (int []bucket : counter) {
                for (int value : bucket) {
                    A[pos++] = value;
                }
            }

        }

    }

    /**
     * 自动扩容,并保存数据
     */
    public int[] arrayAppend(int []A, int value) {
        A = Arrays.copyOf(A, A.length + 1);
        A[A.length - 1] = value;
        return A;
    }

    public static void main(String[] args) {
        int []A = {1, 2123, 37, 45};
        new RadixSort().sort(A);
        for (int i = 0; i < A.length; i ++) {
            System.out.println(A[i]);
        }
    }
}


原文地址:https://www.cnblogs.com/bears9/p/13511463.html