zoukankan      html  css  js  c++  java
  • 【排序算法汇总】5类8种排序算法

    最近回顾了一下排序算法,发现有些算法还是很绕的,重新梳理一遍,总结如下

    很多算法已经忘了,还能流利的写出来的就只有冒泡和选择了,其他排序都是先回顾了一下算法,然后才边调试边写的 其中的基数排序。。感觉很少用,暂时没有梳理,后面再补充

    ##插入排序 分为直接插入和希尔排序,希尔其实就是插入排序的增量版 ###直接插入:

    /**
         * 插入排序(升序)
         * 维护一个有序列,每次新增元素插入到有序列的某位置使有序列仍有序,直到排序结束
         */
        public static int[] insertSort(int[] numbers) {
            //若数组为空,或数组的长度为0或1,直接返回
            if (numbers == null || numbers.length <= 1) {
                return numbers;
            }
            //假设数组的第一个元素是有序的,从第二个元素开始处理
            for (int i = 1; i < numbers.length; i++) {
                //表示要插入的位置下标
                int j;
                //表示当前要插入的元素
                int temp = numbers[i];
                for (j = i; j > 0; j--) {
                    //如果当前处理的元素位置比前一个元素小,则前一个元素后移一位
                    if (temp < numbers[j - 1]) {
                        numbers[j] = numbers[j - 1];
                    } else {
                        //直到找到一个位置当前元素不小于它
                        break;
                    }
                }
                //将当前待插入的元素插入到不小于它的位置
                numbers[j] = temp;
            }
            return numbers;
        }
    

    ###希尔排序:

    /**
         * 希尔排序(升序)
         * 希尔排序是指,先用2来进行分组,分为n/2组,每组2个数字,进行直接插入排序
         * 进而分为n/2/2...直到最后一次分组,只有一个组,然后整体有序
         *
         * @param numbers
         * @return
         */
        public static int[] shellSort(int[] numbers) {
            //若数组为空,或数组的长度为0或1,直接返回
            if (numbers == null || numbers.length <= 1) {
                return numbers;
            }
            //gap为步长
            for (int gap = numbers.length / 2; gap > 0; gap /= 2) {
                //共分为i组
                for (int i = 0; i < gap; i++) {
                    //第i组的下一个元素为i+gap
                    for (int j = i + gap; j < numbers.length; j += gap) {
                        int k;
                        int temp = numbers[j];
                        for (k = j; k >= gap; k -= gap) {
                            if (temp <= numbers[k - gap]) {
                                numbers[k] = numbers[k - gap];
                            } else {
                                break;
                            }
                        }
                        numbers[k] = temp;
                    }
                }
            }
            return numbers;
        }
    

    ##交换排序 分为冒泡和快排 冒泡就是两两比较,小的向前,直到一轮后最小的数到了第一位,然后开始第二轮,次小数到第二位... 快排是选第一个数位基准,用两个下标分别指向头尾,用尾下标开始和基准数比较,若小于基准,则交换两个下标对应的数,否则下标递减;然后用头下标与基准比较,若大于基准,则交换两个下标对应的数,否则下标递增...直到两下标相等,再分别对前半段和后半段重复上述操作 ##冒泡

     /**
         * 冒泡排序
         * 从第最后一个数开始,向前每两个做比较,a>b交换,直到第一个数,这样,最小的数就换到了第一位;
         * 再从第最后一个数开始重复上述步骤,直到第二个数...
         * 最终整个数组有序
         *
         * @param numbers
         * @return
         */
        public static int[] popSort(int[] numbers) {
            if (numbers == null || numbers.length <= 1) {
                return numbers;
            }
            for (int i = 0; i < numbers.length; i++) {
                for (int j = numbers.length - 1; j > i; j--) {
                    if (numbers[j] < numbers[j - 1]) {
                        swap(numbers, j, j - 1);
                    }
                }
            }
            return numbers;
        }
    

    ##快排

    /**
         * 快速排序
         * 有两个游标a,b分别指向数组头尾,用arr[a]作为基准
         * 先从尾部的游标开始与基准作比较,若小于基准,则交换arr[a],arr[b],否则b--
         * 然后从头部开始比较,若大于基准,则交换arr[a],arr[b],否则a++
         * 直到a=b=middle,然后分别将数组0-middle-1位,与middle+1 -arr.length-1位重复上述步骤
         *
         * @param numbers
         * @return
         */
        public static int[] quickSort(int[] numbers, int a, int b) {
            if (numbers == null || numbers.length <= 1 || a >= b) {
                return numbers;
            }
            int mid = divideForQuick(numbers, a, b);
            quickSort(numbers, a, mid - 1);
            quickSort(numbers, mid + 1, b);
            return numbers;
        }
    
        /**
         * 快排首轮划分
         *
         * @param numbers
         * @param start
         * @param end
         */
        private static int divideForQuick(int[] numbers, int start, int end) {
            int temp = numbers[start];
            while (start < end) {
                while (start < end && numbers[end] >= temp) {
                    end--;
                }
                if (numbers[end] < temp) {
                    swap(numbers, start, end);
                }
                while (start < end && numbers[start] <= temp) {
                    start++;
                }
                if (numbers[start] > temp) {
                    swap(numbers, start, end);
                }
            }
            return end;
        }
    
        /**
         * 交换
         *
         * @param num
         * @param a
         * @param b
         */
        private static void swap(int[] num, int a, int b) {
            int temp = num[a];
            num[a] = num[b];
            num[b] = temp;
        }
    

    ##选择排序 分为选择和堆排序 ###选择排序:

    /**
         * 选择排序
         * 从整列中选一个最小的,与第一位交换
         * 然后从第二位开始,重复上述步骤
         * 直到全列有序
         *
         * @param numbers
         * @return
         */
        public static int[] selectSort(int[] numbers) {
            if (numbers == null || numbers.length <= 1) {
                return numbers;
            }
            for (int i = 0; i < numbers.length - 1; i++) {
                int minIndex = i;
                for (int j = i + 1; j < numbers.length; j++) {
                    if (numbers[j] < numbers[minIndex]) {
                        minIndex = j;
                    }
                }
                swap(numbers, i, minIndex);
            }
            return numbers;
        }
    

    ###堆排序:

    /**
         * 堆排序
         * 按从上到下,从左到右的顺序,将数组想象成完全二叉树
         * 则每个子节点的下标/2即为其父节点的下标
         * 堆排序就是先将数组调整为大顶堆
         * 即从第n/2个节点(最后一颗子树的父节点下标)开始,使每个子树都变成大顶堆
         * 直到下标为0的根节点成为整个堆中最大的,将它与第n个数交换
         * 然后就只有第0个是不稳定元素,只要调整第0个元素到底部,即可又形成大顶堆,直到全列有序
         *
         * @param numbers
         * @return
         */
        public static int[] heapSort(int[] numbers) {
            if (numbers == null || numbers.length <= 1) {
                return numbers;
            }
            //从最末端的一颗子树开始构建大顶堆
            for (int i = (numbers.length - 1 - 1) / 2; i >= 0; i--) {
                bigTopHeap(numbers, i, numbers.length - 1);
            }
            //此时下标0的数已经是最大的了,交换0与最后一个数,然后将0-last-1再整树构建大顶堆
            swap(numbers, 0, numbers.length - 1);
            for (int j = numbers.length - 2; j > 0; j--) {
                bigTopHeap(numbers, 0, j);
                swap(numbers, 0, j);
            }
            return numbers;
        }
    
        /**
         * 构建大顶堆,认为传进来的只有第一个元素是不稳定的,即不大于左右叶子
         *
         * @param numbers 要排序的数组
         * @param first   第一个元素下标
         * @param last    最后一个元素下标
         */
        private static void bigTopHeap(int[] numbers, int first, int last) {
            //从最顶部开始,(last-1)/2是最后一个需要调整的子树树根
            int i = first;
            while (i <= (last - 1) / 2) {
                //假设first的左叶子较大
                i = i * 2 + 1;
                //比较左右叶子,用i记录子树最大值下标
                if (i < last && numbers[i] < numbers[i + 1]) {
                    i++;
                }
                //若已经是大顶堆,则直接跳出返回
                if (numbers[i] < numbers[(i - 1) / 2]) {
                    break;
                }
                swap(numbers, i, (i - 1) / 2);
            }
        }
    

    ##归并排序

    /**
         * 归并排序
         * 将一个数组分成两个,然后使每个数组有序,再合并,即为有序数组
         * 对分成的两个小数组递归上述操作
         *
         * @param numbers
         * @return
         */
        public static int[] mergeSort(int[] numbers) {
            if (numbers == null || numbers.length <= 1) {
                return numbers;
            }
            sortForMerge(numbers, 0, numbers.length - 1);
            return numbers;
        }
    
        /**
         * 归并排序的递归方法
         *
         * @param numbers 要操作的数组
         * @param start
         * @param end
         */
        private static void sortForMerge(int[] numbers, int start, int end) {
            if (start >= end) {
                return;
            }
            sortForMerge(numbers, start, (start + end) / 2);
            sortForMerge(numbers, (start + end) / 2 + 1, end);
            mergeTwoArray(numbers, start, (start + end) / 2 + 1, end);
        }
    
        /**
         * 归并排序的合并两个数组的算法
         *
         * @param numbers     要操作的数组
         * @param firstStart  第一个序列的起始下标
         * @param secondStart 第二个序列的结束下标,第一个序列的结束下标位该值-1
         * @param secondEnd   第二个序列的结束下标
         */
        private static void mergeTwoArray(int[] numbers, int firstStart, int secondStart, int secondEnd) {
            int[] temp = new int[secondEnd - firstStart + 1];
            //k是temp的下标
            int i = firstStart, j = secondStart, k = 0;
            while (i < secondStart && j <= secondEnd) {
                if (numbers[i] < numbers[j]) {
                    temp[k++] = numbers[i++];
                } else {
                    temp[k++] = numbers[j++];
                }
            }
            if (i < secondStart) {
                System.arraycopy(numbers, i, temp, k, secondStart - i);
            }
            if (j <= secondEnd) {
                System.arraycopy(numbers, j, temp, k, secondEnd - j + 1);
            }
            System.arraycopy(temp, 0, numbers, firstStart, temp.length);
        }
    
  • 相关阅读:
    Jenkins 主备master-slave模式搭建
    vbox 相关
    jenkins 常见问题汇总
    linux git patch 和patch以及git diff 命令
    Linux中的free命令
    MySQL Show命令的使用
    MySQL 加锁处理分析 转
    共享锁【S锁】 排他锁【X锁】
    MySQL分库分表环境下全局ID生成方案 转
    mysql性能的检查和调优方法
  • 原文地址:https://www.cnblogs.com/shanelau/p/7203715.html
Copyright © 2011-2022 走看看