Leetcode(4)——堆和优先队列

格式:

题号+题名+简单思路+code



※T743: 网络延迟时间

  • 暴力遍历每一条可以被放松的路径
  • O ( N N + E l o g ( E ) ) O(N^N+Elog(E)) O(NN+Elog(E)) 时间复杂度
var MAXINT int = 1<<31-1

type Edge struct {
    to int
    time int
}

func networkDelayTime(times [][]int, N int, K int) int {
    distances:=map[int]int{}
    graph:=map[int][]Edge{}
    for i:=1;i<=N;i++ {
        distances[i]=MAXINT
        graph[i]=[]Edge{}
    }
    distances[K]=0
    for i:=0;i<len(times);i++ {
        ti:=times[i]
        graph[ti[0]]=append(graph[ti[0]],Edge{ti[1],ti[2]})
    }
    dfs(graph,distances,K)
    totalTime:=0
    for _,v:=range distances {
        if v==MAXINT {
            return -1
        }
        if v>totalTime {
            totalTime=v
        }
    }
    return totalTime
}

func dfs(graph map[int][]Edge, distances map[int]int,K int) {
    tmp:=graph[K]
    time:=distances[K]
    for i:=0;i<len(tmp);i++ {
        if time+tmp[i].time<distances[tmp[i].to] {
            distances[tmp[i].to]=time+tmp[i].time
            dfs(graph,distances,tmp[i].to)
        }
    }
}
  • Dijsktra算法
  • O(NlogN)时间复杂度
  1. 索引优先队列实现(《算法四》)
import "container/heap"

type Edge struct {
    to int
    time int
    index int
}

type TimeHeap []*Edge

func (t TimeHeap) Len() int {
    return len(t)
}

func (t TimeHeap) Less(i int, j int) bool {
    return t[i].time-t[j].time <0
}

func (t TimeHeap) Swap(i int, j int) {
    t[i],t[j]=t[j],t[i]
    t[i].index,t[j].index=i,j
}

func (t *TimeHeap) Push(i interface{}) {
    tmp:=i.(*Edge)
    tmp.index=t.Len()
    *t=append(*t,tmp)
}

func (t *TimeHeap) Pop() interface{} {
    x:=(*t)[len(*t)-1]
    *t=(*t)[:len(*t)-1]
    x.index=-1
    return x
}

func (t *TimeHeap) Update(e *Edge,time int) {
    e.time=time
    heap.Fix(t,e.index)
}



var MAXINT int=int(^uint(0) >> 1)

func networkDelayTime(times [][]int, N int, K int) int {
    graph:=make(map[int][]Edge,N)
    minL:=make(map[int]int,N)
    for i:=1;i<=N;i++ {
        graph[i]=[]Edge{}
        minL[i]=MAXINT
    }
    for i:=0;i<len(times);i++ {
        ti:=times[i]
        graph[ti[0]]=append(graph[ti[0]],Edge{ti[1],ti[2],-1})
    }
    totalTime:=0
    memo:=make(map[int]*Edge,0)
    start:=&Edge{K,0,-1}
    memo[K]=start
    minL[K]=0
    timeheap:=&TimeHeap{start}
    for timeheap.Len()>0 {
        edge:=heap.Pop(timeheap).(*Edge)
        delete(memo,edge.to)
        tmp:=graph[edge.to]
        for i:=0;i<len(tmp);i++ {
            n:=tmp[i].to
            d:=tmp[i].time
            if edge.time+d<minL[n] {
                minL[n]=edge.time+d
                if point,ok:=memo[n];ok {
                    timeheap.Update(point,minL[n])
                } else {
                    start=&Edge{n,minL[n],-1}
                    heap.Push(timeheap,start)
                    memo[n]=start
                }
            }
        }
    }
    for i:=range minL {
        if minL[i]==MAXINT {
            return -1
        }
        if minL[i]>totalTime {
            totalTime=minL[i]
        }
    }

    return totalTime
    
}
  1. 不进行Update,仅Push和去重
import "container/heap"

type Edge struct {
    to int
    time int
}

type TimeHeap []Edge

func (t TimeHeap) Len() int {
    return len(t)
}

func (t TimeHeap) Less(i int, j int) bool {
    return t[i].time-t[j].time <0
}

func (t TimeHeap) Swap(i int, j int) {
    t[i],t[j]=t[j],t[i]
}

func (t *TimeHeap) Push(i interface{}) {
    *t=append(*t,i.(Edge))
}

func (t *TimeHeap) Pop() interface{} {
    x:=(*t)[len(*t)-1]
    *t=(*t)[:len(*t)-1]
    return x
}


var MAXINT int=int(^uint(0) >> 1)

func networkDelayTime(times [][]int, N int, K int) int {
    graph:=make(map[int][]Edge,N)
    minL:=make(map[int]int,N)
    for i:=1;i<=N;i++ {
        graph[i]=[]Edge{}
        minL[i]=MAXINT
    }
    for i:=0;i<len(times);i++ {
        ti:=times[i]
        graph[ti[0]]=append(graph[ti[0]],Edge{ti[1],ti[2]})
    }
    totalTime:=0
    memo:=make(map[int]interface{},0)
    timeheap:=&TimeHeap{Edge{K,0}}
    for timeheap.Len()>0 {
        edge:=heap.Pop(timeheap).(Edge)
        if _,ok:=memo[edge.to];ok {
            continue
        }
        memo[edge.to]=nil
        minL[edge.to]=edge.time
        tmp:=graph[edge.to]
        for i:=0;i<len(tmp);i++ {
            n:=tmp[i].to
            d:=tmp[i].time
            if _,ok:=memo[n];!ok && edge.time+d<minL[n] {
                heap.Push(timeheap,Edge{n,edge.time+d})
            }
        }
    }
    for i:=range minL {
        if minL[i]==MAXINT {
            return -1
        }
        if minL[i]>totalTime {
            totalTime=minL[i]
        }
    }

    return totalTime
    
}




T264: 丑数Ⅱ

  • 练习预计算
  • golang中使用全局变量
  • 升序排列→堆排序
import (
    "container/heap"
)

var pre []int=buildList()
type Stack []int

func (s Stack) Len() int {
    return len(s)
}

func (s Stack) Less(i int, j int) bool {
    return s[i]-s[j]<0
}

func (s Stack) Swap(i int,j int) {
    s[i],s[j]=s[j],s[i]
}

func (s *Stack) Push(i interface{}) {
    tmp:=*s
    *s=append(tmp,i.(int))
}

func (s *Stack) Pop() interface{} {
    tmp:=*s
    x:=tmp[len(tmp)-1]
    *s=tmp[:len(tmp)-1]
    return x
}

func buildList() []int {
    result:=make(map[int]interface{})
    stack:=&Stack{1}
    p_result:=make([]int,1690)
    for index:=0;index<1690;index++ {
        i:=heap.Pop(stack).(int)
        if _,ok:=result[i];ok {
            index--
            continue
        }
        result[i]=nil
        p_result[index]=i
        heap.Push(stack,i*2)
        heap.Push(stack,i*3)
        heap.Push(stack,i*5)
    }
    return p_result
}

func nthUglyNumber(n int) int {
    return pre[n-1]
}
  • python中使用静态变量
from heapq import heappush,heappop

def buildList() -> List:
    result=[0 for i in range(1690)]
    memo=set()
    heap=[1]
    index=0
    while index<1690:
        i=heappop(heap)
        if i in memo:
            continue
        memo.add(i)
        result[index]=i
        index+=1
        heappush(heap,i*2)
        heappush(heap,i*3)
        heappush(heap,i*5)
    return result




class Solution:
    pre=buildList()
    def nthUglyNumber(self, n: int) -> int:
        return self.pre[n-1]




※T215: 数组中的第K大元素

  • 快速选择算法
  • 适用于有重复数组
  • 最好时间复杂度O(N)
func findKthLargest(nums []int, k int) int {
    return assist(nums,0,len(nums)-1,len(nums)+1-k)
}

func assist(nums []int,start int,end int,k int) int {
    ans:=0
    ref:=nums[start]
    i1:=start
    i2:=end
    for end>start {
        for ;end>start;end-- {
            if nums[end]<ref {
                nums[start]=nums[end]
                break
            }
        }
        for ;end>start;start++ {
            if nums[start]>ref {
                nums[end]=nums[start]
                break
            }
        }
    }
    if k>start+1 {
        ans=assist(nums,start+1,i2,k)
    } else if k<start+1 {
        ans=assist(nums,i1,start-1,k)
    } else {
        ans=ref
    }
    return ans
    
}
  • 最小堆
  • O(NlogK)
import "container/heap"

type Stack []int

func (s Stack) Len() int {
    return len(s)
}

func (s Stack) Less(i int, j int) bool {
    return s[i]-s[j]<0
}

func (s Stack) Swap(i int,j int) {
    s[i],s[j]=s[j],s[i]
}

func (s *Stack) Push(i interface{}) {
    *s=append(*s,i.(int))
}

func (s *Stack) Pop() interface{} {
    tmp:=*s
    x:=tmp[len(tmp)-1]
    *s=tmp[:len(tmp)-1]
    return x
}

func findKthLargest(nums []int, k int) int {
    if len(nums)==0 {
        return -1
    }
    stack:=&Stack{}
    for i:=0;i<len(nums);i++ {
        heap.Push(stack,nums[i])
        if stack.Len()>k {
            heap.Pop(stack)
        }
    }
    return heap.Pop(stack).(int)
}




剑指40: 最小的k个数

  • 快速选择,找到第k-1个位置后,取前k个数即可
class Solution:
    def getLeastNumbers(self, arr: List[int], k: int) -> List[int]:
        if k<=0:
            return []
        return self.assist(arr, k-1, 0, len(arr)-1)
    def assist(self, arr, k, lo, hi):
        start=lo
        end=hi
        tmp=arr[lo]
        while lo<hi:
            while lo<hi and arr[hi]>=tmp:
                hi-=1
            arr[lo]=arr[hi]
            while lo<hi and arr[lo]<=tmp:
                lo+=1
            arr[hi]=arr[lo]
        arr[lo]=tmp    # 易错
        if lo<k:
            return self.assist(arr, k, lo+1, end)
        elif lo>k:
            return self.assist(arr, k, start, lo-1)
        else:
            return arr[:k+1]




T378: 有序矩阵中第K小元素

  • 最大堆
  • O ( N 2 l o g K ) O(N^2logK) O(N2logK)
from heapq import heappop,heappush

class Solution:
    def kthSmallest(self, matrix: List[List[int]], k: int) -> int:
        if k<1 or k>len(matrix)*len(matrix):
            return -1
        heap=[]
        for i in matrix:
            for j in i:
                heappush(heap,-j)
                if len(heap)>k:
                    heappop(heap)
        return -heappop(heap)
  • 值域二分查找;类似问题(快速选择算法和BST找第K小元素)
  • 针对有序矩阵结构
  • 取ceil
  • O(NlogH)
from math import ceil
class Solution:
    def kthSmallest(self, matrix: List[List[int]], k: int) -> int:
        lo=matrix[0][0]
        hi=matrix[-1][-1]
        while lo<hi:
            mid=ceil((hi-lo)/2)+lo
            rank=self.getRank(matrix,mid)
            if k<rank+1:
                hi=mid-1
            else:
                lo=mid
        return lo
    def getRank(self,matrix:List[List[int]],value:int) -> int:
        count=0
        i=len(matrix)-1
        j=0
        while i>=0 and j<=len(matrix)-1:
            if matrix[i][j]>=value:
                i-=1
            else:
                count+=i+1
                j+=1
        return count




T347: 前K个高频元素

  • 最小堆
  • O(NlogK)
import "container/heap"

type item struct {
    val int
    count int
}

type minHeap []item 

func (m minHeap) Len() int {
    return len(m)
}

func (m *minHeap) Swap(i int,j int) {
    (*m)[i],(*m)[j]=(*m)[j],(*m)[i]
}

func (m minHeap) Less(i int,j int) bool {
    return m[i].count-m[j].count<0
}

func (m *minHeap) Push(i interface{}) {
    *m=append(*m,i.(item))
}

func (m *minHeap) Pop() interface{} {
    i:=(*m)[len(*m)-1]
    *m=(*m)[:len(*m)-1]
    return i
}

func topKFrequent(nums []int, k int) []int {
    if len(nums)==0 {
        return nil
    }
    memo:=map[int]int{}
    for _,i := range nums {
        if _,ok:=memo[i];ok {
            memo[i]++
        } else {
            memo[i]=1
        }
    }
    tmp:=&minHeap{}
    for key,value := range memo {
        heap.Push(tmp,item{key,value})
        if tmp.Len() > k {
            heap.Pop(tmp)
        }
        
    }
    ans:=[]int{}
    for _,v := range *tmp {
        ans=append(ans,v.val)
    }
    return ans

}




※T407: 接雨水Ⅱ

  • 类似T42: 接雨水解法2,该方法通过计算最外端中较小值来更新答案,同时更新新的最外端高度值
  • 该题与解法2基本思路是一样的,不过在二维上需要根据最外圈的最小值来更新答案,即木桶原理
  • 因此首先将最外圈作为边界存入最小堆,同时用数组记录已遍历过的位置;然后弹出最小高度,比较其四个方向中合理且未遍历的位置,如果能接水则更新答案,最后将二者较高的高度加入堆中(即更新最外圈),并记录该位置已遍历
import "container/heap"

type Item struct {
    i int
    j int
    height int
}

type Item_pri []Item

func (this Item_pri) Len() int {
    return len(this)
}

func (this Item_pri) Less(idx_i int, idx_j int) bool {
    return this[idx_i].height-this[idx_j].height<0
}

func (this Item_pri) Swap(idx_i int, idx_j int) {
    this[idx_i],this[idx_j]=this[idx_j],this[idx_i]
}

func (this *Item_pri) Push(x interface{}) {
    *this=append(*this,x.(Item))
}

func (this *Item_pri) Pop() interface{} {
    x:=(*this)[len(*this)-1]
    *this=(*this)[:len(*this)-1]
    return x
}

func trapRainWater(heightMap [][]int) int {
    m:=len(heightMap)
    if m==0 {
        return 0
    }
    n:=len(heightMap[0])
    minHeap:=&Item_pri{}
    directs:=[4][2]int{{0,1},{0,-1},{1,0},{-1,0}}
    ans:=0
    visited:=make([][]bool,m)
    for i:=0;i<m;i++ {
        visited[i]=make([]bool,n)
    }
    for i:=0;i<m;i++ {
        for j:=0;j<n;j++ {
            if i==0 || j==0 || i==m-1 || j==n-1 {
                heap.Push(minHeap,Item{i,j,heightMap[i][j]})
                visited[i][j]=true
            }
        }
    }
    for minHeap.Len() >0 {
        minX:=heap.Pop(minHeap).(Item)
        for k:=0;k<4;k++ {
            idx_i:=minX.i+directs[k][0]
            idx_j:=minX.j+directs[k][1]
            if idx_i>0 && idx_i<m && idx_j>0 && idx_j<n && !visited[idx_i][idx_j] {
                if heightMap[idx_i][idx_j]<minX.height {
                    ans+=minX.height-heightMap[idx_i][idx_j]
                    heap.Push(minHeap,Item{idx_i,idx_j,minX.height})
                } else {
                    heap.Push(minHeap,Item{idx_i,idx_j,heightMap[idx_i][idx_j]})
                }
                visited[idx_i][idx_j]=true
            }
        }
    }
    return ans
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值