堆排序&快速排序

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/Apple_hzc/article/details/83471830

一、堆排序

构建堆

无序数组建立堆最直接的方法是从左到右遍历数组进行上浮操作。一个更高效的方法是从右至左进行下沉操作,如果一个节点的两个节点都已经是堆有序,那么进行下沉操作可以使得这个节点为根节点的堆有序。叶子节点不需要进行下沉操作,可以忽略叶子节点的元素,因此只需要遍历一半的元素即可。

交换堆顶元素与最后一个元素

交换之后需要进行下沉操作维持堆的有序状态。

class Solution{
    public static void heapSort(int[] nums) {
        if (nums == null || nums.length == 0) {
            return ;
        }
        int n = nums.length;
        for (int i = n / 2 - 1; i >= 0; i--) {
            adjustHeap(nums, i, n);
        }
        for (int i = n - 1; i >= 0; i--) {
            int temp = nums[i];
            nums[i] = nums[0];
            nums[0] = temp;
            adjustHeap(nums, 0, i);
        }
    }

    public static void adjustHeap(int[] nums, int current, int n) {
        int temp = nums[current], left = 2 * current + 1;
        while (left < n) {
            if (left + 1 < n && nums[left] < nums[left + 1]) {
                left++;
            }
            if (temp < nums[left]) {
                nums[current] = nums[left];
                current = left;
            } else {
                break;
            }
            left = 2 * left + 1;
        }
        nums[current] = temp;
    }
}

性能分析

一个堆的高度为 logN,因此在堆中插入元素和删除最大元素的复杂度都为 logN。对于堆排序,由于要对 N 个节点进行下沉操作,因此复杂度为 NlogN。堆排序是一种原地排序,没有利用额外的空间。


二、快速排序

基本原理

  • 归并排序将数组分为两个子数组分别排序,并将有序的子数组归并使得整个数组排序。
  • 快速排序通过一个切分元素将数组分为两个子数组,左子数组小于等于切分元素,右子数组大于等于切分元素,将这两个子数组排序也就将整个数组排序了。

切分

取 a[l] 作为切分元素,然后从数组的左端向右扫描直到找到第一个大于等于它的元素,再从数组的右端向左扫描找到第一个小于等于它的元素,交换这两个元素。不断进行这个过程,就可以保证左指针 i 的左侧元素都不大于切分元素,右指针 j 的右侧元素都不小于切分元素。当两个指针相遇时,将切分元素 a[l] 和 a[j] 交换位置。

class Solution{
    public static void quickSort(int[] nums, int left, int right) {
        if (nums == null || nums.length == 0 || left >= right) {
            return ;
        }
        int basePostion = partition(nums, left, right);
        quickSort(nums, left, basePostion - 1);
        quickSort(nums, basePostion + 1, right);
    }

    public static int partition(int[] nums, int left, int right) {
        int i = left, j = right, base = nums[left];
        while (i < j) {
            while (i < j && base <= nums[j]) {
                j--;
            }
            if (i < j) {
                nums[i++] = nums[j];
            }
            while (i < j && base >= nums[i]) {
                i++;
            }
            if (i < j) {
                nums[j--] = nums[i];
            }
        }
        nums[i] = base;
        return j;
    }
}

性能分析

快速排序是原地排序,不需要辅助数组,但是递归调用需要辅助栈。

快速排序最好的情况下是每次都正好能将数组对半分,这样递归调用次数才是最少的。这种情况下比较次数为 CN=2CN/2+N,复杂度为 O(NlogN)。

最坏的情况下,第一次从最小的元素切分,第二次从第二小的元素切分,如此这般。因此最坏的情况下需要比较 N2/2。为了防止数组最开始就是有序的,在进行快速排序时需要随机打乱数组。

猜你喜欢

转载自blog.csdn.net/Apple_hzc/article/details/83471830