堆
第k大/小
leetcode 264
用最小堆解决,先把最小的丑数1入堆,每次出最小的x,把2x、3x、5x入堆,用哈希表去重。
leetcode 373
如果说要枚举所有点对,复杂度至少是O(n^2)起步。
想办法利用非递减顺序排列的性质:想象一个矩阵,纵坐标表示nums1下标,横坐标表示nums2下标,矩阵的值是数对和,这样一个矩阵有什么性质?显然每行都是非递减序列,每列也都是非递减序列,左上角(0, 0)点对和最小。
这样维护一个最小堆,先把第一列入堆,然后开始出堆,出堆元素坐标(i, j),每出堆一个元素对应入堆一个元素,入堆元素坐标有两个选择:(i + 1, j)和(i, j + 1)。由于开始先把第一列入堆,所以取(i + 1, j)位置会导致重复入堆,只加(i, j + 1)即可,注意j + 1越界不处理。出堆k次得到第k小点对,时间复杂度O(klog(min(k, n)))。
重排元素
leetcode 1054
按需要使用次数贪心,优先选使用次数多的,需要用prev变量记录前一次选的,为了不重复前一次选的要在下次选完后才能重新入最大堆。
leetcode 1405
每一步永远取最多的字符,用最大堆维护,判断:如果最多的会造成三连重复,则取第二多的。本质是贪心
leetcode 3081
花费是由字母在字符串中总到出现次数决定的,因此要先遍历整个字符串得到各字母初始花费,然后用最大堆过一遍得到填入到字符,此时要排序才能保证最小字母序,不能直接贪心的填。
反悔堆
反悔贪心的一种常见做法,撤销之前的贪心操作并采用新的贪心操作。反悔贪心并不和堆绑定,经常和堆结合在一起是因为堆可以高效的弹出最值(反悔)和插入新的值,这是贪心的特点决定的。
leetcode 1642
对砖块贪心,反悔的时候维护一个最大堆,把用砖多的换成梯子。
leetcode 630
显然应该先上结束早的课,先按结束日期排序,在遍历课程时,如果发现前面的课需要花更长时间,需要反悔,用当前课程替换之前的课程,同样用最大堆维护。
leetcode 871
问最少加几次油能到终点,先按坐标排序。开始想的是用一个变量维护,在能到的范围内取最大的。这样贪心有一个缺陷:可能加一次油到不了下一站但是加多次可能就能到,加多次的情况被舍弃了。
因此把变量替换成最大堆,堆里的站点一定是可以到达的,跑不动了去堆里取,取到能跑到为止,最后看能不能到终点。