这一篇再画下重点,看一下堆排。
在将堆排之前,有必要先澄清两个概念:堆和堆排序。
堆是一种数据结构,属于完全二叉树的一种。根据数据排列的特点,可以分为大顶堆(最大堆)和小顶堆(最小堆)。所谓的大顶堆,就是满足父节点不小于左、右子节点的树;小顶堆,则是父节点不大于左、右子节点的树。由此,我们也可以得出一个结论:大顶堆(小顶堆)是整个堆最大(小)的元素。
堆排序则是利用堆的性质来进行排序的一种算法。什么性质呢?就是我们刚刚得到的结论——大顶堆(小顶堆)是整个堆最大(小)的元素。具体来说,就是从下往上,拿每一个元素与堆顶元素交换(堆顶元素为最值,这样交换的结果实际上是拿到了排序后的最大或者最小值),交换过后,堆的性质可能会遭到破坏,需要通过沉降重新将剩余元素建立为堆,然后再交换、再沉降…直到到达堆顶。
这样也可以看出,大顶堆适合从小到大排序,小顶堆适合从大到小排序;排序的顺序和堆的名称刚好是相反的。
堆排序的实现一般分为两步:建堆和沉降(维护堆)。这两步其实是一个过程,建堆的过程就是不断地沉降维护。
通过图示,看一下建堆的过程,假设有一个长度为5的数组a=[1,5,8,4,7]。
至此,排序的二星双煞介绍完了,需要重点掌握。
条件:
数据量大,特别适合海量数据的排序,后面讲Top K问题,还会用到它。
原理:
(1)通过沉降,建立一个堆;
(2)从下往上,依次交换每个元素与堆顶元素,并维护剩余元素为一个堆;
…
(n)到达堆顶,结束。
时间复杂度:
nlog(n)
笔面试出现的频率:
在线笔试常考题,多以选择为主,让你选择建堆或者第一次交换之后的数组顺序,偶有填空。面试第一轮,手写代码的命中率比较高,笔者面过的今日头条、腾讯考过(都是以Top K的形式),其他听闻360和58也有考过。
要求必须快速准确地写出代码,并能清晰地讲明原理,画出沉降的过程。
实现:
heapify()是重点,使用了"三个if",切记切记。
public class HeapSort {
public static void main(String[] args) {
// TODO Auto-generated method stub
int[] a = { 1, 17, 3, 4, 5, 6, 7, 16, 9, 10, 11, 12, 13, 14, 15, 8 };
heapSort(a);
print(a);
}
public static void print(int[] array) {
for (int i = 0; i < array.length; i++)
System.out.print(array[i] + ", ");
System.out.println("\n");
}
//核心
public static void heapify(int[] array, int index, int length) {
int left = index * 2 + 1;
int right = index * 2 + 2;
int largest = index;
if (left < length && array[left] > array[index]) {
largest = left;
}
if (right < length && array[right] > array[largest]) {
largest = right;
}
if (index != largest) {
swap(array, largest, index);
heapify(array, largest, length);
}
}
public static void buildHeap(int[] array) {
int length = array.length;
for (int i = length / 2 - 1; i >= 0; i--) {
heapify(array, i, length);
}
}
public static void heapSort(int[] array) {
buildHeap(array);
int length = array.length;
for (int i = length - 1; i >= 0; i--) {
swap(array, 0, i);
heapify(array, 0, i);
}
}
public static void swap(int[] array, int a, int b) {
int temp = array[a];
array[a] = array[b];
array[b] = temp;
}
}