声明:关于文章的任何问题,以及知识点相关问题,可以私信博主,博主虽能力一般,但定尽全力为大家解决
文章前言:
上篇文章我们讨论过二叉树,在上篇文章中我们采用的是链式存储一颗二叉树,那么在本篇文章中将介绍一种新的存储方式,使用顺序存储方式去存储一颗二叉树(这里的二叉树一般指的是完全二叉树),这种存储方式我们管它叫堆,下面我们开始堆的学习
文章目录:
1、二叉树的顺序存储
1.1、存储方式
使用数组保存二叉树结构,方式即将二叉树用层序遍历方式放入数组中。一般只适合表示完全二叉树,因为非完全二叉树会有空间的浪费。这种方式的主要用法就是堆的表示。
①、完全二叉树编号是连续的,所以可以充分的利用数组的空间进行存储,减小空间的浪费
②、非完全二叉树由于层次遍历时会遇到空结点,在数组中也会存储此空间结点,会造成空间的浪费,空间利用率低
1.2、下标关系
2、堆
2.1、概念
关于大/小根堆的详解:
2.2、模拟实现堆
1、首先我们先需要知道堆对应java中的哪种集合(堆对应的是优先级队列)
2、先看怎样利用数组去构建一棵完全二叉树,此时按照数组的下标层次构建的仅仅只是一棵完全二叉树而已
3、调堆,既然需要建立一个堆那么就要把完全二叉树调整成大/小根堆
4、完成堆的建立还需要考虑以下问题:
5、既然已经知道了堆的底层是一个数组那么,首先定义的就是一个数组和记录数组元素个数的变量
6、有了这样基本的框架后先使用外界的数组对堆底下的数组进行赋值
7、数组存在数据后就是这样一棵完全二叉树,我们还需要把此完全二叉树调整为大/小根堆后才是真正的堆,这里是把完全二叉树调整为大根堆
①、具体向下调整思路:
②、代码实现:
③、建堆的时间复杂度
④、整体代码:
private void shiftDown(int parent,int len){
int child = 2 * parent + 1;
while(child < len){
if(child + 1 < len && elem[child] < elem[child+1]){
child = child + 1;
}
if(parent < child){
swap(parent,child);
parent = child;
child = parent * 2 + 1;
}else {
break;
}
}
}
public void createHeap(int[] arr){
for (int i = 0; i < arr.length; i++) {
elem[i] = arr[i];
usedSize++;
}
for (int parent = (elem.length - 1 - 1) / 2; parent >= 0 ; parent--) {
shiftDown(parent,usedSize);
}
}
⑤、成果展示
此时的完全二叉树已经被调整为一个大根堆,建堆完成
3、堆的应用—优先级队列
3.1、入队操作
模拟实现入队操作:
public void offer(int val){
//先判断满没满 满了进行扩容
if(isFull()){
elem = Arrays.copyOf(elem,elem.length*2);
}
elem[usedSize++] = val;
shiftUp(usedSize-1);
}
private boolean isFull(){
return elem.length == usedSize;
}
3.2、出队操作
模拟实现出队操作:
public void poll(){
if(isEmpty()){
throw new RuntimeException("堆中已经没有元素");
}
swap(0,usedSize-1);
usedSize--;
shiftDown(0,usedSize);
}
private boolean isEmpty(){
return usedSize == 0;
}
3.3、返回队首元素
3.4、java中的优先级队列
几个基本操作,以及底层是小堆
4、堆的其他应用-TopK 问题
面试中,TopK,是问得比较多的几个问题之一
思想一、将整个数组进行排序,求得前五个最大的数值即可
此方法过于简单不赘述
思想二、将数组中的所有数值都存储一个优先级队列中,出前5个满足要求的元素即可
思想三、前k个元素先入队,如果求前k个最大元素就建立一个小根堆,反之亦然,后面的数组元素依次和堆顶有元素进行比较,如果比堆顶元素大入队,重新调整成小根堆,一直到数组结束,剩下的K个元素就是数组中前K个最大的元素
5、相关堆的面试题
查找和最小的K对数字
实例:
解题思路:
class Solution {
public List<List<Integer>> kSmallestPairs(int[] nums1, int[] nums2, int k) {
PriorityQueue<int[]> heap = new PriorityQueue<>(Comparator.comparingInt(a ->
nums1[a[0]] + nums2[a[1]]));
for (int i = 0; i < Math.min(nums1.length,k); i++) {
//把数组的下标入队
heap.offer(new int[]{i,0});
}
List<List<Integer>> tmp = new ArrayList<>();
while(k-- > 0 && !heap.isEmpty()){
int[] pos = heap.poll();
tmp.add(Arrays.asList(nums1[pos[0]],nums2[pos[1]]));
if(++pos[1] < nums2.length){
heap.offer(pos);
}
}
return tmp;
}
}
6、堆的其他应用-堆排序
堆排序注意点:需要建立一个大根堆,依次堆顶元素和堆尾元素交换,剩下的元素重新向下调整,直到调整到最后一个元素
7、本章全部代码
import java.util.Arrays;
/**
* Created with IntelliJ IDEA.
* Description:
* User: Lenovo
* Date: 2022-02-23
* Time: 16:33
*/
class TestHeap {
public int[] elem;
public int usedSize;
public TestHeap(){
this.elem = new int[10];
}
private void swap(int i ,int j){
int tmp = elem[j];
elem[j] = elem[i];
elem[i] = tmp;
}
private void shiftDown(int parent,int len){
int child = 2 * parent + 1;
while(child < len){
if(child + 1 < len && elem[child] < elem[child+1]){
child = child + 1;
}
if(parent < child){
swap(parent,child);
parent = child;
child = parent * 2 + 1;
}else {
break;
}
}
}
private void shiftUp(int child){
int parent = (child - 1)/2;
while(child > 0){
if(elem[parent] < elem[child]){
swap(parent,child);
child = parent;
parent = (child - 1)/2;
}else {
break;
}
}
}
public void createHeap(int[] arr){
for (int i = 0; i < arr.length; i++) {
elem[i] = arr[i];
usedSize++;
}
for (int parent = (elem.length - 1 - 1) / 2; parent >= 0 ; parent--) {
shiftDown(parent,usedSize);
}
}
public void offer(int val){
//先判断满没满 满了进行扩容
if(isFull()){
elem = Arrays.copyOf(elem,elem.length*2);
}
elem[usedSize++] = val;
shiftUp(usedSize-1);
}
private boolean isFull(){
return elem.length == usedSize;
}
public void poll(){
if(isEmpty()){
throw new RuntimeException("堆中已经没有元素");
}
swap(0,usedSize-1);
usedSize--;
shiftDown(0,usedSize);
}
private boolean isEmpty(){
return usedSize == 0;
}
public int peek(){
if(isEmpty()) {
throw new RuntimeException("优先级队列为空!");
}
return elem[0];
}
//堆排序
public void heapSort(){
int end = usedSize - 1;
while(end > 0){
swap(end,0);
shiftDown(0,end);
end--;
}
}
}