快排和堆排序

一、堆排序

5.1 堆的定义

这里写图片描述

  • 用数组保存效率高,第 i 个节点保存在下标为 i 的位置,也就是 arry[0]空着。
  • i 个元素的左节点在 2 * i,右节点在 2 * i + 1
  • 一个节点的父节点在 i / 2 位置。
  • 以一个size变量记录堆的大小。

这里写图片描述


5.2 堆的基本操作

5.2.1 提取最大元素

return array[1]

5.2.2 删除最大元素

(1)将 和最后一个元素互换,删除最后一个元素

array[1] = array[size];
size--;

这个时候 的两个子树是最大堆,但整体不是。

(2)下调算法 (percolate down)

与子节点中较大的那个节点互换位置。如果父节点不是比子节点都大的话,继续这个下调操作。

时间复杂度O(Log2N)

   /**
     * array: 数组
     * i: 子堆的根
     * size: 堆的大小
     */
    public static void percolateDown(int[] array, int i, int size) {
        int childIndex;

        while ((childIndex = leftChild(i)) < size) {
            if (childIndex + 1 < size && // 如果存在右节点
                    array[childIndex] < array[childIndex + 1]) {
                childIndex += 1; // 左右子节点中,较大的一个是右节点
            }


            // 如果子节点比父节点大,则需要互换
            if (array[i] < array[childIndex]) {
                swap(array, i, childIndex);
                i = childIndex;
            } else {
                return;
            }
        }


    }

    private static int leftChild(int i) {
        return 2 * i + 1;
    }

5.2.3 插入一个元素

(1)将元素添加到数组的末尾

array[++size] = item;

(2)上调算法(percolate up)

是下调算法的逆过程。如果父节点比自己小,则交换位置。

时间复杂度O(Log2N)


5.2.4 建堆

方法一:不断重复插入过程,利用上调操作建堆。

方法二:把一个数组看作是一个完全树,从最后一个非叶子节点开始,应用下调操作将其转化为堆,接着移到前一个节点。(效率更高)下面只介绍方法二。

这里写图片描述

示例:

这里写图片描述

        int size = array.length;
        for (int i = (size - 1) / 2; i >= 0; i--) {
            percolateDown(array, i, size);
        }

5.3 堆排序步骤

对array进行排序(位置0空闲),size = array.length - 1:

(1)建堆:在array上建堆,建堆之后array[1]是最大的元素。

(2)”删除”最大元素:也就是将 array[1]与array[size]互换,然后从根开始进行下调操作。

(3)循环

时间复杂度:O( N * Log2 N )

    public static void heapSort(int[] array) {
        // 建堆
        int size = array.length;
        for (int i = (size - 1) / 2; i >= 0; i--) {
            percolateDown(array, i, size);
        }

        // 删除最大元素
        for (int i = size - 1; i > 0; i--) {
            swap(array, 0, i);
            percolateDown(array, 0, i);
        }
    }
    /**
     * array: 数组
     * i: 子堆的根
     * size: 堆的大小
     */
    public static void percolateDown(int[] array, int i, int size) {
        int childIndex;

        while ((childIndex = leftChild(i)) < size) {
            if (childIndex + 1 < size && // 如果存在右节点
                    array[childIndex] < array[childIndex + 1]) {
                childIndex += 1; // 左右子节点中,较大的一个是右节点
            }


            // 如果子节点比父节点大,则需要互换
            if (array[i] < array[childIndex]) {
                swap(array, i, childIndex);
                i = childIndex;
            } else {
                return;
            }
        }
    }

    private static int leftChild(int i) {
        return 2 * i + 1;
    }

二、快速排序

6.1 算法

这里写图片描述


6.2 改进

6.2.1 基准的选择

这里写图片描述

6.2.2 短子列表

这里写图片描述


6.3 代码

    public static final int CUT_OFF = 2;

    // 选基准的优化,返回左右中三个数中的中位数,过程类似与插入排序
    public static int median(int[] array, int left, int right) {
        int center = (left + right) / 2;
        if (array[center] < array[left]) {
            swap(array, left, center);
        }
        if (array[right] < array[left]) {
            swap(array, left, right);
        }
        if (array[right] < array[center]) {
            swap(array, center, right);
        }

        // 现在的情况是: left < center(pivot) < right
        // 把pivot放到 right-1的位置,这样left和right-1之间都是没有和pivot比较过的数
        swap(array, center, right - 1);
        return array[right - 1];
    }

    public static void quickSort(int[] array) {
        quickSort(array, 0, array.length - 1);
    }

    private static void quickSort(int[] array, int left, int right) {
        // 小数组就不用快速排序了,直接使用插入排序
        if (left + CUT_OFF >= right) {
            insertionSort(array);
            return;
        }

        int pivot = median(array, left, right);
        // 与基准比较并进行交换
        int l = left;
        int r = right - 1;
        while (true) {
            while (array[l] < pivot) {
                l++;
            }
            while (array[r] >= pivot) {
                r--;
            }

            if (l < r) {
                swap(array, l, r);
            } else {
                break;
            }
        }

        // 将基准移到中间来,左边的元素都比基准小,右边的元素都比基准大
        swap(array, l, right - 1);

        // 递归
        quickSort(array, left, l - 1);
        quickSort(array, l + 1, right);
    }
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值