原理

其实就是一种特殊的完全二叉树,分为大顶堆和小顶堆

大顶堆就是指父节点  >= 左右孩子结点, 而左右孩子结点之间的大小关系随意。小顶堆反之。

堆排序基本思路

就是先把序列构建成大顶堆序(升序用大顶堆)

然后大顶堆的根节点和最后一个结点交换位置,将最大元素沉到数组末端

这样一来每交换一次就得到当前序列的最大值,并把它放在了最后面,接着把剩下的序列继续构建成大顶堆,重复上面动作,直到序列只剩一个。

复杂度

时间复杂度

分为两部分:初始化建堆和重建堆。

初始化堆的时间复杂度:O(n)

重建堆的时间复杂度:O(nlgn)

故:

最好情况:O(nlgn)

最坏情况:O(nlgn)

平均情况:O(nlgn)

不稳定

空间复杂度

O(1)

代码

堆排序

import java.util.Comparator;

public class MyHeap<T> {

    private T[] heap;

    private Comparator<? super T> comparable;

    public MyHeap(T[] heap, Comparator<? super T> comparable) {
        this.heap = heap;
        this.comparable = comparable;
        initHeap();
    }

    public MyHeap(Comparator<? super T> comparable) {
        this.comparable = comparable;
    }
    
    public static void main(String[] args) {
        Integer[] heap = { 6, 4, 7, 2, 9, 15, 11, 10, 18 };
        Comparator<Integer> comparator = new Comparator<Integer>() {
            @Override
            public int compare(Integer o1, Integer o2) {
                return o1 - o2;
            }
        };

        MyHeap<Integer> myHeap = new MyHeap<Integer>(heap, comparator);
        for (int i : heap) {
            System.out.print(i + " ");
        }

        System.out.println();

        myHeap.sort();
        for (int i : heap) {
            System.out.print(i + " ");
        }
        System.out.println();
    }

    public void initHeap() {
        //for (int i = 0; i <= heap.length >> 1 - 1; i++) {//这么写不行
        //从最后一个节点array.length-1的父节点(array.length-1-1)/2开始,直到根节点0,把最大的值弄到根节点
        for (int i = heap.length >> 1 - 1; i >= 0; i--) {
            adjustHeap(heap, i, heap.length);
        }
    }

    public void adjustHeap(T[] heap, int i, int heapSize) {
        int left = (i << 1) + 1;
        int right = (i << 1) + 2;

        int temp = i;
        if (left < heapSize && comparable.compare(heap[left], heap[i]) > 0) {
            temp = left;
        }
        if (right < heapSize && comparable.compare(heap[right], heap[temp]) > 0) {
            temp = right;
        }

        if (temp != i) {
            swap(temp, i);
            adjustHeap(heap, temp, heapSize);
        }
    }

    /**
     * 对堆进行排序 (堆排序)
     */
    public void sort() {
// buildHeap(); 
        for (int i = heap.length - 1; i > 0; i--) {
            swap(0, i);
            adjustHeap(heap, 0, i);
        }
    }

    public void swap(int a, int b) {
        T temp = heap[a];
        heap[a] = heap[b];
        heap[b] = temp;
    }
}

topN

因为每一次调整,都是调整出一个最大(小)的数来,所以,可以控制调整的次数。

甚至可以连第一次都不用全部调整全部的数据。可以先调整N个数,其他的数,再比较一下,是不是可能加到这N个数中,再调整。

public class MyHeapTest {

    public static void main(String[] args) {
        Integer[] heap = { 6, 4, 7, 2, 9, 15, 11, 10, 18 };

        Integer[] result = topN(heap, 3);
        for (int integer : result) {
            System.out.print(integer + ",");
        }
    }

    public static Integer[] topN(Integer[] n, int size) {
        if (size >= n.length) {
            return n;
        }

        Integer[] result = new Integer[size];
        for (int i = 0; i < size; i++) {
            result[i] = n[i];
        }

        Comparator<Integer> comparator = new Comparator<Integer>() {
            @Override
            public int compare(Integer o1, Integer o2) {
                return o2 - o1;// 这是最小堆的写法,最大堆相反,并改变下面的。。
            }
        };

        MyHeap<Integer> myHeap = new MyHeap<Integer>(result, comparator);
        for (int i = 0; i < size; i++) {
            System.out.print(result[i] + " ");
        }
        System.out.println();

        for (int i = size; i < n.length; i++) {
            if (n[i] > result[0]) {// ....改变这里
                result[0] = n[i];
                myHeap.adjustHeap(result, 0, size);
            }
        }

        return result;
    }
}

优化点

传入的待排序数据,直接从最后一个节点的父节点开始向上调整。如上面的代码。

这样不用浪费额外空间,一遍一遍地将最大(小)值找到。

posted on 2019-03-16 19:42  反光的小鱼儿  阅读(300)  评论(0编辑  收藏  举报