zoukankan      html  css  js  c++  java
  • 排序算法

    *时间复杂度

    排序算法 平均时间复杂度 最坏时间复杂度 空间复杂度 稳定性
    冒泡排序 O( n ^ 2) O( n ^ 2) O(1)
    选择排序 O( n ^ 2) O( n ^ 2) O(1) 不是
    插入排序 O( n ^ 2) O( n ^ 2) O(1)
    归并排序 O( n log n) O( n log n) O(n)
    快速排序 O( n log n) O( n ^ 2) O(log n) 不是
    堆排序 O( n log n) O( n log n) O(1) 不是

    1. 冒泡排序

    两两对比,较大的数往后面沉。

        public static void bubbleSort(int[] arr) {
            for(int i = arr.length - 1; i > 0; i--) {
                for(int j = 0; j < i; j++) {
                    if(arr[j] > arr[j + 1]) {
                        //两两对比,大的数往后面沉
                        swap(arr, j, j + 1);
                    }
                }
            }
        }
    
        public static void swap(int[] arr, int i, int j) {
            int temp = arr[i];
            arr[i] = arr[j];
            arr[j] = temp;
        }

    2. 选择排序

    最前面的数分别与后面所有数对比,找出最小的,交换。

        public static void selectSort(int[] arr) {
            for(int i = 0; i < arr.length; i++) {
                int min = i;
                for(int j = i + 1; j < arr.length; j++) {
                    //从当前位置往后面找,选择一个最小的数
                    if(arr[j] < arr[min]) {
                        min = j;
                    }
                }
                swap(arr, i, min);
            }
        }
    
        public static void swap(int[] arr, int i, int j) {
            int temp = arr[i];
            arr[i] = arr[j];
            arr[j] = temp;
        }

    3. 插入排序

    类似于打扑克牌,已经在手上的牌有序,新抽的牌插入到有序的位置。

    第二个数与第一个数比较,排序。第三个数与前两个数比较,排序。

      public static void insertSort(int[] arr) {
            for(int i = 1; i < arr.length; i++) {
                for(int j = i - 1; j >= 0 && arr[j] > arr[j + 1]; j--) {
                    swap(arr, j, j+ 1);
                }
            }
        }
    
        public static void swap(int[] arr, int i, int j) {
            int temp = arr[i];
            arr[i] = arr[j];
            arr[j] = temp;
        }

    4. 希尔排序

    是简单插入排序的优化版,与插入排序的不同之处在于,会优先比较距离较远的元素,希尔排序又叫缩小增量排序。

    public static void shellSort(int[] arr) {
            int temp = 1;
            while(temp < arr.length / 3)
                temp = temp * 3 + 1;
            while(temp >= 1) {
                for(int i = temp; i < arr.length; i++) {
                    if(arr[i] < arr[i - temp]) {
                        swap(arr, i, i - temp);
                    }
                }
                temp = temp / 3;
            }
            SelectSort s = new SelectSort();
            s.selectSort(arr);
        }
        
        public static void swap(int[] arr, int i, int j) {
            int temp = arr[i];
            arr[i] = arr[j];
            arr[j] = temp;
        }

     

    5. 归并排序

    分治的思想,不断将数组二分,直到不可再分时(即只剩一个元素时)便有序了,然后在将两部分合并的时候,利用外排将两部分合成有序序列

      public static void mergeSort(int[] arr) {
            if(arr == null || arr.length < 2) return;
            mergeSort(arr, 0, arr.length - 1);
        }
    
        public static void mergeSort(int[] arr, int left, int right) {
            if(left == right) return;
            int mid = left + ((right - left) >> 1);
            //右移一位时除于2,右移 n 位相当于除于 2 的 n 次方
            mergeSort(arr, left, mid);
            mergeSort(arr, mid + 1, right);
            merge(arr, left, mid, right);
            //合并分开的两部分
        }
    
        public static void merge(int[] arr, int left, int mid, int right) {
            int[] help = new int[right - left + 1];
            //辅助数组,空间复杂度O(n)
            int index = 0;
            int p1 = left, p2 = mid + 1;
            //通过外排的方式,按照顺序放入辅助数组中
            while(p1 <= mid && p2 <= right) {
                help[index++] = arr[p1] < arr[p2] ? arr[p1++] : arr[p2++];
            }
            while(p1 <= mid) {
                help[index++] = arr[p1++];
            }
            while(p2 <= right) {
                help[index++] = arr[p2++];
            }
            //将辅助数组中的元素复制回原素组
            for(int i = 0; i < help.length; i++) {
                arr[left + i] = help[i];
            }
        }
    
        public static void swap(int[] arr, int i, int j) {
            int temp = arr[i];
            arr[i] = arr[j];
            arr[j] = temp;
        }

    6. 快速排序

    经典快排:选择一个元素作为切分元素,左右两个指针分别向中间遍历,当左遇到大于切分元素,而右小于切分元素时,两者交换,继续遍历,直到左边都小于切分元素,右边都大于切分元素,一趟结束。

    性能分析:

    • 快排是原地排序,不需要辅助数组,但是递归调用需要辅助栈,快排最好的情况是每次切分元素都正好将数组对半分,这样递归调用的次数是最少的。
    • 实现方法:1. 随机快排,每趟快排之前随机选取一个元素与原定切分元素交换。 2. 三数取中,最好的情况是每次都能取到数组的中位数作为切分元素,但是计算中位数显然不可取,折中的方法是:每趟快排选取3个元素,取中间的值作为切分元素
    • 三向切分优化:大于有大量重复元素的数组,可以将数组划分为三部分,分别对应小于、等于、大于切分元素。对于大量重复元素的随机数组可以在线性时间内完成排序。
        public static void quickSort(int[] arr) {
            if(arr == null || arr.length < 2) return;
            quickSort(arr, 0, arr.length - 1);
        }
    
        public static void quickSort(int[] arr, int left, int right) {
            if(left < right) {
                swap(arr, right, left + (int)(Math.random()*(right - left + 1)));
                //随机快排实现:随机生成left-right中的下标,然后与right位置的值交换
                int[] result = partition(arr, left, right);
                //三向切分优化快排,返回等于划分right位置的值的前后下标
                quickSort(arr, left, result[0] - 1);
                quickSort(arr, result[1] + 1, right);
            }
        }
    
        public static int[] partition(int[] arr, int left, int right) {
            int less = left - 1;
            int more = right;
            int index = left;
            while(index < more) {
                if(arr[index] < arr[right]) {
                    //小于基准值,与小于区域的后一个值交换
                    swap(arr, index++, ++less);
                } else if(arr[index] > arr[right]) {
                    //大于基准值,与大于区域的前一个值交换
                    swap(arr, index, --more);
                } else {
                    //等于基准值时,继续遍历下一个数
                    index++;
                }
            }
            swap(arr, more, right);
            return new int[]{less + 1, more};
        }
    
        public static void swap(int[] arr, int i, int j) {
            int temp = arr[i];
            arr[i] = arr[j];
            arr[j] = temp;
        }

    7.堆排序

    堆的底层结构使用数组实现,每个结点的父亲结点为 ( i - 1) / 2 ,孩子结点为 2 * i + 1 和 2 * i + 2。

    排序过程:先将数组构建成大顶堆,然后将堆顶元素与最后一个元素交换(最后一个元素不一定最小,但一定会比堆顶元素小),交换之后除掉最后一个元素(即原堆顶元素)重新调整为大顶堆。

        public static void heapSort(int[] arr) {
            if(arr == null || arr.length < 2) return;
            for(int i = 0; i < arr.length; i++) {
                heapInsert(arr, i);
            }
            int size = arr.length - 1 ;
            swap(arr, 0, size);
            while(size > 0) {
                heapify(arr, 0, size--);
                swap(arr, 0, size);
            }
        }
        //构建大顶堆
        public static void heapInsert(int[] arr, int index) {
            //不断与父亲节点比较,交换
            while(arr[index] > arr[(index - 1) / 2]) {
                swap(arr, index, (index - 1) / 2);
                index = (index - 1) / 2;
            }
        }
    
        //当将堆顶与最后一个数交换后,重建构建大顶堆
        public static void heapify(int[] arr, int index, int size) {
    
            int left = index * 2 + 1;
    
            while(left < size) {
                int largeSet = left + 1 < size && arr[left + 1] > arr[left] ? left + 1 : left;
                //选择孩子结点中较大的一个
                if(arr[index] > arr[largeSet]) {
                    break;
                }
                swap(arr, index, largeSet);
                index = largeSet;
                left = index * 2 + 1;
            }
        }
    
    
        public static void swap(int[] arr, int i, int j) {
            int temp = arr[i];
            arr[i] = arr[j];
            arr[j] = temp;
        }
  • 相关阅读:
    JavaScript——标准对象
    JavaScript——方法
    JavaScript——变量作用域
    移动开发程序员的悲哀是什么?
    腾讯14年老员工被公司恶意逼走!以不胜任工作为由被裁!腾讯对待老员工也太狠了吧?
    Android开发北漂 8 年,飘飘飘 飘够了。。。。
    我是双非/三本/专科学校的Android开发,我有机会进入大厂吗?
    Android开发3年,我转Java后台了,真香!
    我的字节跳动Android面试初体验——稀里糊涂结束战斗
    Android Studio 教程:入门开发第一个程序
  • 原文地址:https://www.cnblogs.com/BitingCat/p/10538060.html
Copyright © 2011-2022 走看看