带你看懂他
Posted 小乔不掉发
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了带你看懂他相关的知识,希望对你有一定的参考价值。
数据结构中的堆:(Heap)
一、堆的概念:
- 堆 逻辑上 是一棵 完全二叉树
- 堆 物理上 是保存在 数组 中
- 满足 任意结点的值 都 大于 其 子树中结点的值,叫做大堆,或者大根堆,或者最大堆
- 满足 任意结点的值 都 小于 其 子树中结点的值,则是小堆,或者小根堆,或者最小堆
- 堆的基本作用是,快速找集合中的最值
二、堆的操作:
1、向下调整(小堆为例)
重要操作:
(前提:只有需要调整的位置不清楚,但其他位置已经满足堆的性质和了)
伪代码思路:
引出的问题:
1、怎么判断 index 对应的位置是不是叶子结点 ?
2、怎么找最小的孩子?
3、小堆是有序的吗?有序的是小堆吗?
小堆不一定是有序的,但有序的一定是小堆。
代码实现:
public class HeapTest
public static void shiftDown(int[] array, int size, int index)
while (true)
//1.判断 index 对应的下标是不是叶子结点
int leftIndex = 2 * index + 1;
if(leftIndex >= size)
return;
//2.找到两个孩子中最小的
int minIndex = leftIndex;
int rightIndex = leftIndex + 1;
if(rightIndex < size && array[rightIndex] < array[leftIndex])
minIndex = rightIndex;
//3.最小的孩子的值和 index 对应位置的值比较
if(array[index] <= array[minIndex])
return;
//4.交换最小的孩子的值和 index 的值
int temp = array[index];
array[index] = array[minIndex];
array[minIndex] = temp;
//5.把最小的孩子视为 index,循环回去(从步骤 1,继续往下走)
index = minIndex;
2、向上调整(大堆为例)
思路:伪代码分析
public static void adjustUp(int[] array,int size,int index)
1.判断 index 是不是树的根,如果是根,调整结束
2.找到 index 的父结点
3.比较父结点的值和 index 的值
4.只要父结点的值 > index,调整结束
5.交换父结点和 index 的值
6.把父结点看做 index,继续这个大循环
代码实现:
public class HeapTest
public static void adjustUp(int[] array,int size,int index)
while (true)
//1.判断 index 是不是树的根,如果是根,调整结束
if(index == 0)
break;
//2.找到 index 的父结点
int parentIndex = (index - 1) / 2;
//3.比较父结点的值和 index 的值
//4.只要父结点的值 > index,调整结束
if(array[parentIndex] > array[index])
break;
//5.交换父结点和 index 的值
int temp = array[index];
array[index] = array[parentIndex];
array[parentIndex] = temp;
//6.把父结点看做 index,继续这个大循环
index = parentIndex;
3、建堆:
public static void createHeap(int[] array, int size)
//找到层序遍历的最后一个结点下标
int lastIndex = size - 1;
//找到最后一个结点的父节点的下标
int lastParentIndex = lastIndex - 1 / 2;
//从[(size-2)/2,0] 不断地进行向下调整
for(int i = lastParentIndex; i >= 0; i++)
shiftDown(array,size,i);
三、堆的应用:
堆的应用有挺多:优先级队列、堆排序、TopK。
1、堆排序
分为两个步骤:
- 1、用当前需要排序的数据构建一个堆
- 2、不断的弹出当前堆的堆顶元素,因为小顶堆的堆顶元素一定是最小的,即可以用于排序。堆排序的本质就是,把数据构建成堆之后,弹出堆顶元素,然后互换堆顶元素和最后一个元素,不断对当前堆进行自顶向下的堆的调整,然后继续弹出。
2、TopK
TopK一般解决的是求解前K个最大或者最小的元素,或第K个最大或最小的元素。
拿到这类问题,我们的第一想法肯定是排序求解。但排序还会浪费一定的资源排序前K个元素,为了节省计算资源,我们需要思考的是怎么优化,聪明的你肯定想到了,是不是这最大的k个元素也不需要排序呢?
为此,我们需要构建包含K个元素的小顶堆,这个小顶堆用于存储,当前最大的k个元素。接着我们需要从第 k+1个元素开始扫描,如果被扫描的元素大于堆顶,则替换堆顶的元素,并调整堆,以保证堆内的k个元素,总是当前最大的k个元素。扫描完所有n-k个元素,最终堆中的k个元素,就是优化后求的TopK。
3、优先级队列(Priority Queue)
入队列操作
过程(以大堆为例):
出队列操作
过程(以大堆为例):
代码实现:
public class MyPriorityQueue
private Integer[] array;
private int size;
public MyPriorityQueue()
array = new Integer[100];
size = 0;
public Integer element()
if(size == 0)
throw new RuntimeException("空的");
return array[0];
/**
* 入队列
*/
public void add(Integer e)
array[size] = e;
size++;
adjustUp(size-1);
public void adjustUp(int index)
while (true)
if(index == 0)
break;
int parentIndex = (index - 1) / 2;
if(array[parentIndex] <= array[index])
break;
int temp = array[index];
array[index] = array[parentIndex];
array[parentIndex] = temp;
index = parentIndex;
/**
* 出队列
*/
public Integer remove()
if(size == 0)
throw new RuntimeException("空的");
int e = array[0];
array[0] = array[size - 1];
size--;
adjustDown(0);
return e;
private void adjustDown(int index)
while (true)
int leftIndex = 2 * index + 1;
if(leftIndex >= size)
return;
int minIndex = leftIndex;
int rightIndex = leftIndex + 1;
if(rightIndex < size && array[rightIndex] < array[leftIndex])
minIndex = rightIndex;
if(array[index] <= array[minIndex])
return;
int temp = array[index];
array[index] = array[minIndex];
array[minIndex] = temp;
index = minIndex;
以上是关于带你看懂他的主要内容,如果未能解决你的问题,请参考以下文章