239. 滑动窗口最大值
暴力+剪枝
遍历数组,每个滑动区间计算最大值,当推出的元素不是最大值时无需计算
时间复杂度O(n2) 空间复杂度O(n)
func maxSlidingWindow(nums []int, k int) []int {
maxVal := nums[0]
maxCount := 1
result := make([]int, 0)
for i := 1; i < k; i++ {
if nums[i] > maxVal {
maxVal = nums[i]
maxCount = 1
} else if nums[i] == maxVal {
maxCount++
}
}
result = append(result, maxVal)
for i := k; i < len(nums); i++ {
if nums[i] > maxVal {
maxVal = nums[i]
maxCount = 1
} else if nums[i] == maxVal {
maxCount++
}
if nums[i-k] == maxVal {
maxCount--
// 弹出最大值,若没有最大值了,则重新计算最大值
if maxCount == 0 {
maxVal = nums[i-k+1]
maxCount = 1
for _, val := range nums[i-k+2 : i+1] {
if val > maxVal {
maxVal = val
maxCount = 1
} else if val == maxVal {
maxCount++
}
}
}
}
result = append(result, maxVal)
}
return result
}
单调队列
硬控我三个小时,一开始想着当出队值和入队值相等时,直接修改记录front值的索引即可,但是一直错,后来才发现不能这么做,因为单调队列中不仅要保持大小顺序,还要保持入队顺序,另设一个存储索引值和值的结构体的做法本身没问题,但是不能改变索引值,不然会违背顺序的原则
时间复杂度 O(n) 空间复杂度 O(n)
type MyQueue struct {
elements []int
}
// Size 计算长度
func (this *MyQueue) Size() int {
return len(this.elements)
}
// Empty 判断是否为空
func (this *MyQueue) Empty() bool {
return this.Size() == 0
}
// Front 获取队列头部元素
func (this *MyQueue) Front() int {
return this.elements[0]
}
// Back 获取队列尾部元素
func (this *MyQueue) Back() int {
return this.elements[this.Size()-1]
}
// Enqueue 进队操作
func (this *MyQueue) Enqueue(value int) {
// 自定义入队操作,核心步骤之一,构造单调队列
for !this.Empty() && value > this.Back() {
this.elements = this.elements[:this.Size()-1]
}
this.elements = append(this.elements, value)
}
// Dequeue 出队操作
func (this *MyQueue) Dequeue() int {
element := this.Front()
this.elements = this.elements[1:]
return element
}
func maxSlidingWindow(nums []int, k int) []int {
myQueue := MyQueue{}
// 先初始化myQueue,即第一个窗口的情况
for i := 0; i < k; i++ {
myQueue.Enqueue(nums[i])
}
result := []int{myQueue.Front()}
// 开始滑动窗口
for index := k; index < len(nums); index++ {
if nums[index - k] == myQueue.Front(){
myQueue.Dequeue()
}
// 当遇到相等值或更大值时直接Enqueue
myQueue.Enqueue(nums[index])
result = append(result, myQueue.Front())
}
return result
}
347. 前 K 个高频元素
哈希+排序
先遍历数组,将数字出现频率记录下来,再将频次排序并输出
时间复杂度 O(nlogn) 空间复杂度 O(n)
type Element struct {
value int // 值
frequency int // 出现频次
}
// 法一:排序 + 哈希
func topKFrequent(nums []int, k int) []int {
frequencyMap := map[int]*Element{} // 存放出现频率的哈希表
for _, num := range nums { // 遍历数组并记录每个数字出现的频率
if _, ok := frequencyMap[num]; ok {
frequencyMap[num].frequency++
} else {
frequencyMap[num] = &Element{value: num, frequency: 1}
}
}
frequencyList := []Element{} // 记录频率与对应值的数组
for _, element := range frequencyMap {
frequencyList = append(frequencyList, *element)
}
// 按频率进行排序
sort.Slice(frequencyList, func(i, j int) bool {
return frequencyList[i].frequency > frequencyList[j].frequency
})
result := make([]int, k)
for i := 0; i < k; i++ {
result[i] = frequencyList[i].value
}
return result
}
优先队列
由于go中没有优先队列的容器(但有堆的容器,但我没用),所以手搓了一个,花了不少时间,当作是复习堆排序的知识了,注意堆中数组下标的关系:2i+1为左节点的下标,2i+2为右节点的下标
type Element struct {
value int // 值
frequency int // 出现频次
}
// PriorityQueue 优先队列(使用数组实现堆)
type PriorityQueue struct {
myHeap []Element // 内部维护的一个堆
}
// Size 获取长度
func (pq *PriorityQueue) Size() int {
return len(pq.myHeap)
}
// UpLeak 上滤函数
func (pq *PriorityQueue) UpLeak() {
tempCur := pq.Size() - 1 // 新元素的索引
for tempCur != 0 && pq.myHeap[tempCur].frequency < pq.myHeap[(tempCur - 1)/2].frequency { // 若小,则与父节点交换
pq.myHeap[tempCur], pq.myHeap[(tempCur - 1)/2] = pq.myHeap[(tempCur - 1)/2], pq.myHeap[tempCur] // 交换节点
tempCur = (tempCur - 1)/2
}
}
// DownLeak 下渗函数
func (pq *PriorityQueue) DownLeak() {
tempCur := 0
if pq.Size() == 1 {
return
}
isLeft := true
for tempCur*2+1 < pq.Size() {
isLeft = true
if tempCur*2+2 < pq.Size() && pq.myHeap[tempCur*2+2].frequency < pq.myHeap[tempCur*2+1].frequency {
isLeft = false
}
if isLeft { // 左边
if pq.myHeap[tempCur].frequency > pq.myHeap[tempCur*2+1].frequency {
pq.myHeap[tempCur*2+1], pq.myHeap[tempCur] = pq.myHeap[tempCur], pq.myHeap[tempCur*2+1]
tempCur = tempCur*2 + 1
} else {
return
}
} else {
if pq.myHeap[tempCur].frequency > pq.myHeap[tempCur*2+2].frequency {
pq.myHeap[tempCur*2+2], pq.myHeap[tempCur] = pq.myHeap[tempCur], pq.myHeap[tempCur*2+2]
tempCur = tempCur*2 + 2
} else {
return
}
}
}
}
// InitHeap 建堆
func (pq *PriorityQueue) InitHeap(nums []Element) {
// 自上向下建堆(容易实现,但时间复杂度为O(nlogn)
pq.myHeap = make([]Element, 0)
pq.myHeap = append(pq.myHeap, nums[0])
for _, element := range nums[1:] {
pq.pushBack(element)
}
}
// pushBack 添加元素到末尾
func (pq *PriorityQueue) pushBack(element Element) {
pq.myHeap = append(pq.myHeap, element)
pq.UpLeak()
}
// Front 头部元素
func (pq *PriorityQueue) Front() Element {
return pq.myHeap[0]
}
// Push 添加新元素
func (pq *PriorityQueue) Push(element Element) {
if element.frequency > pq.Front().frequency {
pq.myHeap[0] = element
pq.DownLeak()
}
}
// Pop 移除头部元素
func (pq *PriorityQueue) Pop() Element {
element := pq.myHeap[0]
pq.myHeap = pq.myHeap[1:]
return element
}
// 法二:优先队列(堆排序) + 哈希
func topKFrequent(nums []int, k int) []int {
myQueue := &PriorityQueue{}
countMap := map[int]int{}
for _, element := range nums {
if _, ok := countMap[element]; !ok {
countMap[element] = 1
} else {
countMap[element]++
}
}
countList := make([]Element, 0)
for key, val := range countMap {
countList = append(countList, Element{key, val})
}
myQueue.InitHeap(countList[:k])
for _, element := range countList[k:] {
myQueue.Push(element)
}
resultList := make([]int, 0)
for myQueue.Size() != 0 {
resultList = append(resultList, myQueue.Pop().value)
}
return resultList
}
桶排序
先使用哈希表计算数字出现的频次,再桶排序,最后倒序输出,注意可能出现相同频次的不同数字,故桶中存放一个Slice
时间复杂度: O(n) 空间复杂度:O(n)
// 法三:桶排序
func topKFrequent(nums []int, k int) []int {
countMap := map[int]int{}
for _, element := range nums {
if _, ok := countMap[element]; !ok {
countMap[element] = 1
} else {
countMap[element]++
}
}
boltList := make([][]int, len(nums)+1) // 考虑到重复频次的不同数字,桶中存放slice
for key, frequent := range countMap {
boltList[frequent] = append(boltList[frequent], key)
}
resultList := make([]int, 0)
for index := len(boltList) - 1; index >= 0 && len(resultList) < k; index-- {
if boltList[index] != nil {
resultList = append(resultList, boltList[index]...)
}
}
return resultList
}