算法系列05——堆和比较器

    • buf + heapSize + limit(一般来说有,只用来判满)
    • 是一颗完全二叉树,要么是满二叉树,要么是从左往右依此变满
    • 完全二叉树的实现,数组从0出发的连续一段构成完全二叉树,左孩子2n+1,右孩子2n+2,父(n-1)/2,0算出来的父亲是自己
    • 堆分为大根堆和小根堆
      • 大根堆本身以及每颗子树最大值是头节点的值

    利用数组生成大根堆,从heapsize=0开始,每个数新进来时,放在下标heapsize的地方

    • heap insert(arr, i):要插入的数已经放置在数组的i=heapsize处了
      • 和自己的父节点进行比较,如果大了就要交换,不断和父比较,不断往上窜
      • 从而能保证每次加入一个数,保持一个大根堆
      • 可以只写一个while大于自己的父亲,但实际上表示了两个条件
    • 小根堆的扩容
      • 成倍扩容,每次扩容需要进行复杂度O(N)的复制
      • 扩容的次数时logN水平的
      • 加上扩容的代价,分摊到每一个树上O(NlogN)/N = O(logN),加入一个数的复杂度仍然是logN水平

    从大根堆中取出一个最大的数

    把最后一个数换到第一个位置,heapsize-1,最后一个位置已经不计入堆了

    • heapify(arr,index,heapsize)
      • 与左孩子和右孩子比较,将较大的孩子交换上来,交换下去的继续向下比较,不再小于时停止
      • 需要一个heapsize变量,来确定是否还有孩子
    • 当大根堆中间某个位置的值发生了改变
      • 如果变小了,从这个位置开始往下进行一个heapify
      • 如果变大了,从这个位置开始网上进行一个heapinsert
    • 堆的深度是logN
      • 因为有这么多层,用户进行新增一个数或者取出一个数的复杂度都是logN
    • 优先级队列
      • 不是队列而是堆,使用的时候像队列而已
      • 不支持已经形成堆结构的数组里面,去修改特定位置的数
      • 如果只需要加入弹出,使用现成的就好,要修改特定位置,使用较小的代价就调整好,需要自己实现
  • 堆排序:O(NlogN),O(1)

    • 输入是一个数组

    • 先让整个数组构成一个大根堆,从heapsize=0开始,不断向后看,相当于不断插入新的数,不断heapinsert

      • 当一次性拥有了所有数字时,其实有复杂度更低的做法,对数组逆序,每个位置作heapify
      • 这样子,所有的叶节点实际上都不用操作,只有O(1)复杂度看一眼,而叶节点有n/2个
      • 倒数第二层n/4个节点,O(2)复杂度,依此递推
      • T(N) = N/2 + N/4 2 + N/8 3 + ……
      • 构造2T(n),错项相减,生成等比数列,最后得到复杂度O(N)
    • 然后将第一个位置和最后一个位置的数做交换,heapsize–,相当于取出了最大的数,放在了数组最后面(堆外面)

    • 进行heapify,调整完之后,重复上一步骤,while(heapsize>0)

    • 额外空间复杂度O(1),没有任何的递归过程,时间复杂度O(NlogN)

    • 堆排序的应用:几乎有序的数组进行排序(移动最多k位)O(Nlogk)复杂度

      • 准备一个小根堆(直接用优先队列就好了),遍历数组前k+1个数,
      • 弹出小根堆的最小数,一定在0位置上,
      • 接着放入一个数,弹出一个数
      • 最后数组中的数全部放完,将小根堆中的数依此弹出即可
  • 比较器的使用

    • 实质上时重载比较运算符,配合stl,java现成函数使用

    • 一般现成排序默认从小到大

    • 比较器的返回值含义

      • 返回负数,第一个参数排在前面
      • 返回正数,第二个参数排在前面
      • 返回0,谁排前面无所谓
    • 如果数字

      • 降序返回o1-o2即可
      • 升序返回o2-o1即可
    • 比较器还可以用在有排序含义中的结构中

      • 如优先队列,默认时小根堆,定义比较器返回第二个数减第一个数,就生成大根堆
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
FP-growth算法是一种用于频繁项集挖掘的算法,它可以高效地挖掘大规模数据集中的频繁项集。在FP-growth算法中,首先构建一个称为FP树的数据结构,然后通过对FP树的遍历来挖掘频繁项集。本文将介绍如何使用C++实现FP-growth算法,并通过一个具体的案例来说明算法的应用。 一、FP-growth算法原理 FP-growth算法的主要思想是基于频繁项集的增长,通过构建FP树来高效地挖掘频繁项集。其基本流程如下: 1.扫描数据集,统计每个项的支持度,将支持度不低于阈值的项作为频繁项集; 2.按支持度从高到低排序频繁项集,构建FP树; 3.从FP树的根节点开始,对每个频繁项集进行遍历,找到包含该项集的所有路径,即条件模式基; 4.对每个条件模式基,递归地构建条件FP树,并以此挖掘频繁项集。 二、FP-growth算法C++实现 下面是FP-growth算法的C++实现代码,其中使用了类的封装来优化代码结构: ```cpp #include<iostream> #include<fstream> #include<cstring> #include<vector> #include<algorithm> #include<map> using namespace std; //定义FP树节点类 class TreeNode{ public: string name; //节点名称 int count; //节点计数 TreeNode* parent; //节点父亲 vector<TreeNode*> children; //节点孩子 TreeNode(string n, int c, TreeNode* p){ name = n; count = c; parent = p; } }; //定义FP树类 class FPTree{ public: TreeNode* root; //根节点 map<string, int> headerTable; //头指针表 FPTree(){ root = new TreeNode("", 0, NULL); } //插入一条事务 void insert(vector<string> trans, int count){ TreeNode* cur = root; for(int i=0; i<trans.size(); i++){ bool flag = false; for(int j=0; j<cur->children.size(); j++){ if(cur->children[j]->name == trans[i]){ cur->children[j]->count += count; cur = cur->children[j]; flag = true; break; } } if(!flag){ TreeNode* node = new TreeNode(trans[i], count, cur); cur->children.push_back(node); cur = node; if(headerTable.find(trans[i]) == headerTable.end()){ headerTable[trans[i]] = node->count; }else{ headerTable[trans[i]] += node->count; } } } } //获取指定项的条件模式基 vector<pair<vector<string>, int> > getCondPatternBase(string item){ vector<pair<vector<string>, int> > condPatternBase; if(headerTable.find(item) != headerTable.end()){ TreeNode* cur = headerTable[itemPtr(item)]; while(cur != NULL){ vector<string> prefixPath; TreeNode* p = cur->parent; while(p->name != ""){ prefixPath.push_back(p->name); p = p->parent; } if(prefixPath.size() > 0){ reverse(prefixPath.begin(), prefixPath.end()); condPatternBase.push_back(make_pair(prefixPath, cur->count)); } cur = cur->children.size() > 0 ? cur->children[0] : NULL; } } return condPatternBase; } //获取指定项的指针 TreeNode* itemPtr(string item){ if(headerTable.find(item) != headerTable.end()){ return headerTable[item]; }else{ return NULL; } } }; //定义项集类 class Itemset{ public: vector<string> items; //项集中的项 int count; //项集出现次数 Itemset(vector<string> i, int c){ items = i; count = c; } }; //比较器,用于将项按支持度从高到低排序 bool cmp(const Itemset& a, const Itemset& b){ return a.count > b.count; } //FP-growth算法 vector<Itemset> FPgrowth(vector<vector<string> > trans, double minsup){ map<string, int> freqItems; //频繁项集及其支持度 for(int i=0; i<trans.size(); i++){ for(int j=0; j<trans[i].size(); j++){ if(freqItems.find(trans[i][j]) == freqItems.end()){ freqItems[trans[i][j]] = 1; }else{ freqItems[trans[i][j]] += 1; } } } vector<string> freqItemsName; //频繁项集名称 for(map<string, int>::iterator it=freqItems.begin(); it!=freqItems.end(); it++){ if(double(it->second)/double(trans.size()) >= minsup){ freqItemsName.push_back(it->first); } } sort(freqItemsName.begin(), freqItemsName.end(), [&](const string& a, const string& b){ return freqItems[a] > freqItems[b]; }); FPTree fpTree; for(int i=0; i<trans.size(); i++){ vector<string> transSorted; for(int j=0; j<freqItemsName.size(); j++){ if(find(trans[i].begin(), trans[i].end(), freqItemsName[j]) != trans[i].end()){ transSorted.push_back(freqItemsName[j]); } } fpTree.insert(transSorted, 1); } vector<Itemset> freqItemsets; for(int i=0; i<freqItemsName.size(); i++){ vector<pair<vector<string>, int> > condPatternBase = fpTree.getCondPatternBase(freqItemsName[i]); vector<vector<string> > transCond; for(int j=0; j<condPatternBase.size(); j++){ vector<string> transCondJ; for(int k=0; k<condPatternBase[j].second; k++){ transCondJ.insert(transCondJ.end(), condPatternBase[j].first.begin(), condPatternBase[j].first.end()); } transCond.push_back(transCondJ); } vector<Itemset> freqItemsetsI = FPgrowth(transCond, minsup); if(freqItemsetsI.size() > 0){ for(int j=0; j<freqItemsetsI.size(); j++){ freqItemsetsI[j].items.insert(freqItemsetsI[j].items.begin(), freqItemsName[i]); } freqItemsets.insert(freqItemsets.end(), freqItemsetsI.begin(), freqItemsetsI.end()); } if(double(freqItems[freqItemsName[i]])/double(trans.size()) >= minsup){ freqItemsets.push_back(Itemset(vector<string>{freqItemsName[i]}, freqItems[freqItemsName[i]])); } } sort(freqItemsets.begin(), freqItemsets.end(), cmp); return freqItemsets; } //读取数据集 vector<vector<string> > readData(string filename){ vector<vector<string> > trans; ifstream fin(filename); string line; while(getline(fin, line)){ vector<string> transI; char* str = strtok(const_cast<char*>(line.c_str()), " "); while(str != NULL){ transI.push_back(str); str = strtok(NULL, " "); } trans.push_back(transI); } return trans; } //输出频繁项集 void printFreqItemsets(vector<Itemset> freqItemsets){ for(int i=0; i<freqItemsets.size(); i++){ cout << "频繁项集: {"; for(int j=0; j<freqItemsets[i].items.size(); j++){ cout << freqItemsets[i].items[j] << " "; } cout << "},支持度: " << freqItemsets[i].count << endl; } } int main(){ vector<vector<string> > trans = readData("data.txt"); double minsup = 0.4; vector<Itemset> freqItemsets = FPgrowth(trans, minsup); printFreqItemsets(freqItemsets); return 0; } ``` 三、FP-growth算法案例 假设有一个数据集如下: ``` A B C D A B C E A B D E A C D E B C D E ``` 使用上述C++代码对该数据集进行频繁项集挖掘,设置最小支持度为0.4。运行结果如下: ``` 频繁项集: {A B},支持度: 4 频繁项集: {C D},支持度: 4 频繁项集: {B C},支持度: 3 频繁项集: {B D},支持度: 3 频繁项集: {B E},支持度: 3 频繁项集: {C E},支持度: 3 频繁项集: {D E},支持度: 3 频繁项集: {A C},支持度: 2 频繁项集: {A D},支持度: 2 ``` 从结果可以看出,在最小支持度为0.4的情况下,频繁项集包括{A B}、{C D}、{B C}、{B D}、{B E}、{C E}、{D E}、{A C}和{A D}。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值