排序算法----堆排序

       堆排序利用了二叉堆这一特殊的数据结构,关于二叉堆我们可以将其想象成一棵完全二叉树,虽然其实现是通过数组,为什么能通过数组实现呢?因为其父节点与左、右子节点存在明确的关系,已知父节点的索引就可以找到其子节点,已知子节点索引也能很容易找到父节点的索引。

       如果父节点索引为 i,那么其左节点的索引为 2*i + 1,右节点的索引为2*i + 2。

       如果子节点索引为 i,那么其父节点的索引为 (i - 1)/ 2。

       堆排序的过程是首先建立一个大根堆,建堆的过程是每一次往数组尾端添加元素,然后涉及到一个堆调整的基本操作----元素上溯,通过元素上溯使得插入一个元素之后仍然是一个堆结构。当所有的元素都添加进来,建堆就完成了,这个过程的时间复杂度为O(N)。

       大根堆的堆顶元素为当前堆中最大的元素,将堆顶元素与最后一个元素交换,同时将堆的size - 1,此时最后一个元素就排定了,剩下的问题是由于交换,将最后一个元素推到了堆顶位置,而这很可能破坏了堆结构,此时要通过元素下溯这一堆调整的基本步骤使之重新恢复成大根堆。不断重复这一过程完成堆排序。

       元素上溯:最后一个元素可能不是最小的,它应该处于离堆顶更近的位置,通过比较其于其父节点的大小实现元素上溯,比如说一比较发现其比父节点大,那么就交换之,重复这个过程直到发现其比父节点小或者已经到达堆顶位置。

       元素下溯:堆顶元素不是最大,说明其不应该处于堆顶位置。首先比较其左右子节点,找到其中较大的那个节点,再将当前节点与较大的那个子节点进行比较,如果当前节点小于较大的那个子节点,交换之,重复这一步骤,直到当前节点大于较大的那个子节点或者已经下溯到了堆的最底下了。

      

package young.unit01;

/**
 * 堆排序:1.建立大根堆
 *       2.头结点与尾结点交换,最末尾位置数字排除出堆(这个数已排好) 重新剩余部分调整为大根堆
 *       3.重复步骤2,直至heapSize == 0
 * 时间复杂度     两部分:建立大根堆       排序调整过程
 *       1.建立大根堆
 *       加入第几个数                            堆中数字个数                     堆的高度
 *           1                0             0
 *           2                1            log1
 *           3                2            log2
 *          ...              ...           ...
 *           n               n-1          log(n-1)
 *       每加入一个数进入堆,最差情况是把堆上要加入的这一层以上的所有层过一遍
 *       建堆花费的时间 = lg1 + lg2 + ... + lg(n-1) = O(N)
 *       
 *       2.真正排序调整--- O(NlgN)
 *       每交换一次,就要对堆进行一次调整,最差情况是每次将整个堆过一遍
 *       一共交换调整N次,每次时间复杂度O(N)---最差时间复杂度O(NlgN)
 *       
 *       以上是按照最坏情况分析,进一步分析可知在最好,最坏,长期期望下,堆排序时间复杂度都是---O(NlgN)
 * 
 * 空间复杂度
 *      所有的过程没有用到辅助数组,全部都是在原数组进行,只是用了常数个变量
 *      空间复杂度---O(1)
 * 
 * 稳定性
 *     无法做到稳定性
 *     因为每次头结点与尾结点交换之前并不会进行判断,这样无法保证交换之后的稳定性
 * @author sunmin
 * @Description:TODO
 * @date:2018年6月17日    下午7:08:37 
 * @Copyright: 2018 www.xy.com. All rights reserved. 
 * 内部代码,严禁外泄
 */
public class T11_HeapSort {
    
    public static void heapSort(int[] nums, int left, int right) {
        if (nums == null || nums.length < 2 || (right - left) < 2) {
            return;
        }
        for (int i = left; i <= right; i++) {
            heapInsert(nums, i);
        }
        swap(nums, left, right);
        int heapSize = right - left + 1;
        while (heapSize > 0) {
            heapIfy(nums, left, heapSize--);
            swap(nums, left, --right);
        }
    }
    
    public static void heapSort(int[] nums) {
        if (nums == null || nums.length < 2) {
            return;
        }
        for (int i = 0; i < nums.length; i++) {
            heapInsert(nums, i);
        }
        int heapSize = nums.length;
        swap(nums, 0, --heapSize);
        while (heapSize > 0) {
            heapIfy(nums, 0, heapSize);
            swap(nums, 0, --heapSize);
        }
    }
    
    private static void heapInsert(int[] nums, int index) {
        while (nums[index] > nums[(index - 1) / 2]) {
            swap(nums, index, (index - 1) / 2);
            index = (index - 1) / 2;
        }
    }
    
    // O(lgN)---堆调整的速度是很快的
    private static void heapIfy(int[] nums, int index, int heapSize) {
        int left = 2 * index + 1;
        while (left < heapSize) {
            int largest = nums[left] > nums[left + 1] ? left : left + 1;
            largest = nums[index] > nums[largest] ? index : largest;
            if (largest == index) {
                break;
            }
            swap(nums, index, largest);
            index = largest;
            left = 2 * index + 1;
        }
    }

    private static void swap(int[] nums, int i, int j) {
        int tmp = nums[i];
        nums[i] = nums[j];
        nums[j] = tmp;
    }
}

 

posted @ 2018-07-17 15:57  大将军姜伯约  阅读(118)  评论(0)    收藏  举报