• 十大排序


    (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]);
            }
        }
    }
    
    
    
  • 相关阅读:
    日记搬迁
    学生会管理系统(JavaWeb与数据库课程小实践)
    疯狂忙碌边缘
    英语复习二:每单元的翻译篇章
    Don't always upset yourself !
    一文教你读懂并使用GTD高效时间管理法
    Day05-黑马学习篇(二)matplot基本绘图函数集合
    Day04-黑马学习篇(一)matplot画图基本要点
    Day03-基础篇(四)Pandas与数据清洗
    Day02 基础篇(三)用NumPy快速处理数据
  • 原文地址:https://www.cnblogs.com/bears9/p/13511463.html
Copyright © 2020-2023  润新知