C++实现python标准库中的Counter
看python standard library by exmple里面提到一个Counter容器,它像muliset一样,能够维持一个集合,并在常量时间插入元素、查询某个元素的个数,而且还提供了一个
most_common(n)方法,用于统计频数最大的n个元素,这在读取文本并统计词频的时候显得非常实用。
考虑C++实现的时候,查到一个叫做LFU的东西,https://en.wikipedia.org/wiki/Least_frequently_used,是关于磁盘缓存策略的,基本想法跟这个counter有类似的地方。
http://dhruvbird.com/lfu.pdf 这里有相关的实现。
#include<iostream>
#include<list>
#include<vector>
#include<unordered_map>
using namespace std;
//关键字节点
template<typename T>
struct keyNode{
typedef T value_type;
keyNode(){}
keyNode(T v, keyNode* p, keyNode* n) :val(v), prev(p), next(n){}
T val;
keyNode* prev;
keyNode* next;
};
//计数器节点
template<typename T>
struct countNode{
countNode(){
keyhead = new keyNode<T> ;
keyhead->prev = keyhead->next = NULL;
}
~countNode(){
while (keyhead->next != NULL){
keyNode<T>* p = keyhead->next;
keyhead->next = p->next;
delete p;
}
delete keyhead;
}
countNode(int f, countNode* p, countNode *n):
freq(f),prev(p),next(n){
keyhead = new keyNode<T>;
keyhead->prev = keyhead->next = NULL;
}
keyNode<T>* insertKey(const T& v){
keyNode<T>* node = new keyNode<T>(v, keyhead, keyhead->next);
if (keyhead->next != NULL)
keyhead->next->prev = node;
keyhead->next = node;
return node;
}
int freq;
keyNode<T>* keyhead;
countNode* prev;
countNode* next;
}; //计数器容器
/***支持如下操作:
插入(insert) 时间复杂度O(1)
查找(lookup) 时间复杂度O(1)
查询最频繁的n个元素(most_common(n)) 时间复杂度o(n)
删除操作 时间复杂度o(1)
**/
template<typename T>
class Counter{
public:
Counter(){
head = new countNode<T>(0, NULL, NULL);
tail = NULL;
}
~Counter(){
while (head->next != NULL){
countNode<T>* p = head->next;
head->next = p->next;
delete p;
}
delete head;
}
//插入一个关键字,如果已经存在,频数加1
void insert(const T& v){
if (dict.find(v) == dict.end()){
//关键字是新插入的
if (head->next == NULL || head->next->freq != 1){
//需要新建count节点
countNode<T>* node = new countNode<T>(1, head, head->next);
if (head->next == NULL)
tail = node;
head->next = node;
dict[v] = pair<countNode<T>*, keyNode<T>*>(node, node->insertKey(v));
}
else{
dict[v] =
pair<countNode<T>*, keyNode<T>*>(head->next, head->next->insertKey(v));
}
}
else{
//关键字已经存在了
//频数必然会有增加,这时对结构的改动较大
countNode<T>* countAddr = dict[v].first;
countNode<T>* nextCount = countAddr->next;
keyNode<T>* keyAddr = dict[v].second;
int freq = countAddr->freq;
//首先从countAddr删除一个keyAddr节点
keyAddr->prev->next = keyAddr->next;
if (keyAddr->next != NULL)
keyAddr->next->prev = keyAddr->prev;
delete keyAddr;
if (nextCount == NULL || nextCount->freq != freq + 1){
//需要加一个countNode节点
countNode<T>* node = new countNode<T>(freq + 1, countAddr, nextCount);
if (nextCount != NULL)
nextCount->prev = node;
else
tail = node;
countAddr->next = node;
dict[v] =
pair<countNode<T>*, keyNode<T>*>(node, node->insertKey(v)); }
else{
dict[v] =
pair<countNode<T>*, keyNode<T>*>(nextCount, nextCount->insertKey(v));
}
//如果删除的keyNode节点是countNode中最后一个keyNode,就要把countAddr也删除了
if (countAddr->keyhead->next == NULL){
countAddr->prev->next = countAddr->next;
if (countAddr->next != NULL)
countAddr->next->prev = countAddr->prev;
delete countAddr;
}
}
}
//返回关键字的频数
int lookup(const T& v)const{
return dict[v].first->freq;
}
/**返回频数最高的n个元素
返回形式为:(key,count)
**/
vector<pair<T, int>> most_common(int n){
//链表的顺序是频数从低到高的,此时需要从尾节点逆向遍历n个元素
vector<pair<T, int>> result;
countNode<T>* countVisitor = tail;
while (n > 0 && countVisitor != NULL){
keyNode<T>* keyVisitor = countVisitor->keyhead->next;
while (n > 0 && keyVisitor != NULL){
result.emplace_back(keyVisitor->val, countVisitor->freq);
n--;
keyVisitor = keyVisitor->next;
}
countVisitor = countVisitor->prev;
}
return result;
}
vector<pair<T, int>> least_common(int n){
vector<pair<T, int>> result;
countNode<T>* countVisitor = head->next;
while (n > 0 && countVisitor != NULL){
keyNode<T>* keyVisitor = countVisitor->keyhead->next;
while (n > 0 && keyVisitor != NULL){
result.emplace_back(keyVisitor->val, countVisitor->freq);
n--;
keyVisitor = keyVisitor->next;
}
countVisitor = countVisitor->next;
}
return result;
}
private:
countNode<T>* head;
countNode<T>* tail;
unordered_map<T, pair<countNode<T>*, keyNode<T>*>> dict;
};
int main(){
{
Counter<char> wordCount;
string s("jfoaedfrerlkmgvj9ejajiokl;fdaks");
for (auto v : s){
wordCount.insert(v);
}
auto result = wordCount.least_common(3);
}
return 0;
}
C++实现python标准库中的Counter的更多相关文章
- Python 标准库中的装饰器
题目描述 1.简单举例 Python 标准库中的装饰器 2.说说你用过的 Python 标准库中的装饰器 1. 首先,我们比较熟悉,也是比较常用的 Python 标准库提供的装饰器有:property ...
- (转)python标准库中socket模块详解
python标准库中socket模块详解 socket模块简介 原文:http://www.lybbn.cn/data/datas.php?yw=71 网络上的两个程序通过一个双向的通信连接实现数据的 ...
- Python标准库中的生成器函数
一.用于过滤的生成器函数 - 从输入的可迭代对象中产出元素的子集,而不修改元素本身 import itertools l1 = [1,2,3,4,5] l2 = [True,False,True,Fa ...
- 06.队列、python标准库中的双端队列、迷宫问题
class QueueUnderflow(ValueError): """队列为空""" pass class SQueue: def __ ...
- python标准库中socket模块详解
包含原理就是tcp的三次握手 http://www.lybbn.cn/data/datas.php?yw=71 这篇讲到了socket和django的联系 https://www.cnblogs.co ...
- Python标准库14 数据库 (sqlite3)
作者:Vamei 出处:http://www.cnblogs.com/vamei 欢迎转载,也请保留这段声明.谢谢! Python自带一个轻量级的关系型数据库SQLite.这一数据库使用SQL语言.S ...
- python标准库00 学习准备
Python标准库----走马观花 python有一套很有用的标准库.标准库会随着python解释器一起安装在你的电脑上的.它是python的一个组成部分.这些标准库是python为你准备的利器,可以 ...
- python标准库xml.etree.ElementTree的bug
使用python生成或者解析xml的方法用的最多的可能就数python标准库xml.etree.ElementTree和lxml了,在某些环境下使用xml.etree.ElementTree更方便一些 ...
- 【python】Python标准库defaultdict模块
来源:http://www.ynpxrz.com/n1031711c2023.aspx Python标准库中collections对集合类型的数据结构进行了很多拓展操作,这些操作在我们使用集合的时候会 ...
随机推荐
- Qt之QPauseAnimation
简述 QPauseAnimation类为QSequentialAnimationGroup提供了一个暂停. 如果你想为QSequentialAnimationGroup动画之间添加延迟,可以插入一个Q ...
- java多线程下如何调用一个共同的内存单元(调用同一个对象)
/* * 关于线程下共享相同的内存单元(包括代码与数据) * ,并利用这些共享单元来实现数据交换,实时通信与必要的同步操作. * 对于Thread(Runnable target)构造方法创建的线程, ...
- Excepion
异常:就是程序在运行时出现不正常的情况. 异常由来:问题也就是现实生活中一个具体的食物,也可以通过java的类的形式进行秒速.并封装成对象.其实就是java对不正常情况进行毛素后的对象体现. 对于问题 ...
- spring之初识Ioc&Aop
Spring框架的作用 spring是一个轻量级的企业级框架,提供了ioc容器.Aop实现.dao/orm支持.web集成等功能,目标是使现有的java EE技术更易用,并促进良好的编程习惯. Spr ...
- MATLAB实现矩阵分块相乘
要实现一下功能,这里$\bf{x}_i$为行向量 $${\bf{A}} = \left[ \begin{array}{l}{{\bf{x}}_1}\\{{\bf{x}}_2}\end{array} \ ...
- C++中颜色的设置
1.改变整个控制台的颜色用 system("color 0A"); 其中color后面的0是背景色代号,A是前景色代号.各颜色代码如下: 0=黑色 1=蓝色 2=绿色 3=湖蓝色 ...
- windows下捕获dump之Google breakpad_client的理解
breakpad是Google开源的一套跨平台工具,用于dump的处理.很全的一套东西,我这里只简单涉及breakpad客户端,不涉及纯文本符号生成,不涉及dump解析. 一.使用 最简单的是使用进程 ...
- 如何采集所有QQ群成员?
首先,你需要有一个CHROME浏览器其实,你要装一个叫REGEX SCRAPER的插件 在qun.qzone.qq.com打开你的QQ群页面-查看群成员 点击REGEX 插件, 粘贴上这个代码 tex ...
- jQuery的bind()与live()
前言 最近一个项目的前端有这样的一个需求:页面中有某按钮,点击按钮之后通过服务器的返回信息更改这个按钮的点击事件执行函数. 方案1 之前小猪使用的方法是给按钮增加class.在jquery中通过cla ...
- MySQL存储过程之事务管理
原文链接:http://hideto.iteye.com/blog/195275 MySQL存储过程之事务管理 ACID:Atomic.Consistent.Isolated.Durable 存储程序 ...