11、堆
1、堆的结构
2、建堆方式
1、将 n 个元素逐个插⼊到⼀个空堆中,算法复杂度是 O(N * logN)
2、heapify:从最后一个非叶子节点开始,从后向前,倒序进行 siftDown 操作,算法复杂度为 O(n)
3、实现最大堆
/**
* 二叉堆是是一棵完全二叉树
* 最大堆: 所有节点都大于等于孩子节点
*/
public class MaxHeap<E extends Comparable<E>> {
private Array<E> data;
public MaxHeap() {
data = new Array<>();
}
public MaxHeap(int capacity) {
data = new Array<>(capacity);
}
public MaxHeap(E[] arr) {
heapify(arr);
}
public int size() {
return data.getSize();
}
public boolean isEmpty() {
return data.isEmpty();
}
/**
* 返回完全二叉树的数组表示中, 一个索引所表示的元素的父亲节点的索引
*/
private int parent(int index) {
if (index == 0) throw new IllegalArgumentException("index-0 doesn't have parent.");
return (index - 1) / 2;
}
/**
* 返回完全二叉树的数组表示中, 一个索引所表示的元素的左孩子节点的索引
*/
private int leftChild(int index) {
return index * 2 + 1;
}
/**
* 返回完全二叉树的数组表示中, 一个索引所表示的元素的右孩子节点的索引
*/
private int rightChild(int index) {
return index * 2 + 2;
}
/**
* 上浮: O(logN)
*/
private void siftUp(int index) {
while (index > 0 && data.get(index).compareTo(data.get(parent(index))) > 0) {
data.swap(index, parent(index));
index = parent(index);
}
}
/**
* 下沉: O(logN)
*/
private void siftDown(int index) {
while (leftChild(index) < data.getSize()) {
int bigger = leftChild(index);
if (bigger + 1 < data.getSize() && data.get(bigger + 1).compareTo(data.get(bigger)) > 0) bigger++;
if (data.get(index).compareTo(data.get(bigger)) >= 0) break;
data.swap(index, bigger);
index = bigger;
}
}
/**
* 将任意数组整理成堆的形状: O(n)
* 最后一个节点的父节点就是最后一个非叶子节点
*/
private void heapify(E[] arr) {
data = new Array<>(arr);
for (int i = parent(data.getSize() - 1); i >= 0; i--) siftDown(i);
}
/**
* O(logN)
*/
public void add(E e) {
data.addLast(e);
siftUp(data.getSize() - 1);
}
/**
* 查看堆中的最大元素
*/
public E findMax() {
if (isEmpty()) throw new RuntimeException("Heap is empty!");
return data.get(0);
}
/**
* 取出堆中的最大元素: O(logN)
*/
public E extractMax() {
E max = findMax();
data.swap(0, data.getSize() - 1);
data.removeLast();
siftDown(0);
return max;
}
/**
* 取出堆中的最大元素, 并替换为一个新元素
*/
public E replace(E e) {
E max = findMax();
data.set(0, e);
siftDown(0);
return max;
}
}
4、实现最小堆
/**
* 二叉堆是是一棵完全二叉树
* 最大堆: 所有节点都小于等于孩子节点
*/
public class MinHeap<E extends Comparable<E>> {
private Array<E> data;
public MinHeap() {
data = new Array<>();
}
public MinHeap(int capacity) {
data = new Array<>(capacity);
}
public MinHeap(E[] arr) {
heapify(arr);
}
public int size() {
return data.getSize();
}
public boolean isEmpty() {
return data.isEmpty();
}
private int parent(int index) {
if (index == 0) throw new IllegalArgumentException("index-0 doesn't have parent.");
return (index - 1) / 2;
}
private int leftChild(int index) {
return index * 2 + 1;
}
private int rightChild(int index) {
return index * 2 + 2;
}
private void siftUp(int index) {
while (index > 0 && data.get(index).compareTo(data.get(parent(index))) < 0) {
data.swap(index, parent(index));
index = parent(index);
}
}
private void siftDown(int index) {
while (leftChild(index) < data.getSize()) {
int smaller = leftChild(index);
if (smaller + 1 < data.getSize() && data.get(smaller + 1).compareTo(data.get(smaller)) < 0) smaller++;
if (data.get(index).compareTo(data.get(smaller)) <= 0) break;
data.swap(index, smaller);
index = smaller;
}
}
private void heapify(E[] arr) {
data = new Array<>(arr);
for (int i = parent(data.getSize() - 1); i >= 0; i--) siftDown(i);
}
public void add(E e) {
data.addLast(e);
siftUp(data.getSize() - 1);
}
public E findMin() {
if (isEmpty()) throw new RuntimeException("Heap is empty!");
return data.get(0);
}
public E extractMin() {
E min = findMin();
data.swap(0, data.getSize() - 1);
data.removeLast();
siftDown(0);
return min;
}
public E replace(E e) {
E min = findMin();
data.set(0, e);
siftDown(0);
return min;
}
}
5、优先队列
public interface Queue<E> {
void enqueue(E e);
E dequeue();
E getFront();
int getSize();
boolean isEmpty();
}
/**
* 基于最大堆实现的优先队列
*/
public class PriorityQueue<E extends Comparable<E>> implements Queue<E> {
private final MaxHeap<E> maxHeap;
public PriorityQueue() {
maxHeap = new MaxHeap<>();
}
@Override
public void enqueue(E e) {
maxHeap.add(e);
}
@Override
public E dequeue() {
return maxHeap.extractMax();
}
@Override
public E getFront() {
return maxHeap.findMax();
}
@Override
public int getSize() {
return maxHeap.size();
}
@Override
public boolean isEmpty() {
return maxHeap.isEmpty();
}
}
6、堆排序
/**
* 堆排序: O(N * logN)
* 基于最大堆来实现
*/
public class HeapSort {
private HeapSort() {
}
/**
* 自顶向下建堆: O(N * logN)
*/
public static <E extends Comparable<E>> void sort1(E[] arr) {
MaxHeap<E> maxHeap = new MaxHeap<>();
for (E e : arr) maxHeap.add(e); // 自顶向下建堆: O(N * logN)
for (int i = arr.length - 1; i >= 0; i--) arr[i] = maxHeap.extractMax();
}
/**
* 堆排序优化, 自底向上建堆: O(n)
*/
public static <E extends Comparable<E>> void sort2(E[] arr) {
if (arr == null || arr.length <= 1) return;
// 把 arr[] 变成最大堆, 自底向上建堆: O(n)
int last = arr.length - 1;
for (int i = (last - 1) / 2; i >= 0; i--) siftDown(arr, i, last);
while (last >= 1) {
swap(arr, 0, last);
last--;
siftDown(arr, 0, last);
}
}
/**
* 对 arr[0, last] 所形成的最大堆中, 索引 index 的元素, 执行 siftDown
*/
private static <E extends Comparable<E>> void siftDown(E[] arr, int index, int last) {
while (index * 2 + 1 <= last) {
int bigger = index * 2 + 1;
if (bigger + 1 <= last && arr[bigger + 1].compareTo(arr[bigger]) > 0) bigger++;
if (arr[index].compareTo(arr[bigger]) >= 0) break;
swap(arr, index, bigger);
index = bigger;
}
}
private static <E> void swap(E[] arr, int a, int b) {
E k = arr[a];
arr[a] = arr[b];
arr[b] = k;
}
}
/**
* 堆排序: O(N * logN)
* 基于最小堆来实现
*/
public class HeapSort {
private HeapSort() {
}
public static <E extends Comparable<E>> void sort1(E[] arr) {
MinHeap<E> minHeap = new MinHeap<>();
for (E e : arr) minHeap.add(e);
for (int i = arr.length - 1; i >= 0; i--) arr[i] = minHeap.extractMin();
}
public static <E extends Comparable<E>> void sort2(E[] arr) {
if (arr == null || arr.length <= 1) return;
int last = arr.length - 1;
for (int i = (last - 1) / 2; i >= 0; i--) siftDown(arr, i, last);
while (last >= 1) {
swap(arr, 0, last);
last--;
siftDown(arr, 0, last);
}
}
/**
* 对 arr[0, last] 所形成的最小堆中, 索引 index 的元素, 执行 siftDown
*/
private static <E extends Comparable<E>> void siftDown(E[] arr, int index, int last) {
while (index * 2 + 1 <= last) {
int smaller = index * 2 + 1;
if (smaller + 1 <= last && arr[smaller + 1].compareTo(arr[smaller]) < 0) smaller++;
if (arr[index].compareTo(arr[smaller]) <= 0) break;
swap(arr, index, smaller);
index = smaller;
}
}
private static <E> void swap(E[] arr, int a, int b) {
E k = arr[a];
arr[a] = arr[b];
arr[b] = k;
}
}
7、Select K 与 Top K 问题
7.1、Select K 问题
/**
* 求数组升序排序好后, 从右往左数第 K 个元素, 它的索引是 length - K
* 直接使用最小堆
* 复杂度: O(N * logK)
*/
public class FindKthLargest {
public static int findKthLargest(int[] arr, int k) {
int[] minHeap = Arrays.copyOf(arr, k);
int last = k - 1;
for (int i = (last - 1) / 2; i >= 0; i--) siftDown(minHeap, i);
for (int i = k; i < arr.length; i++) {
if (arr[i] > minHeap[0]) {
minHeap[0] = arr[i];
siftDown(minHeap, 0);
}
}
return minHeap[0];
}
private static void siftDown(int[] arr, int index) {
int last = arr.length - 1;
while (index * 2 + 1 <= last) {
int smaller = index * 2 + 1;
if (smaller + 1 <= last && arr[smaller + 1] < arr[smaller]) smaller++;
if (arr[index] <= arr[smaller]) break;
swap(arr, index, smaller);
index = smaller;
}
}
private static void swap(int[] arr, int a, int b) {
int k = arr[a];
arr[a] = arr[b];
arr[b] = k;
}
}
7.2、Top K 问题
/**
* 求数组升序排序好后, 从左往右数共 K 个元素
* 直接使用最大堆
* 复杂度: O(N * logK)
*/
public class GetLeastNumbers {
public static int[] getLeastNumbers(int[] arr, int k) {
if (k == 0) return new int[0];
int[] maxHeap = Arrays.copyOf(arr, k);
int last = k - 1;
for (int i = (last - 1) / 2; i >= 0; i--) siftDown(maxHeap, i);
for (int i = k; i < arr.length; i++) {
if (arr[i] < maxHeap[0]) {
maxHeap[0] = arr[i];
siftDown(maxHeap, 0);
}
}
return maxHeap;
}
private static void siftDown(int[] arr, int index) {
int last = arr.length - 1;
while (index * 2 + 1 <= last) {
int bigger = index * 2 + 1;
if (bigger + 1 <= last && arr[bigger + 1] > arr[bigger]) bigger++;
if (arr[index] >= arr[bigger]) break;
swap(arr, index, bigger);
index = bigger;
}
}
private static void swap(int[] arr, int a, int b) {
int k = arr[a];
arr[a] = arr[b];
arr[b] = k;
}
}
8、快排思想和优先队列比较
9、更多话题
堆
- d 叉堆
- 索引堆:之前根据内容排序,现在根据索引排序
- 二项堆、斐波那契堆
广义队列
- 普通队列
- 优先队列
- 随机队列
本文来自博客园,作者:lidongdongdong~,转载请注明原文链接:https://www.cnblogs.com/lidong422339/p/17304833.html