2021-05-29:最常使用的K个单词II。在实时数据流中找到最常使用的k个单词,实现TopK类中的三个方法: TopK(k), 构造方法。add(word),增加一个新单词。topk(),得到当前最常使用的k个单词。如果两个单词有相同的使用频率,按字典序排名。

福大大 答案2021-05-30:

方法一:
redis的sorted set。hash+跳表实现计数和查找。无代码。
方法二:
节点结构体:有字符串和词频。
词频表:key是字符串,value是节点。
堆:节点数组。刚开始,我以为是大根堆。采用小根堆,如果比堆顶还小,是进不了小根堆的。
反向表:key是节点,value是在堆中的索引。
有代码。

代码用golang编写。代码如下:

package main

import (
"fmt"
"sort"
) func main() { a := NewTopK(2)
a.add("fdd")
a.add("moon")
a.add("moonfdd")
a.add("moonfdd")
ret := a.topk()
for i := 0; i < len(ret); i++ {
fmt.Println(ret[i])
} } type TopK struct {
//堆
heap []*Node
heapSize int
//字,次数
wordNodeMap map[string]*Node
//反向表
nodeIndexMap map[*Node]int
} func NewTopK(k int) *TopK {
ret := &TopK{}
ret.heap = make([]*Node, k)
ret.wordNodeMap = make(map[string]*Node)
ret.nodeIndexMap = make(map[*Node]int)
return ret
} func (this *TopK) add(word string) {
if len(this.heap) == 0 {
return
}
var curNode *Node
preIndex := -1
curNode = this.wordNodeMap[word] //词频表 反向表
if curNode == nil {
curNode = &Node{word, 1}
this.wordNodeMap[word] = curNode
this.nodeIndexMap[curNode] = -1
} else {
curNode.Times++
preIndex = this.nodeIndexMap[curNode]
} //小根堆
if preIndex == -1 {
if this.heapSize == len(this.heap) {
if this.compare(curNode, this.heap[0]) {
//不用管了
return
}
curNode, this.heap[0] = this.heap[0], curNode
this.nodeIndexMap[curNode] = -1
this.nodeIndexMap[this.heap[0]] = 0
this.HeapDown(0) } else {
this.Push(curNode)
}
} else {
this.HeapDown(preIndex)
}
} func (this *TopK) topk() []string {
heapCopy := make([]*Node, this.heapSize)
copy(heapCopy, this.heap)
sort.Slice(heapCopy, func(i, j int) bool {
return !this.compare(heapCopy[i], heapCopy[j])
})
ans := make([]string, this.heapSize)
for i := 0; i < this.heapSize; i++ {
ans[i] = heapCopy[i].Str
}
return ans
} type Node struct {
Str string
Times int
} //索引上移,小根堆
func (this *TopK) HeapUp(index int) {
for (index-1)/2 != index && !this.compare(this.heap[(index-1)/2], this.heap[index]) { //父节点小于当前节点,当前节点必须上移
this.heap[index], this.heap[(index-1)/2] = this.heap[(index-1)/2], this.heap[index]
//加强堆
this.nodeIndexMap[this.heap[index]], this.nodeIndexMap[this.heap[(index-1)/2]] = (index-1)/2, index
index = (index - 1) / 2
}
} //索引下沉,小根堆
func (this *TopK) HeapDown(index int) {
left := 2*index + 1
for left <= this.heapSize-1 { //左孩子存在
//获取小孩子
largest := left
if left+1 <= this.heapSize-1 && this.compare(this.heap[left+1], this.heap[left]) {
largest++
} //比较
if !this.compare(this.heap[index], this.heap[largest]) { //当前大于最小孩子,必须下沉
this.heap[index], this.heap[largest] = this.heap[largest], this.heap[index]
//加强堆
this.nodeIndexMap[this.heap[index]], this.nodeIndexMap[this.heap[largest]] = largest, index
} else {
break
} //下一次遍历
index = largest
left = 2*index + 1
}
} func (this *TopK) Push(node *Node) {
this.heap[this.heapSize] = node
//加强堆
this.nodeIndexMap[node] = this.heapSize
//索引上移
this.HeapUp(this.heapSize)
this.heapSize++
} func (this *TopK) Pop() *Node {
ans := this.heap[0]
this.heap[0], this.heap[this.heapSize-1] = this.heap[this.heapSize-1], this.heap[0]
//加强堆
this.nodeIndexMap[this.heap[0]] = 0
this.nodeIndexMap[this.heap[this.heapSize-1]] = -1 this.heapSize--
//索引下沉
this.HeapDown(0)
return ans
} func (this *TopK) compare(node1 *Node, node2 *Node) bool {
if node1.Times == node2.Times {
return node1.Str > node2.Str
}
return node1.Times < node2.Times
}

执行结果如下:

福大大 答案2021-05-29:

方法一:
redis的sorted set。hash+跳表实现计数和查找。无代码。
方法二:
节点结构体:有字符串和词频。
词频表:key是字符串,value是节点。
堆:节点数组。
反向表:key是节点,value是在堆中的索引。
有代码,但不完整,因为时间紧。

代码用golang编写。代码如下:

package main

import "fmt"

func main() {
a := NewTopK(2)
a.add("lint")
a.add("code")
a.add("code")
fmt.Println(a.topk())
} type TopK struct {
//堆
heap []*Node
heapSize int
//字,次数
wordNodeMap map[string]*Node
//反向表
nodeIndexMap map[*Node]int
} func NewTopK(k int) *TopK {
ret := &TopK{}
ret.heap = make([]*Node, k) return ret
} func (this *TopK) add(word string) {
if len(this.heap) == 0 {
return
}
var curNode *Node
preIndex := -1
curNode = this.wordNodeMap[word]
if curNode == nil {
curNode = &Node{word, 1}
this.wordNodeMap[word] = curNode
this.nodeIndexMap[curNode] = -1
} else {
//tree set
curNode.Times++
preIndex = this.nodeIndexMap[curNode]
}
if preIndex == -1 {
if this.heapSize == len(this.heap) {
//treeset } else {
//tree add
this.nodeIndexMap[curNode] = this.heapSize
this.heap[this.heapSize] = curNode
this.HeapUp(preIndex)
}
} else {
//tree add
this.HeapDown(preIndex)
}
} func (this *TopK) topk() []string {
ans := make([]string, len(this.heap))
return ans
} type Node struct {
Str string
Times int
} //索引上移,大根堆
func (this *TopK) HeapUp(index int) {
for this.heap[(index-1)/2].Times < this.heap[index].Times { //父节点小于当前节点,当前节点必须上移
this.heap[index], this.heap[(index-1)/2] = this.heap[(index-1)/2], this.heap[index]
//加强堆
this.nodeIndexMap[this.heap[index]], this.nodeIndexMap[this.heap[(index-1)/2]] = (index-1)/2, index
index = (index - 1) / 2
}
} //索引下沉,大根堆
func (this *TopK) HeapDown(index int) {
left := 2*index + 1
for left <= this.heapSize-1 { //左孩子存在
//获取大孩子
largest := left
if left+1 <= this.heapSize-1 && this.heap[left+1].Times > this.heap[left].Times {
largest++
} //比较
if this.heap[index].Times < this.heap[largest].Times { //当前小于最大孩子,必须下沉
this.heap[index], this.heap[largest] = this.heap[largest], this.heap[index]
//加强堆
this.nodeIndexMap[this.heap[index]], this.nodeIndexMap[this.heap[largest]] = largest, index
} else {
break
} //下一次遍历
index = largest
left = 2*index + 1
}
} func (this *TopK) Push(node *Node) {
this.heap[this.heapSize] = node
//加强堆
this.nodeIndexMap[node] = this.heapSize
this.heapSize++
//索引上移
this.HeapUp(this.heapSize)
} func (this *TopK) Pop() *Node {
ans := this.heap[0]
this.heap[0], this.heap[this.heapSize-1] = this.heap[this.heapSize-1], this.heap[0]
//加强堆
this.nodeIndexMap[this.heap[0]] = 0
this.nodeIndexMap[this.heap[this.heapSize-1]] = -1
this.heapSize--
//索引下沉
this.HeapDown(0)
return ans
}

执行结果如下:


左神java代码

2021-05-29:最常使用的K个单词II。在实时数据流中找到最常使用的k个单词,实现TopK类中的三个方法: Top的更多相关文章

  1. 《程序员代码面试指南》第八章 数组和矩阵问题 在数组中找到出现次数大于N/K 的数

    题目 在数组中找到出现次数大于N/K 的数 java代码 package com.lizhouwei.chapter8; import java.util.ArrayList; import java ...

  2. 2021.05.29【NOIP提高B组】模拟 总结

    T1 题意:给你一个图,可以不花代价经过 \(K\) 条边,问从起点到终点的最短路 考试的想法:设 \(dis_{i,j}\) 表示从起点免费了 \(j\) 条边到 \(i\) 的最短路 然后直接跑 ...

  3. [算法]在数组中找到出现次数大于N/K的数

    题目: 1.给定一个整型数组,打印其中出现次数大于一半的数.如果没有出现这样的数,打印提示信息. 如:1,2,1输出1.    1,2,3输出no such number. 2.给定一个整型数组,再给 ...

  4. 2021.10.29 数位dp

    2021.10.29 数位dp 1.数字计数 我们先设数字为ABCD 看A000,如果我们要求出它所有数位之和,我们会怎么求? 鉴于我们其实已经求出了0到9,0到99,0到999...上所有数字个数( ...

  5. 2021.05.03 T3 数字

    2021.05.03 T3 数字 问题描述 一个数字被称为好数字当他满足下列条件: 1. 它有**2*n**个数位,n是正整数(允许有前导0) 2. 构成它的每个数字都在给定的数字集合S中. 3. 它 ...

  6. 2021.05.14 tarjan

    2021.05.14 tarjan 标准版tarjan 这里使用数组来模拟栈 void tarjan(int x){ ++ind; dfn[x]=low[x]=ind; stacki[++top]=x ...

  7. 项目Beta冲刺(团队)——05.29(7/7)

    项目Beta冲刺(团队)--05.29(7/7) 格式描述 课程名称:软件工程1916|W(福州大学) 作业要求:项目Beta冲刺(团队) 团队名称:为了交项目干杯 作业目标:记录Beta敏捷冲刺第7 ...

  8. 2021.6.29考试总结[NOIP模拟10]

    T1 入阵曲 二位前缀和暴力n4可以拿60. 观察到维护前缀和时模k意义下余数一样的前缀和相减后一定被k整除,前缀和维护模数,n2枚举行数,n枚举列, 开一个桶记录模数出现个数,每枚举到该模数就加上它 ...

  9. 2021.10.29 P1649 [USACO07OCT]Obstacle Course S(BFS)

    2021.10.29 P1649 [USACO07OCT]Obstacle Course S(BFS) 题意: 给一张n*n的图,起点为A,终点为 B,求从A到B转弯次数最少为多少. 分析: 是否存在 ...

  10. 2021.05.09【NOIP提高组】模拟赛总结

    2021.05.09[NOIP提高组]模拟赛总结 T1 T2

随机推荐

  1. k8s探针

    探针是由kubelet对容器执行的定期诊断.要执行诊断,kubelet调用由容器实现的Handler.有三类处理程序: ExecAction:在容器内执行指定命令.如果命令退出时返回码为0认为诊断成功 ...

  2. Python库之os库和logging库的基本使用说明

    使用os库操作目录及文件 使用os.sep() 方法获取系统分隔符 print(os.sep) 使用os.name()方法获取操作系统的平台类型 print(os.name) 使用os.getcwd( ...

  3. 深入理解 Python 虚拟机:元组(tuple)的实现原理及源码剖析

    深入理解 Python 虚拟机:元组(tuple)的实现原理及源码剖析 在本篇文章当中主要给大家介绍 cpython 虚拟机当中针对列表的实现,在 Python 中,tuple 是一种非常常用的数据类 ...

  4. CF1286F Harry The Potter

    CF1286F Harry The Potter 首先答案上界为 \(n\),就是对每个点用一次操作 1. 那么我们现在的思维模式就是利用操作 2 来减少操作 1 的次数. 不难发现,如果操作 2 的 ...

  5. 【ASP.NET Core】在node.js上托管Blazor WebAssembly应用

    由于 Blazor-WebAssembly 是在浏览器中运行的,通常不需要执行服务器代码,只要有个"窝"能托管并提供相关文件的下载即可.所以,当你有一个现成的 Blazor was ...

  6. 千亿参数开源大模型 BLOOM 背后的技术

    假设你现在有了数据,也搞到了预算,一切就绪,准备开始训练一个大模型,一显身手了,"一朝看尽长安花"似乎近在眼前 -- 且慢!训练可不仅仅像这两个字的发音那么简单,看看 BLOOM ...

  7. 升级pip报错ERROR: Could not install packages due to an OSError: [WinError 5]

    今天在安装python第三方库时,提示pip需要升级,没有多想直接升级,结果报错: 1 ERROR: Could not install packages due to an OSError: [Wi ...

  8. pandas之合并操作

    Pandas 提供的 merge() 函数能够进行高效的合并操作,这与 SQL 关系型数据库的 MERGE 用法非常相似.从字面意思上不难理解,merge 翻译为"合并",指的是将 ...

  9. [Linux]常用命令之【which/whereis/whatis】

    1 which shows the full path of (shell) commands. 显示(系统)命令所在目录 [root@test ~]# which ls alias ls='ls - ...

  10. vue高阶函数

    <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8&quo ...