原理
堆
其实就是一种特殊的完全二叉树,分为大顶堆和小顶堆。
大顶堆
就是指父节点 >= 左右孩子结点, 而左右孩子结点之间的大小关系随意。小顶堆反之。
堆排序基本思路
就是先把序列构建成大顶堆序(升序用大顶堆)
然后大顶堆的根节点和最后一个结点交换位置,将最大元素沉到数组末端
这样一来每交换一次就得到当前序列的最大值,并把它放在了最后面,接着把剩下的序列继续构建成大顶堆,重复上面动作,直到序列只剩一个。
复杂度
时间复杂度
分为两部分:初始化建堆和重建堆。
初始化堆的时间复杂度: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; } }
优化点
传入的待排序数据,直接从最后一个节点的父节点开始向上调整。如上面的代码。
这样不用浪费额外空间,一遍一遍地将最大(小)值找到。