单调队列和优先队列

本篇记录下一下关于单调队列和优先队列(堆)的方法以及解题思路.

文章目录

  • 一. 单调队列
    • 1. 绝对差不超过限制得最长连续子数组
    • 2. 跳跃游戏 VI
    • 3. 设计自助结算系统
    • 4. 和至少为k的最短子数组
    • 5. 满足不等式的最大值
  • 二. 优先队列
    • 1. 最后一块石头的重量
    • 2. 数据流中的第k大元素
    • 3. 拼车
      • (1)前缀和
      • (2)优先队列(堆)
    • 4. 滑动窗口最大值
      • (1)单调队列
      • (2)优先队列

一. 单调队列

1. 绝对差不超过限制得最长连续子数组

在这里插入图片描述
这道题是要求我们求出连续子数组中最大值和最小值差,差小于等于所给的limit。
然后返回连续子数组最大的长度。

  1. 找出子数组中最大值和最小值
  2. 两值相减去,小于等于limt,取其长度,选择那个最长的。

所以我们可以利用两个指针锁定窗口,也即是长度,然后在窗口中更新不断的更新最大值和最小值。

  • 所以我们可以维护两个单调增减队列,使队首永远是最大值或者最小值。
  • 当窗口中最大值 - 最小值 > limit 时候就需要修改窗口大小,使left++
  • 如果说nums[left] 这个值呢正好就是最大值或者最小值,那么相应的它也得出队列。
#define MAX_SIZE 100000int Max(int x , int y)
{return x > y ? x : y;
}int longestSubarray(int* nums, int numsSize, int limit) 
{//增减单调队列int* deQueue = (int*)malloc(sizeof(int) * MAX_SIZE);int* inQueue = (int*)malloc(sizeof(int) * MAX_SIZE);int deFront = 0, deRear = 0, inFront = 0, inRear = 0;//int left = 0, right = 0, result = 0;while(right < numsSize){//维护单调递减队列while(deFront < deRear && deQueue[deRear - 1] < nums[right]){deRear--;}//维护单调递增队列while(inFront < inRear && inQueue[inRear - 1] > nums[right]){inRear--;}//入队列deQueue[deRear++] = nums[right];inQueue[inRear++] = nums[right++];while(deFront < deRear && inFront < inRear && deQueue[deFront] - inQueue[inFront] > limit){//修改窗口大小,即left往前。//对于两个队列来说,如果删除的left 是当前窗口的最大值或者最小值,那么相对应得队首也得删除if(deQueue[deFront] == nums[left]){deFront++;}if(inQueue[inFront] == nums[left]){inFront++;}left++;}//更新窗口大小 取最大值result = Max(result, right - left);}return result;
}

2. 跳跃游戏 VI

在这里插入图片描述
这道题也是要求我们从0下标位置开始,然后跳k步,返回经过之和的最大值。
说白了就是说,在给定的k窗口大小中,找出从0起点位置,和窗口中各个数据总和的最大值,然后让那个数成为了新的起点,接着运算。

  • 需要一个dp数组和单调队列来实现整个代码。
  • dp数组里面存放的是和,队列中存储的下标,而下标所对应的dp一定是递增的,保证越来越大,这样子才可以使最后的结果越来越大。

在整个遍历数组的过程中,对于每个数据分为三个步骤。

  1. 首先判断当前的窗口大小是否满足 k,又因为使单调增队列,所以将队首更新成下一个就好了。
  2. 接下来就是算出当前的dp[i],也就是front + nums[i]的值。
  3. 算出当前的dp[i]后,需要保证队列是单调增的,所以如果发现队尾的数据和当前的dp不满足单调增时候,需要将队尾进行出队操作。
int maxResult(int* nums, int numsSize, int k)
{int* dp = (int*)malloc(sizeof(int) * numsSize);int* queue = (int*)malloc(sizeof(int) * numsSize);int front = 0, rear = 0;//dp数组存放的最大的分数dp[0] = nums[0];//队列中,队首永远会是当前的最优解,整个队列也是呈递增的。queue[rear++] = 0;for (int i = 1; i < numsSize; i++){//最多可以跳K步,判断是否需要收缩窗口大小if(front < rear && queue[front] < i - k){front++;}//dpdp[i] = dp[queue[front]] + nums[i];//维护单调队列while(front < rear && dp[queue[rear - 1]] <= dp[i]){rear--;}//入队列queue[rear++] = i;}return dp[numsSize - 1];
}

3. 设计自助结算系统

在这里插入图片描述
这道题其实就是要求我们设计一个队列出来,遵循现进先出的原则,但是在此基础上增加了一个要求,就是获取最大值的要求,你可以遍历整个队列获取,但是不符合题,题目中要求是O(1).
所以我们可以维护一个单调递减的队列出来,这样就可以保证说这个递减队列的队首位置永远是最大的。
这道题和栈中的设计一个最小栈题目是很类似的,那道题目是设计两个栈,而这一道是设计两个队列。

#define MAX_SIZE 10000typedef struct
{int* queue;     //正常队列,待结算的商品int* deQueue;   //单调递减队列,也就是说队首永远是最大的待结商品int front,rear,deFront,deRear;
} Checkout;Checkout* checkoutCreate()
{Checkout* obj = (Checkout*)malloc(sizeof(Checkout));obj -> queue = (int*)malloc(sizeof(int) * MAX_SIZE);obj -> deQueue = (int*)malloc(sizeof(int) * MAX_SIZE);obj -> front = obj -> rear = obj -> deFront = obj -> deRear = 0;return obj;
}int checkoutGet_max(Checkout* obj)
{if(obj -> front == obj -> rear){//队列为空return -1;}//递减队列的队首return obj -> deQueue[obj -> deFront];
}void checkoutAdd(Checkout* obj, int value)
{//入正常队列obj -> queue[(obj -> rear)++] = value; //入单调递减队列while(obj -> deFront != obj -> deRear && obj -> deQueue[obj -> deRear - 1] < value){obj -> deRear--;}obj -> deQueue[(obj -> deRear)++] = value;
}int checkoutRemove(Checkout* obj)
{if(obj -> front == obj -> rear){//队列为空return -1;}if(obj -> queue[obj -> front] == obj -> deQueue[obj -> deFront]){//同时出队列obj -> deFront++;}return obj -> queue[(obj -> front)++];
}void checkoutFree(Checkout* obj)
{free(obj -> queue);free(obj -> deQueue);free(obj);    
}/*** Your Checkout struct will be instantiated and called as such:* Checkout* obj = checkoutCreate();* int param_1 = checkoutGet_max(obj);* checkoutAdd(obj, value);* int param_3 = checkoutRemove(obj);* checkoutFree(obj);
*/

4. 和至少为k的最短子数组

在这里插入图片描述
这道题要求我们求出连续的子数组的和最少是k的最短长度是多少。
经典的滑动窗口 + 单调队列的解法。

  • 这道题需要反复的取求数组的和,所以我们先预处理一下前缀和数组。
  • 当求出前缀和数组后,知道一些基本的道理有助于下面的运算。
  • 一下这个公式应该都会吧,

给一个前缀和数组 prefixSum, 然后 i < j
pre[i] = nums[0] + nums[1] + nums[2] + nums[i].
pre[j] = nums[0] + nums[1] + nums[2] + nums[i] + nums[i + 1] +…+ nums[j]
pre[i,j] = pre[j] - pre[i].

  • i 和 j 其实就是滑动窗口中的左右边界。
  • 当 i ~ j 的和求出来后,发现和大于等与k的话。
  • 我们可以计算出该窗口的长度 i - j;
  • 同时缩小窗口大小。
  • 如果没有大于等于k,我们继续扩大窗口的大小。
    下图是滑窗口简单的运算过程:举例有点特殊,k是6,所以一直在不断的更新

在这里插入图片描述

  • 但是这个不就是用双指针就可以做到吗?为什么需要优先队列,因为还有一个细节需要注意。
  • 我们在遍历nums数组的时候,必须保证前缀和是单调递增的,
  • 如果发现当前的前缀和比队尾前缀和小,那么我们就将当前的变成新的队尾
  • 看下下面的例子,当只是用滑动窗口处理这道题的时候,最后的结果一定是4,也就是整个数组的和35,窗口的大小是从0到4.(绿色)
  • 但是呢,发现15+15=30长度为2,2才是最后的答案,所以前缀和数组中收0 2 3的方式,因为prefix[2] < prefix[1] ,将其更换掉了。
    在这里插入图片描述
int Min(int x, int y)
{return x < y ? x : y;
}int shortestSubarray(int* nums, int numsSize, int k)
{int n = numsSize, i;//前缀和long long* prefixSum = (long long*)malloc(sizeof(long long) * (n + 1));prefixSum[0] = 0;for (i = 1; i <= n; i++){prefixSum[i] = prefixSum[i - 1] + nums[i - 1];}//递增队列,队列中存储的是前i个的前缀和 queue[i]。int* dequeue = (int*)malloc(sizeof(int) * (n + 1));int front = 0, rear = 0;dequeue[rear++] = 0;    //初始化,也就是前0个的前缀和。int ans = n + 1;for (i = 1; i <= n; i++){//判断是否需要更新窗口大小,left,左边的窗口while (front != rear && prefixSum[i] - prefixSum[dequeue[front]] >= k){   ans = Min(ans,i - dequeue[front]);front++;}//维护单调队列,保证队列中下标所存储的前缀和是递增的.while (front != rear && prefixSum[i] <= prefixSum[dequeue[rear - 1]]){rear--;}dequeue[rear++] = i;}return ans == n + 1 ? - 1 : ans;
}

5. 满足不等式的最大值

在这里插入图片描述
这道题是要求我们在一个范围,按公式求值,返回最大的那个值。
范围就是| xi - xj | <= k. 公式是:yi + yj + | xi - xj |.

对公式简单的进行推导一下:
既然说题目中说了是升序排列,所以一定有i < j。
那么|xi - xj| 可以变换成 xj - xi。
yi + yj + xj - xi = (xj + yj) + (yi - xi)的形式

  • 有了上面新的公式后呢,我们规定(xj + yj)为新节点,(yi - xi)为队列中存储的节点。
  • 那么要想使此式子变大,新节点大小不可控,遍历到什么使什么,但是(yi - xi)既然使遍历过的节点,存放在队列中的,那么我们可以在存放的时候,就选择越来越大的存放,不用讲小的存进去,就好了。

对于整体的数组遍历可分成一下几种步骤:

  1. 判断窗口大小是否符合,队首即窗口left
  2. 更新ans,利用这个公式去更新(xj + yj) + (yi - xi)。
  3. 维护队列,使队列成一个单调递增的形式,越来越大的,用当前(yi - xi)和队尾的进行比较。
int Max(int x, int y)
{return x > y ? x : y;
}int findMaxValueOfEquation(int **points, int pointsSize, int *pointsColSize, int k)
{int* queue = (int*)malloc(sizeof(int) * pointsSize);int front = 0, rear = 0;int ans = INT_MIN;queue[rear++] = 0;for (int i = 1; i < pointsSize; i++){//先判断窗口大小是否符合while(front != rear && points[i][0] - points[queue[front]][0] > k){front++;}if(front != rear){//队列不为空则尝试更新ans//(xj + yj) + (yi - xi)ans = Max(ans,points[i][0] + points[i][1] + points[queue[front]][1] - points[queue[front]][0]);}//维护单调递增队列,使队尾永远保持大的值。//判断那一个的 yi - xi 更大 while(front != rear && points[queue[rear - 1]][1] - points[queue[rear - 1]][0] <= points[i][1] - points[i][0]){rear--;}//pushqueue[rear++] = i;}free(queue);return ans;
}  

二. 优先队列

1. 最后一块石头的重量

在这里插入图片描述
这道题目要求我们每次从数组中选择出两个最大值,如果他俩相等抵消,否则的话就将差入再入数组,最后返回仅省一个石头的重量,也可以没有石头(return 0).

  • 这道题我们肯定能使用排序来做,选出两个最大值后,判断是否需要插入差值。
  • 然后再进行排序,直到数组中的元素不够两个时候。
  • 我们可以在此思想上进一步优化优化一下,不用对数组每个元素都进行排序。
  • 我们理由优先队列(堆),建立一个大顶堆,是堆的首元素永远是最大的。
  • 然后对其进行相应的出队列,入队列即可。
void Swap(int* x, int* y)
{int tmp = *x;*x = *y;*y = tmp;
}//维护堆
void Heapify(int* nums, int numsSize, int i)
{int maxIndex = i, lc = 2 * i + 1, rc = 2 * i + 2;if (lc < numsSize && nums[lc] > nums[maxIndex]){maxIndex = lc;}if (rc < numsSize && nums[rc] > nums[maxIndex]){maxIndex = rc;}if (maxIndex != i){Swap(&nums[maxIndex], &nums[i]);Heapify(nums, numsSize, maxIndex);}
}//将val插入堆中去
void HeapPush(int* nums, int* numsSize, int val)
{//在最后面插入nums[(*numsSize)++] = val;//维护当前插入后的父亲节点for (int parent = (*numsSize - 2) / 2; parent >= 0; parent = (parent - 1) / 2){Heapify(nums, *numsSize, parent);//最后一次if (parent == 0){break;}}
}//删除堆顶元素
void HeapPop(int* nums, int* numsSize)
{//将堆顶元素和最后一个交换Swap(&nums[0], &nums[--(*numsSize)]);//重新维护堆Heapify(nums, *numsSize, 0);
}int lastStoneWeight(int* stones, int stonesSize)
{if (stonesSize == 1){return stones[0];}if (stonesSize == 2){return abs(stones[0] - stones[1]);}int i, n = stonesSize;//创建堆for (i = (n - 1) / 2; i >= 0; i--){Heapify(stones, n, i);}while (n >= 2){//获取两次堆顶的元素int x = stones[0];HeapPop(stones, &n);int y = stones[0];HeapPop(stones, &n);//如果两次不一样,那么相减后入堆if (x > y){HeapPush(stones, &n, x - y);}}//堆为空的话,说明抵消没了。return n == 0 ? 0 : stones[0];
}

下面是对上述代码中的所提到堆函数进行简单的一些讲解:
堆这种数据结构是二叉树的一种形式,其在数组中存放,实现起来还是挺方便的。
下面简单的讲一下heap函数中一些细节。

  • Pop: 删除堆顶元素,其实就跟堆排序一样,将堆顶数据和最后一个交换,然后size减小,重新维护堆顶就好了。
  • Push:插入元素,这个有些细节需要注意,在当前队列(堆)得末尾直接插入元素,同样也是需要进行维护的,在数组中存储二叉树,我们可以用利用下标直接得到父亲节点和左右孩子。
  • 对于我上面的是根节点存放在0下标的位置,
  • 已知parent = i, leftchild = 2 * i + 1, rightChild = 2 * i + 2;
  • 已知(左右通用)child = i,parent = (i - 1) / 2;
    还有一种是根节点存放在1号索引处是又一种方式,这里就不仔细说了。

2. 数据流中的第k大元素

在这里插入图片描述
这道题要求我们设计一种数据结构,可以返回第k大的元素,简单来讲就是说给你一个数组,然后对其进行降序排序,第k个就是第k大的元素,用数组可以做出来,但是对于这道题目来说,时间耗费非常大,这道题可以使用堆来实现。

  • 我们来维护一个小顶堆出来,保证每次堆顶的数据都是最小的。
  • 接下来终点来了:整个堆的大小一定得是 k,那么既然大小是k,所以堆顶的元素肯定就是第k个最大的数据了,有点抽象还是看图吧:
    在这里插入图片描述
    就比如上图中,nums数组大小是4,但是我们的堆中只放k个,至于是如何舍弃2的,待会再说。
  • 当后续新的数据需要add到堆中时候,如果数据比堆顶小,那么就不需要插入堆中,你插进来不会影响第k大元素,没有意义,就不插入了,
  • 相反,如果说新的数据比堆顶的大,那么将其将堆顶数据替换后,继续维护堆。
    下面是测试用例1的模拟过程:
    在这里插入图片描述
    那对于刚开始的初始化堆,从刚开始初始化的时候,就选择数组中的前k个最大的就好了,如果数组中不够k个,将整个数组入堆就好了。
typedef struct
{int* heap;int capacity,k; // k是整个堆的大小,capacity 是当前堆中的元素
} KthLargest;int cmp_int(const void* x, const void* y)
{return *(int*)x - *(int*)y;
}
void Swap(int* x, int* y)
{int tmp = *x;*x = *y;*y = tmp;
}void Heapfiy(int* nums, int size, int i)
{int minIndex = i, lc = 2 * i + 1, rc = 2 * i + 2;if(lc < size && nums[lc] < nums[minIndex]){minIndex = lc;}if(rc < size && nums[rc] < nums[minIndex]){minIndex = rc;}if(minIndex != i){Swap(&nums[i],&nums[minIndex]);Heapfiy(nums,size,minIndex);}
}KthLargest* kthLargestCreate(int k, int* nums, int numsSize)
{KthLargest* obj = (KthLargest*)malloc(sizeof(KthLargest));obj -> heap = (int*)malloc(sizeof(int) * k);    //堆中最多放k个数据,然后维护一个小顶堆,堆顶就是第k个最大元素。int i;qsort(nums,numsSize,sizeof(int),cmp_int);if(numsSize < k){//将数组中的所有元素入堆即可for (i = 0; i < numsSize; i++){obj -> heap[i] = nums[i];}obj -> capacity = numsSize;}else{int pos = 0;//只入后k个for (i = numsSize - k; i < numsSize; i++){obj -> heap[pos++] = nums[i];}obj -> capacity = k;}obj -> k = k;return obj;
}int kthLargestAdd(KthLargest* obj, int val)
{if(obj -> capacity != obj -> k){//需要插入堆obj -> heap[(obj -> capacity)++] = val;for (int parent = (obj -> capacity - 2) / 2; parent >= 0; parent = (parent - 1) / 2){Heapfiy(obj -> heap,obj -> capacity, parent);if(parent == 0){break;}}return obj -> heap[0];}if(val > obj -> heap[0]){//新元素大于堆顶则需要入堆obj -> heap[0] = val;Heapfiy(obj -> heap,obj -> capacity, 0);}return obj -> heap[0];
}void kthLargestFree(KthLargest* obj)
{free(obj -> heap);free(obj);
}

3. 拼车

在这里插入图片描述
这道题是给我们一个二维数组,里面放着当前站上几个人,然后到哪里下。
问我们会不会超载。

(1)前缀和

  • 最直观的我们遍历一遍所给的数组,将其起点和终点的位置进行相应的赋值
  • 看下图,我在每个位置进行相应的增人减人操作
  • 那total 进行相应的自增,如果total > capacity 时候就说明超载了
    在这里插入图片描述
    代码如下:
#define MAX_SIZE 1001bool carPooling(int** trips, int tripsSize, int* tripsColSize, int capacity)
{int* bucket = (int*)malloc(sizeof(int) * MAX_SIZE);int i, total = 0;memset(bucket,0,sizeof(int) * MAX_SIZE);for (i = 0; i < tripsSize; i++){int num = trips[i][0], from = trips[i][1], to = trips[i][2];bucket[from] += num;bucket[to] -= num;}for (i = 0; i < MAX_SIZE; i++){total += bucket[i];if(total > capacity){return false;}}return true;
}

(2)优先队列(堆)

也可以使用优先队列进行模拟实现

  • 首先对原来的数组进行排序,按照它的上车顺序进行升序排序。
  • 然后使用优先队列,其队首永远是最先下车的那批乘客。
  • 就是说最先下车的位置 <= 当前上车的位置。
  • 就将其出队列。
  • 还是如果total大于了capacity的话返回false

下面是代码思路还是比较简单的:

bool carPooling(int** trips, int tripsSize, int* tripsColSize, int capacity)
{ //首先给trips 排序,使其上车起点是递增的qsort(trips,tripsSize,sizeof(trips[0]),cmp_from);int* heap = (int*)malloc(sizeof(int) * tripsSize);int size = 0,total = 0; //size 是堆的大小, total 是当前车上的乘客for (int i = 0; i < tripsSize; i++){//最先下车的位置 <= 当前上车的位置while(size != 0 && trips[heap[0]][2] <= trips[i][1]){total -= trips[heap[0]][0];HeapPop(trips,heap,&size);}//入堆HeapPush(trips,heap,&size,i);total += trips[i][0];if(total > capacity){return false;}}return true;
}

C语言需要自己实现一下堆.

//因为堆中存储的是下标,需要将trips整个数组传过来。
void Heapfiy(int** trips, int* nums, int size, int i)
{int minIndex = i, lc = 2 * i + 1, rc = 2 * i + 2;if(lc < size && trips[nums[lc]][2] < trips[nums[minIndex]][2]){minIndex = lc;}if(rc < size && trips[nums[rc]][2] < trips[nums[minIndex]][2]){minIndex = rc;}if(minIndex != i){Swap(&nums[minIndex],&nums[i]);Heapfiy(trips,nums,size,minIndex);}
}//插入堆
void HeapPush(int** trips,int* nums, int* size, int i)
{nums[(*size)++] = i;for (int parent = (*size - 2) / 2; parent >= 0; parent = (parent - 1) / 2){Heapfiy(trips,nums,*size,parent);if(parent == 0){break;}}
}//删除
void HeapPop(int** trips, int* nums, int* size)
{Swap(&nums[0],&nums[--(*size)]);Heapfiy(trips,nums,*size,0);
}

4. 滑动窗口最大值

在这里插入图片描述
这道题给我们一个k大小的窗口,向右边的滑动,并且求出每一个窗口内的最大值。
下面是两种解法,在这道题目中,单调队列的时间更快。

(1)单调队列

第一种办法我们可以维护一个单调队列出来,此队列为递增序列。

  • 利用两个指针来锁定窗口的大小,left 和 right,
  • 如果right - left != k,就说明窗口还没形成,right一直往后走
  • 否则left++。
    在这里插入图片描述
    接下来再维护一个单调队列就好了。
  • 队列要保持一个单调递减的队列,这样队首永远都是属于一个最大值。
  • 当我们进行出队列的时候,需要判断当前的left是否是队首的元素,如果是,那么队首也随之而然的消除。
  • 当每次需要更新窗口大小的时候,就是收获结果的时候,我下面是直接在数组中进行修改的。
int* maxSlidingWindow(int* nums, int numsSize, int k, int* returnSize)
{int* deQueue = (int*)malloc(sizeof(int) * numsSize);int front = 0, rear = 0;int left = 0;for (int i = 0; i < numsSize; i++){if(i - left == k){//调整窗口大小 && 更新值nums[left] = nums[deQueue[front]];if(left == deQueue[front]){front++;}left++;}//维护单调队列while(front != rear && nums[deQueue[rear - 1]] < nums[i]){rear--;}//入队列deQueue[rear++] = i;}nums[left++] = nums[deQueue[front]];*returnSize = left;return nums;
}

(2)优先队列

根据上面的单调队列我们可以发现,只需要对于每个窗口获取最大值就好,所以我们同样也可以用优先队列来存储最大值利用大顶堆。

  • 优先队列中存储的依旧是下标,思路其实和单调队列的思路是一样的。
  • 只是换了一种获取最大值的数据结构而已。
  • 单调队列:获取答案后出队列只需要将等于left对首出队列即可。
  • 优先队列:获取答案后出队列需要将 小于等于left的所有节点出队列
  • 所以优先队列需要一个循环出队列,而单调队列则需要一个if就ok了。

听起来好像优先队列更麻烦,其实不然,因为单调队列还需要维护一下,而优先队列直接插入就好了。

int* maxSlidingWindow(int* nums, int numsSize, int k, int* returnSize)
{int* ans = (int*)malloc(sizeof(int) * numsSize);int* heap = (int*)malloc(sizeof(int) * numsSize);int size = 0, left = 0, pos = 0;for (int i = 0; i < numsSize; i++){if(i - left == k){//修改窗口大小 && 获取ansans[pos++] = nums[heap[0]];//最大的数不在窗口内,出队列while(size != 0 && heap[0] <= left){HeapPop(nums,heap,&size);}left++;}//入队列HeapPush(nums,heap,&size,i);}ans[pos++] = nums[heap[0]];*returnSize = pos;return ans;
}

而对于堆函数的实现,和上一题拼车一样,存储的都是下标,修改修改就能用了。

//heap 中存放下标
void Heapfiy(int* nums, int* heap, int size, int i)
{int maxIndex = i, lc = 2 * i + 1, rc = 2 * i + 2;if (lc < size && nums[heap[lc]] > nums[heap[maxIndex]]){maxIndex = lc;}if (rc < size && nums[heap[rc]] > nums[heap[maxIndex]]){maxIndex = rc;}if(maxIndex != i){Swap(&heap[i], &heap[maxIndex]);Heapfiy(nums,heap,size,maxIndex);}
}//
void HeapPush(int* nums, int* heap, int* size, int i)
{heap[(*size)++] = i;for (int parent = (*size - 2) / 2; parent >= 0; parent = (parent - 1) / 2){Heapfiy(nums,heap,*size,parent);if(parent == 0){break;}}
}void HeapPop(int* nums,int* heap, int* size)
{Swap(&heap[0],&heap[--(*size)]);Heapfiy(nums,heap,*size,0);
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/690599.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

十二:枚举与注解

文章目录 01、枚举类的使用1.1、枚举类的理解1.2、自定义枚举类1.3、使用enum关键字定义枚举类1.4、Enum类中的常用方法1.5、使用enum关键字定义的枚举类实现接口 02、注解的使用2.1、注解的理解2.3、如何自定义注解2.4、jdk中4个基本的元注解的使用12.5、jdk中4个基本的元注解…

uniapp 适配鸿蒙next调研

1.官方的一些回答 DCloud有资源第一时间得到鸿蒙无apk手机的上市计划。我们和华为保持着紧密沟通&#xff0c;会把握好节奏&#xff0c;不用担心。大家可以观察一个信号&#xff0c;等微信的鸿蒙next版敲定了&#xff0c;鸿蒙无apk手机就可以明确上市计划了。鸿蒙的开发语言是a…

数据结构:动态内存分配+内存分区+宏+结构体

一、作业 1.定义一个学生结构体&#xff0c;包含结构体成员&#xff1a;身高&#xff0c;姓名&#xff0c;成绩&#xff1b;定义一个结构体数组有7个成员&#xff0c;要求终端输入结构体成员的值&#xff0c;根据学生成绩&#xff0c;进行冒泡排序。 #include <stdio.h>…

UE蓝图 Cast节点和源码

系列文章目录 UE蓝图 Cast节点和源码 文章目录 系列文章目录Cast节点功能一、Cast节点用法二、Cast节点使用场景三、Cast节点实现步骤四、Cast节点源码 Cast节点功能 在Unreal Engine&#xff08;UE&#xff09;中&#xff0c;Cast节点是一种蓝图系统中的节点&#xff0c;用于…

【性能测试入门必看】性能测试理论知识

一、性能测试理论知识 1、常用的七种性能测试方法 (1) 后端性能测试&#xff1a;其实&#xff0c;你平时听到的性能测试&#xff0c;大多数情况下指的是后端性能测试&#xff0c;也就是服务器端性能测试。后端性能测试&#xff0c;是通过性能测试工具模拟大量的并发用户请求&…

linux系统Grafana关联zabbix显示

Grafana关联zabbix 服务器下载浏览器配置开启zabbix插件配置zabbix数据源可视化Zabbix数据 服务器下载 grafana-cli plugins list-remote grafana-cli plugins list-remote|grep -i zabbix grafana-cli plugins install alexanderzobnin-zabbix-appsystemctl restart grafana-…

HBase 进阶

参考来源: B站尚硅谷HBase2.x 目录 Master 架构RegionServer 架构写流程MemStore Flush读流程HFile 结构读流程合并读取数据优化 StoreFile CompactionRegion Split预分区&#xff08;自定义分区&#xff09;系统拆分 Master 架构 Master详细架构 1&#xff09;Meta 表格介…

Vue | (一)Vue核心(上) | 尚硅谷Vue2.0+Vue3.0全套教程

文章目录 &#x1f4da;Vue简介&#x1f4da;初识Vue&#x1f4da;模板语法&#x1f4da;数据绑定&#x1f4da;MVVM模型&#x1f4da;数据代理&#x1f407;回顾Object.defineproperty方法&#x1f407;何为数据代理&#x1f407;Vue中的数据代理 &#x1f4da;事件处理&#…

基于java的眼镜店仓库管理系统

源码获取&#xff0c;加V&#xff1a;qq2056908377 摘要&#xff1a; 随着电子商务的兴起&#xff0c;越来越多的商家选择在线销售他们的产品。眼镜店作为零售业的一种&#xff0c;也不例外。随着市场需求的不断增加&#xff0c;眼镜店需要更加高效的管理他们的仓库和库存&…

Mysql 权限与安全管理

0 引言 MySQL是一个多用户数据库&#xff0c;具有功能强大的访问控制系统&#xff0c;可以为不同用户指定允许的权限。MySQL用户可以分为普通用户和root用户。root用户是超级管理员&#xff0c;拥有所有权限&#xff0c;包括创建用户、删除用户和修改用户的密码等管理权限&…

LeetCode21.合并两个有序链表

题目 将两个升序链表合并为一个新的 升序 链表并返回。新链表是通过拼接给定的两个链表的所有节点组成的。 示例 &#xff1a; 输入&#xff1a;l1 [1,2,4], l2 [1,3,4] 输出&#xff1a;[1,1,2,3,4,4] 思路 创建一个新的链表头节点&#xff08;dummyNode&#xff09;和一个…

RegExp正则表达式左限定右限定左右限定,预查询,预查寻,断言 : (?<= , (?= , (?<! , (?!

RegExp正则表达式左限定右限定左右限定,预查询,预查寻,断言 : (?< , (? , (?<! , (?! 有好多种称呼 (?< , (? , (?<! , (?! 有好多种称呼 , 我称为: 左限定, 右限定, 左否定, 右否定 (?<左限定)    (?右限定)(?<!左否定)    (?!右限定) 再…

阿里云个人建站笔记

导航 一、购买ECS服务器二、配置mysql&#xff08;一&#xff09;安装Mysql步骤一&#xff1a;安装mysql步骤二&#xff1a;配置MySQL步骤三&#xff1a;远程访问MySQL数据库 &#xff08;二&#xff09;给实例配置安全组策略&#xff08;三&#xff09;设置防火墙 一、购买ECS…

.ma1x0勒索病毒解密方法|勒索病毒解决|勒索病毒恢复|数据库修复

尊敬的读者&#xff1a; 数据安全问题备受关注。而勒索病毒是其中一种最为恶劣的威胁之一。其中&#xff0c;.ma1x0勒索病毒备受人们担忧&#xff0c;因其可将用户的数据文件加密&#xff0c;并要求支付赎金以解密文件。本文将介绍.ma1x0勒索病毒的特征、预防方法以及如何恢复…

transformer-Attention is All You Need(二)

transformer中的解码器 解码器层 每个解码器层根据给定的输入向目标方向进行特征提取&#xff0c;即完成解码过程 transformer的解码器也是一个自回归模型&#xff0c;根据编码器的结果以及上一次预测的结果&#xff0c;对下一次可能出现的值进行特征表示。它也是由N层完全相同…

循环队列|超详细|数据结构学习讲解与笔记

队列元素先进先出队列只允许在线性表的一端进行操作&#xff0c;是一种操作受限的线性表 队列的基本操作 InItQueue(&Q)初始化队列&#xff0c;构造一个空队列 QEmptyQueue(Q)队列判空FullQueue(Q)队列判满EnQueue(&Q , x)入队操作DeQueue(&Q , &x)出队操作G…

OpenAI新爆款Sora,大佬们怎么看?

OpenAI新爆款Sora的热度持续发酵&#xff0c;在科技圈的刷屏阵仗都快赶上正月初五迎财神了。 智东西2月17日报道&#xff0c;这两天&#xff0c;OpenAI首款文生视频大模型Sora以黑马之姿占据AI领域话题中心&#xff0c;马斯克、杨立昆、贾扬清、Jim Fan、谢赛宁、周鸿祎、李志…

【动态规划专栏】专题一:斐波那契数列模型--------2.三步问题

本专栏内容为&#xff1a;算法学习专栏&#xff0c;分为优选算法专栏&#xff0c;贪心算法专栏&#xff0c;动态规划专栏以及递归&#xff0c;搜索与回溯算法专栏四部分。 通过本专栏的深入学习&#xff0c;你可以了解并掌握算法。 &#x1f493;博主csdn个人主页&#xff1a;小…

数据结构-最短路径(Dijkstra算法与Floyd算法)

介绍 对于网图来说&#xff0c;最短路径是指两顶点之间经过的边上权值之和最少的路径&#xff0c;其路径上第一个点记为源点&#xff0c;最后一个为终点。 计算最短路径有两个经典算法&#xff0c;即迪杰斯特拉&#xff08;Dijkstra&#xff09;算法与弗洛伊德&#xff08;Fl…

unity学习(20)——客户端与服务器合力完成注册功能(2)调试注册逻辑

接着上一节的问题&#xff0c;想办法升级成具备数据库功能的服务器&#xff0c;这个是必须的。 至少在初始化要学会把文件转换为session&#xff0c;新知识&#xff0c;有挑战的。 现在是从LoginHandler.cs跳到了AccountBiz.cs的create&#xff0c;跳度还是很大的。 create函…