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;
}
时间: 2024-08-27 21:18:06

C++实现python标准库中的Counter的相关文章

python标准库Beautiful Soup与MongoDb爬喜马拉雅电台的总结

Beautiful Soup标准库是一个可以从HTML/XML文件中提取数据的Python库,它能够通过你喜欢的转换器实现惯用的文档导航,查找,修改文档的方式,Beautiful Soup将会节省数小时的工作时间.pymongo标准库是MongoDb NoSql数据库与python语言之间的桥梁,通过pymongo将数据保存到MongoDb中.结合使用这两者来爬去喜马拉雅电台的数据... Beautiful Soup支持Python标准库中的HTML解析器,还支持一些第三方的解析器,其中一个是

Python标准库14 数据库 (sqlite3)

Python标准库14 数据库 (sqlite3) 作者:Vamei 出处:http://www.cnblogs.com/vamei 欢迎转载,也请保留这段声明.谢谢! Python自带一个轻量级的关系型数据库SQLite.这一数据库使用SQL语言.SQLite作为后端数据库,可以搭配Python建网站,或者制作有数据存储需求的工具.SQLite还在其它领域有广泛的应用,比如HTML5和移动端.Python标准库中的sqlite3提供该数据库的接口. 我将创建一个简单的关系型数据库,为一个书店存

[python标准库]XML模块

1.什么是XML XML是可扩展标记语言(Extensible Markup Language)的缩写,其中的 标记(markup)是关键部分.您可以创建内容,然后使用限定标记标记它,从而使每个单词.短语或块成为可识别.可分类的信息. XML有以下几个特点. XML的设计宗旨是传输数据,而非显示数据. XML标签没有被预定义.您需要自行定义标签. XML被设计为具有自我描述性. XML是W3C的推荐标准. 其解析流程如下图: 2.常用解析XML的Python包 Python的标准库中,提供了6种

Python标准库07 信号 (signal包,部分os包)

作者:Vamei 出处:http://www.cnblogs.com/vamei 欢迎转载,也请保留这段声明.谢谢! 在了解了Linux的信号基础之后,Python标准库中的signal包就很容易学习和理解.signal包负责在Python程序内部处理信号,典型的操作包括预设信号处理函数,暂停并等待信号,以及定时发出SIGALRM等.要注意,signal包主要是针对UNIX平台(比如Linux, MAC OS),而Windows内核中由于对信号机制的支持不充分,所以在Windows上的Pytho

Python标准库的学习准备

作者:Vamei 出处:http://www.cnblogs.com/vamei 欢迎转载,也请保留这段声明.谢谢! Python标准库是Python强大的动力所在,我们已经在前文中有所介绍.由于标准库所涉及的应用很广,所以需要学习一定的背景知识. 硬件原理 这一部份需要了解内存,CPU,磁盘存储以及IO的功能和性能,了解计算机工作的流程,了解指令的概念.这些内容基础而重要. Python标准库的一部份是为了提高系统的性能(比如mmap),所以有必要了解基本的计算机各个组成部分的性能. 操作系统

Python标准库——走马观花

Python标准库——走马观花 作者:Vamei 出处:http://www.cnblogs.com/vamei 欢迎转载,也请保留这段声明.谢谢! Python有一套很有用的标准库(standard library).标准库会随着Python解释器,一起安装在你的电脑中的.它是Python的一个组成部分.这些标准库是Python为你准备好的利器,可以让编程事半功倍. 我将根据我个人的使用经验中,挑选出标准库三个方面的包(package)介绍: Python增强 系统互动 网络 第一类:Pyth

[学习笔记] Python标准库的学习准备 [转]

Python标准库是Python强大的动力所在,我们已经在前文中有所介绍.由于标准库所涉及的应用很广,所以需要学习一定的背景知识. 硬件原理 这一部份需要了解内存,CPU,磁盘存储以及IO的功能和性能,了解计算机工作的流程,了解指令的概念.这些内容基础而重要. Python标准库的一部份是为了提高系统的性能(比如mmap),所以有必要了解基本的计算机各个组成部分的性能. 操作系统 在了解操作系统时,下面是重点: 1) 操作系统的进程管理,比如什么是UID, PID, daemon 2) 进程之间

【python】Python标准库defaultdict模块

来源:http://www.ynpxrz.com/n1031711c2023.aspx Python标准库中collections对集合类型的数据结构进行了很多拓展操作,这些操作在我们使用集合的时候会带来很多的便利,多看看很有好处. defaultdict是其中一个方法,就是给字典value元素添加默认类型,之前看到过但是没注意怎么使用,今天特地瞅了瞅. 首先是各大文章介绍的第一个例子: 代码如下: import collections as coll def default_factory()

Python标准库之collections模块

本文后大家啊分享的主要是python 标准库的collections 模块,一起来看看吧,希望对大家 学习python有所帮助.. 这个模块提供几个非常有用的Python 容器类型 1.容器 2.OrderedDict OrderedDict  类似于正常的词典,只是它记住了元素插入的顺序,当迭代它时,返回它会根据插入的顺序返回. ·  和正常字典相比 , 它是 " 有序 " 的 ( 插入的顺序 ) . from collections import OrderedDict dict1