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. 痞子衡嵌入式:MCUXpresso IDE下生成镜像文件的方法及其与IAR,MDK差异

    大家好,我是痞子衡,是正经搞技术的痞子.今天痞子衡给大家分享的是MCUXpresso IDE下生成镜像文件的方法及其与IAR,MDK差异. 痞子衡很久以前写过一篇文章 <ARM Cortex-M ...

  2. Vditor在原生JS中如何结合后端使用

    目录 1.Vditor介绍 2.如何在原生JS中结合后端使用 2.1 背景 2.2 正确使用方式 2.2.1 编辑页面 2.2.2 回显页面(修改页面) 2.2.3 预览页面 3.小结一下 1.Vdi ...

  3. 记一次 .NET 某企业 ERP网站系统 崩溃分析

    一:背景 1. 讲故事 前段时间收到了一个朋友的求助,说他的ERP网站系统会出现偶发性崩溃,找了好久也没找到是什么原因,让我帮忙看下,其实崩溃好说,用 procdump 自动抓一个就好,拿到 dump ...

  4. 介绍ChatGPT:基于GPT-3.5的强大自然语言处理工具

    大家好,今天我们来聊一下ChatGPT,一个基于GPT-3.5架构的大型语言模型.ChatGPT在自然语言处理方面有着非常强大的能力,可以进行语言生成.文本分类.对话生成等多种任务.接下来,我们将会详 ...

  5. Ceres 自动求导解析-从原理到实践

    Ceres 自动求导解析-从原理到实践 目录 Ceres 自动求导解析-从原理到实践 1.0 前言 2.0 Ceres求导简介 3.0 Ceres 自动求导原理 3.1 官方解释 3.2 自我理解 4 ...

  6. Unity3D中的Attribute详解(六)

    本文将重点对Unity剩下常用的Attribute进行讲解,其他不常用的Attribute各位可以自行去官方文档查阅. 首先是UnityEngine命名空间下的. ColorUsage,这个主要作用于 ...

  7. Unity3D中的Attribute详解(三)

    上一篇我们对系统的Attributes进行了MSIL代码的查看,了解到了其本质就是一个类的构造函数.本章我们将编写自己的Attributes. 首先我们定义书的属性代码,如下: [AttributeU ...

  8. 排队论——系统运行指标的R语言实现

    排队是在日常生活中经常遇到的现象,如顾客到商店购买物品.病人到医院看病常常要排队.此时要求服务的数量超过服务机构(服务台.服务员等)的容量.也就是说,到达的顾客不能立即得到服务,因而出现了排队现象.这 ...

  9. [Java/Arthas]Arthas The telnet port 3658 is used by process 13988 instead of target process 11208, y[转载]

    1 问题描述 Arthas 跟踪 一个已经在tomcat部署的工程quality,第一次使用过的是135091号进程,后来出现问题,换进程连接,报错如上图所示,提示端口占用.原因是上次连接了一个进程, ...

  10. devops工具链基建建设评价标准

    之所以写这篇是因为有朋友私下让我完善下基建建设的标准和四个阶梯划分,然后让我一定要把腾讯和百度加到基建建设的排名中(看热闹不嫌事大). 基建infra建设四个考察维度 1)工具链完整性:该有的工具是否 ...