zoukankan      html  css  js  c++  java
  • 各大排序算法的分析与实现以及时间复杂度

    时间复杂度:

    时间复杂度是一个算法流程中,常数操作数量的指标。常用O表示。在常数操作数量的表达式中,只要高阶项,不要低阶项,也不要高阶项系数,剩下的部分如果记为f(n),那么时间复杂度就是O(f(n))。

    一、冒泡排序


    思想:n个数一一对比之后找出最大的,再在剩下的n-1个数中一一对比找出第二大的,以此类推。

    时间复杂度:O(n^2)

    实现代码:

        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;
        }

    二、选择排序

    思想:n个数中第一个数与后面的数字比较,若有更小的就交换,从而让第一个数字为最小值,接着再处理剩下的n-1个数,以此类推。

    时间复杂度:O(n^2)

    实现代码:

        public static void insertionSort(int[] arr) {
            for (int i = 0; i < arr.length; i++) {
                int minIndex = i;
                for (int j = i + 1; j < arr.length; j++) {
                    //如果有小于min的值,就改变min的下标
                    if (arr[j] < arr[minIndex]) {
                        minIndex = j;
                    }
                }
                swap(arr, i, minIndex);
            }
        }
    
        public static void swap(int[] arr, int i, int j) {
            int temp = arr[i];
            arr[i] = arr[j];
            arr[j] = temp;
        }

    三、插入排序

    思想:第二个数先与第一个数比较,形成有序序列。接着第三个数和前两个形成有序序列的数字比较,找到合适的位置插入。以此类推。

    最好的时间复杂度:O(n)

    最坏的时间复杂度:O(n^2)

    实现代码:

        public static void insertionSort(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;
        }

    四、归并排序

    思想:采用分治的思想,将n个元素的数组切成一半,每一半再分别排序,最后将两半已经排序的数组合并。

    时间复杂度:O(n*logn)

    空间复杂度:O(n)

    实现代码:

        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 index, int end) {
            if (index == end) return;
            int mid = index + (end - index) / 2;
            mergeSort(arr, index, mid);
            mergeSort(arr, mid + 1, end);
            //合并两个数组
            merge(arr, index, mid, end);
        }
    
        public static void merge(int[] arr, int index, int mid, int end) {
            //构造辅助数组
            int[] help = new int[end - index + 1];
            int i = 0;
            int p1 = index;
            int p2 = mid + 1;
            //哪个比较小就放入辅助数组,并且移动指针
            while (p1 <= mid && p2 <= end) {
                help[i++] = arr[p1] < arr[p2] ? arr[p1++] : arr[p2++];
            }
            //说明p2指针移动完毕,只剩p1
            while (p1 <= mid) {
                help[i++] = arr[p1++];
            }
            //说明p1指针移动完毕,只剩p2
            while (p2 <= end) {
                help[i++] = arr[p2++];
            }
            //将辅助数组的元素复制回原来数组
            for (int j = 0; j < help.length; j++) {
                arr[index + j] = help[j];
            }
        }

    五、快速排序(改进后的)

    思想:把数组中的最后一个元素作为分界量,也就是基准点,排成左边比它小的,中间等于它的,右边比它大的。再递归调用分别排左边的和右边的。

    最好的时间复杂度:O(n*logn)

    最坏的时间复杂度:O(n^2)

    长期期望的时间复杂度:O(n*logn)

    长期期望的额外空间复杂度:O(logn)

    实现代码:

        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 index, int end) {
            if (index < end) {
                //swap(arr, l + (int) (Math.random() * (r - l + 1)), r);(这一句加上就是随机快速排序,暂时不加)
                //p数组代表的是等于划分基准点的元素的初始和结束位置
                int[] p = partition(arr, index, end);
                quickSort(arr, index, p[0] - 1);
                quickSort(arr, p[1] + 1, end);
            }
        }
    
        //下面是以数组的最后一个元素作为基准点的,这个方法完成的结果就是小于基准点放左边,等于基准点放中间,大于基准点放右边
        public static int[] partition(int[] arr, int index, int end) {
            //less指针用来划分小于基准点的范围
            int less = index - 1;
            //more指针用来划分大于基准点的范围
            int more = end;
            while (index < more) {
                //如果当前元素小于基准点的元素,就让它与less指针的前一个数字交换,并移动less指针,移动index指针操作下一个数
                //如果当前元素大于基准点的元素,就让它与more指针的前一个数字交换,并移动more指针
                //如果当前元素等于基准点的元素,移动index指针操作下一个数
                if (arr[index] < arr[end]) {
                    swap(arr, index++, ++less);
                } else if (arr[index] > arr[end]) {
                    swap(arr, index, --more);
                } else {
                    index++;
                }
            }
            //由于基准点的元素一直没有动过,最后要让它和大于基准点的元素交换
            swap(arr, more, end);
            //返回等于基准点元素的下标
            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;
        }

    六、堆排序

    思想:先让数组的n个元素依次进堆,构建出大顶堆。然后交换顶堆的元素和最末尾的元素(最末尾的元素不一定最小,但是一定小于等于堆顶的元素),然后让剩下的n-1元素进行调整,重新调整为大顶堆。然后在进行交换,再调整,以此类推。

    时间复杂度:O(n*logn)

    实现代码:

        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;
            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;
            //如果左结点在size范围内
            while (left < size) {
                //从左右结点中选出最大的,赋给largest
                int largest = left + 1 < size && arr[left + 1] > arr[left] ? left + 1 : left;
                //判断当前结点和左右结点的最大值比较,如果当前结点大就维持原样(因为调整到index的结点可能比左结点的值要大的)
                if (arr[largest]<arr[index]) {
                    break;
                }
                //交换index和largest结点的值
                swap(arr, largest, index);
                //让index指针移动到largest结点位置
                index = largest;
                //取index的左结点
                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;
        }

    排序算法的稳定性

    排完顺序之后不会改变相同的原数字在原来数组中的相对次序,称为稳定。反之为不稳定

    冒泡排序:稳定

    选择排序:不稳定

    插入排序:稳定

    归并排序:稳定

    快速排序:不稳定

    堆排序:不稳定

  • 相关阅读:
    Java 蓝桥杯 算法训练 貌似化学
    Java 蓝桥杯 算法训练 貌似化学
    Java 蓝桥杯 算法训练 字符串的展开 (JAVA语言实现)
    Java 蓝桥杯 算法训练 字符串的展开 (JAVA语言实现)
    Java 蓝桥杯 算法训练 字符串的展开 (JAVA语言实现)
    Java 蓝桥杯 算法训练 字符串的展开 (JAVA语言实现)
    Java 蓝桥杯 算法训练 字符串的展开 (JAVA语言实现)
    JAVA-蓝桥杯-算法训练-字符串变换
    Ceph:一个开源的 Linux PB 级分布式文件系统
    shell 脚本监控程序是否正在执行, 如果没有执行, 则自动启动该进程
  • 原文地址:https://www.cnblogs.com/lzxin/p/10030433.html
Copyright © 2011-2022 走看看