极大堆(优先队列)的构建
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 }