极大堆(优先队列)的构建

1.先明确堆的概念:

* 堆的定义详解:
* 堆即为一个满二叉树,或是一个顺序完全二叉树(即如果没有满二叉树,则有且仅有一个父结点只有左儿子,没有右儿子
* 且这个结点一定是在倒数第二层的最右边)
*
* 堆在内存中的实际结构就是数组,只不过给它的下标赋予了一些额外的含义
* 在数组中,若一个结点下标为i,则其父节点编号为(i-1)/2,左儿子结点编号为2*i+1,右儿子结点编号为2*i+2

2.大根堆的概念:
*大根堆(优先队列):两个子节点一定小于其父节点的堆,称为大根堆

 

3.大根堆的两大重要的调整函数(其余的一切处理操作都从这两个函数出发):

(1).较大值的向上调整:

 1 //已知要插入的下标,合理的调整插入,使得插入后整个数组的元素位置仍满足大根堆
 2     //实质上为向上的调整(即把应该往上调的数给它往上拉)
 3     public static void HeapInsert(int[] arr,int index)
 4     {
 5         while (arr[index] > arr[(index - 1) / 2]) {//子节点大于它的父节点时,将子节点向上调整
 6                                                    //(因为根节点的父节点是本身,所以不会死循环)
 7             swap(arr,index,(index - 1) / 2);
 8             index = (index - 1) / 2;//继续往上去处理上一层的父结点
 9         }
10     }

(2).较小值的向下调整:

 1 //直接对数组元素位置进行调整,使其满足大根堆的特点
 2     //实质上为向下的调整(即把应该往下调的数给它往下降)
 3     public static void Heapify(int[] arr,int index,int heapSize) 
 4     {
 5         int left = 2 * index + 1;//获取这个元素的左儿子的下标
 6         while (left < heapSize) {//当这个元组还有左儿子的时候
 7             int largest = left;//先设左右儿子的最大元素就是左儿子
 8             if (left + 1 < heapSize && arr[left] < arr[left + 1]) {//在有右儿子的情况,且右儿子比左儿子大时
 9                 largest = left + 1;
10             }
11             if (arr[largest] > arr[index]) {//即左右儿子的最大值比父亲大时
12                 swap(arr,index,largest);
13             }
14             else {//父亲结点的值比左右儿子都大(说明这一层不需要调整,也就没对下一层产生影响,所以直接退出循环)
15                 break;
16             }
17             index = largest;//对被调整交换的这个儿子,会影响到它和它的儿子的关系,所以也需要进行调整
18                             //即此时调整的结点从父节点变成了儿子结点
19             left = 2 * index + 1;//为下一次循环做好准备(提前找到下一个要调整的父节点的左儿子的位置) 
20         } 
21     }

 

总结:

* 对HeapInsert和Heapify的总结:
* 1.其本质上都是对这个堆进行合理调整,使其满足大根堆的规则要求
* 2.如果是一个大数进入(比父节点大)要向上调整,则使用HeapInsert
* 3.如果是一个小数进入(比父节点小)要向下调整,则使用Heapify

4.大根堆的两个重要的实际应用:

(1).将一个普通数组转换为大根堆

1 //给定一个数组,将其调整为符合大根堆类型的数(法一)(时间复杂度为O(n*log n))
2     public static void GetMaxHeap1(int[] arr) 
3     {
4         int heapSize = arr.length;
5         for (int i = 0; i < heapSize; i++) {
6             //因为每次新插入的数肯定都在最下层了,所以只需要往上调整即可
7             HeapInsert(arr,i);
8         }
9     }
1 //给定一个数组,将其调整为符合大根堆类型的数(法二)(时间复杂度为O(n))所以采用该方法
2     public static void GetMaxHeap2(int[] arr) 
3     {
4         int heapSize = arr.length;
5         for (int i = heapSize - 1; i >= 0; i--) {
6             Heapify(arr,i,heapSize);
7         }
8     }

 

(2).得到这个数组的最大值,并删去这个最大值

 1 //获得这个数组的最大值,并将其移除,产生新的大根堆
 2     public static int GetMaxAndDelete(int[] arr) 
 3     {
 4         int ans = arr[0];//把这个最大值给存起来
 5         swap(arr,0,arr.length - 1);//把最后面的那个数给换上来
 6         int heapSize = arr.length - 1;//改变数组(堆)的长度
 7         //接下来对堆进行调整
 8         //因为是把尾部的值提到了顶部来,且又位于最上层,所以将其进行向下调整即可
 9         Heapify(arr,0,heapSize);
10         return ans;
11     }

 

posted @ 2022-04-05 22:05  jue1e0  阅读(183)  评论(0)    收藏  举报