数据挖掘作业——FP Tree算法之C++实现
2014-12-08 21:28
906 查看
#include<stdio.h> #include <cstdio> #include<string> #include<math.h> #include<stdlib.h> #include<set> #include<map> #include<vector> #include<queue> #include<string.h> #include<algorithm> #include<iostream> #include<time.h> #include<list> using namespace std; //Author: I-Hsin const int maxn=10000;//输入记录条数的上界 const int sigmasize=1000;//项的种类的上界 struct ListNode//Head Table中的节点 { int item;//项的名称 int cnt;//项的计数 ListNode() { item=0; cnt=0; } ListNode(int i,int c)//构造函数 { item=i; cnt=c; } }; struct TreeNode//FP Tree的节点 { int parent;//父亲节点的编号 vector<int>child;//孩子节点的编号 int cnt;//该节点的计数 int item;//该节点项的名称 TreeNode() { parent=0; cnt=0; item=0; child.clear(); } bool Empty() { if(parent==0&&cnt==0&&item==0&&child.empty()) //判断节点是否为空节点 { return true; } return false; } }; TreeNode Copy(TreeNode t)//复制FP Tree的节点 { TreeNode ret; ret.parent=t.parent; ret.cnt=t.cnt; ret.item=t.item; ret.child=t.child; return ret; } int tree[maxn][sigmasize];//字典树,用来实现FP Tree list<ListNode>database[maxn];//原始数据库 list<ListNode>HeadTable;//项头表 int Count[maxn];//原始数据库每一项的出现次数 int minsupportcnt;//最小支持度计数 int totalnum;//item的种类数 int num;//记录条数 int maxsz;//Tire树最多节点数 map<string,int> mp;//用于将string型的输入数据映射成int存储,以方便后续操作 int linenum;//原始数据库有多少条记录 string printitem(int t)//输出某个项,需要将int还原成原始数据库中的string { string s; for(map<string,int>::iterator it=mp.begin();it!=mp.end();it++) { if(it->second==t)//first分量是string, second是int { s=it->first; return s; } } return s; } TreeNode* BuildTree(list<ListNode>a [],int num)//建立FP Tree,参数a为数据库,参数num为数据库记录数量 { TreeNode Val[maxn];//用数组存储数节点,可以通过每个节点的parent和child还原整个树,Val[0]存根节点 memset(tree,0,sizeof(tree)); int sz=1;//记录FP树的节点数 for(int i=0;i<num;i++)//遍历数据库中的所有记录 { int u=0;//添加某一条记录时要从根节点开始 for(list<ListNode>::iterator it=a[i].begin();it!=a[i].end();it++)//遍历每一条记录的所有项 { ListNode t=*it; int c=t.item; if(tree[u][c]==0)//如果u的孩子中没有c,则要添加新节点c { memset(tree[sz],0,sizeof(tree[sz])); TreeNode tmp=TreeNode(); tmp.cnt=t.cnt; tmp.item=c; tmp.parent=u; Val[sz]=tmp;//在Val[]中添加新节点,且新节点的编号是sz Val[u].child.push_back(sz);//u的孩子节点中要添加c tree[u][c]=sz++;//字典数中新建节点 } else//如果u的孩子中有c,只要更新c的计数 { for(int k=0;k<Val[u].child.size();k++)//遍历u的所有孩子 { if(Val[Val[u].child[k]].item==c) { Val[Val[u].child[k]].cnt+=t.cnt;//节点c计数+t.cnt break; } } } u=tree[u][c];//往下走 } } maxsz=sz;//更新FP Tree节点总数 return Val;//返回FP Tree } void Input() { scanf("%d %d",&linenum,&minsupportcnt);//输入原始数据库中的记录条数和最小支持度计数 int idx=1;//将string型的项用int重新编号 for(int i=0;i<linenum;i++) { while(true) { string s; cin>>s; if(s=="0") break;//每一条记录末尾是0,表示该条记录输入完毕 map<string,int>::iterator l_it; l_it=mp.find(s);//返回的是一个指针 if(l_it==mp.end())//如果该记录之前没有输入过,给该记录编号并放入database[i]中 { database[i].push_back(ListNode(idx,1)); Count[idx]++; mp[s]=idx; idx++; } else//如果该记录之前输入过,直接增加其计数 { database[i].push_back(ListNode(mp[s],1)); Count[mp[s]]++; } } } totalnum=idx;//记录项的种类总数 } bool cmp(ListNode a,ListNode b)//用于将项头表中的项按个数从大到小排序 { return a.cnt>b.cnt; } int priority[maxn];//记录Head Table每个项的优先级 bool cmp2(ListNode a,ListNode b)//用于将条件模式基中的项按Head Table中的次序排序 { int x=a.item; int y=b.item; if(priority[x]<priority[y])//标记越小优先级越高,在head中cnt越大 return true; return false; } void printlist(list<ListNode>head)//输出链表(用于调试) { for(list<ListNode>::iterator iter=head.begin();iter!=head.end();iter++) { string str=printitem((*iter).item); cout<<str<<" "<<(*iter).cnt<<endl; } cout<<endl; } list<ListNode> BuildConditionalHeadTable(list<ListNode>*a,int n)//a为数据库,该函数为指针调用会改变a的值 { int CondCount[maxn];//记录a每个项的出现次数 memset(CondCount,0,sizeof(CondCount)); list<ListNode>head;//项头表 for(int i=0;i<n;i++)//遍历a中每个项 { list<ListNode>::iterator iter=a[i].begin(); for(iter;iter!=a[i].end();iter++) { ListNode t=*iter; int c=t.item; CondCount[c]+=t.cnt;//记录a中每个项的出现次数 } } for(int i=0;i<totalnum;i++) { if(CondCount[i]<minsupportcnt) continue; head.push_back(ListNode(i,CondCount[i]));//如果某个项出现次数满足最小支持度计数,则把它加入项头表中 } head.sort(cmp);//将项头表按照每个项出现次数从大到小排序 memset(priority,0,sizeof(priority)); int pir=0; for(list<ListNode>::iterator iter=head.begin();iter!=head.end();iter++) { ListNode t=*iter; int c=t.item; priority[c]=pir++;//priority[c]记录项c在Head Table中的优先级(出现次数越多优先级越高),priority[c]值越小在cmp2中越优先 } for(int i=0;i<n;i++) { list<ListNode>::iterator iter=a[i].begin(); for(iter;iter!=a[i].end();) { ListNode t=*iter; int c=t.item; if(CondCount[c]<minsupportcnt)//删除数据库a中出现次数小于最小支持度计数的项 { a[i].erase(iter++); } else { iter++; } } a[i].sort(cmp2);//将a[i]中的项按照在Head Table的出现次数从大到小排序 } return head;//返回项头表 } void FP_Growth(list<ListNode>transrecords[],list<ListNode>patternbase,int n)//使用FP Tree,通过模式增长挖掘频繁模式 {//transrecords[]是数据库,patternbase是条件模式基,n是数据库记录条数 list<ListNode>head=BuildConditionalHeadTable(transrecords,n);//通过数据库构建项头表 TreeNode *val; val=BuildTree(transrecords,n);// 构建FP-Tree TreeNode tmpval[maxn];//在之后的递归调用会改变val的值,所以要用tmpval保存现场 int tmpmaxsz=maxsz;//在之后的递归调用会改变maxsz的值,所以要用tmpmaxsz保存现场 for(int i=0;i<maxsz;i++) { tmpval[i]=Copy(val[i]); } if(val[0].Empty())// 如果FP-Tree为空则返回 { return; } if(!patternbase.empty())//输出项头表的每一项+postPattern { for(list<ListNode>::iterator iter=head.begin();iter!=head.end();iter++) { ListNode t=*iter; string str=printitem(t.item);//需要将int还原成原始数据库中的string int cnt=t.cnt; for(list<ListNode>::iterator it=patternbase.begin();it!=patternbase.end();it++) { str+="\t"+printitem((*it).item);//加上条件模式基 } cout<<str<<" "<<cnt<<endl; } } //对项头表中的每一个节点,构造其条件模式基与条件FP Tree for(list<ListNode>::reverse_iterator iter=head.rbegin();iter!=head.rend();iter++)//反向遍历Head Table(从小到大) { ListNode t=*iter; list<ListNode>newpostpattern;//新的条件模式基 newpostpattern.push_back(ListNode(t.item,t.cnt));//新的条件模式基中先加入项头表当前项 if(!patternbase.empty())//如果之前的条件模式基不为空,则合并 { list<ListNode>tmp=patternbase; newpostpattern.merge(patternbase,cmp); patternbase=tmp; } list<ListNode>newdatabase[maxn];//条件数据库 int id=0; for(int i=0;i<maxsz;i++) { if(val[i].item==t.item)//在当前FP Tree中找到所有项为t.item的节点 { for(int pre=val[i].parent;pre!=0;pre=val[pre].parent)//由parent从下往根节点遍历,找出前缀路径 { newdatabase[id].push_back(ListNode(val[pre].item,val[i].cnt));//将路径中的每个节点加入新数据库中 //注意前缀路径中的每个节点的计数都更新为当前项的计数,即t.cnt } id++; } } FP_Growth(newdatabase,newpostpattern,id);//由新的数据库和新的条件模式基递归调用,在条件FP Tree中继续挖掘频繁模式 //恢复现场 maxsz=tmpmaxsz; for(int i=0;i<maxsz;i++) { val[i]=Copy(tmpval[i]); } } } void run() { Input(); list<ListNode>patternbase=list<ListNode>();//第一次由原始数据库构建FP Tree时,后缀项集初始化为空链表 FP_Growth(database,patternbase,linenum);//用FP Tree递归挖掘频繁模式 } int main() { printf("Author: I-Hsin\n\n"); freopen("Test6.txt","r",stdin); //freopen("Test4.txt","r",stdin); //freopen("Test5.txt","r",stdin); run(); return 0; } //输入样例:(文件读入,每一条记录末尾添上0作为该条记录的结束标志) //9 3 //牛奶 鸡蛋 面包 薯片 0 //鸡蛋 爆米花 薯片 啤酒 0 //鸡蛋 面包 薯片 0 //牛奶 鸡蛋 面包 爆米花 薯片 啤酒 0 //牛奶 面包 啤酒 0 //鸡蛋 面包 啤酒 0 //牛奶 面包 薯片 0 //牛奶 鸡蛋 面包 黄油 薯片 0 //牛奶 鸡蛋 黄油 薯片 0
相关文章推荐
- 数据挖掘作业——K-Means算法之C++实现
- 数据挖掘—概念空间挖掘FindS算法的C++实现
- 数据挖掘-基于机器学习的SNS隐私策略推荐向导分类器的C++及WEKA实现与评估
- 数据挖掘分类算法之贝叶斯分类法原理及C++实现
- 数据挖掘实验:Apriori算法实现C++
- 数据挖掘—概念空间挖掘FindS算法的C++实现
- C++---Apriori算法实现,频繁模式数据挖掘,最大频繁项集,闭频繁项集
- Python数据挖掘课程 三.Kmeans聚类代码实现、作业及优化
- 【Python数据挖掘课程】三.Kmeans聚类代码实现、作业及优化
- 【Python数据挖掘课程】三.Kmeans聚类代码实现、作业及优化
- 数据挖掘-决策树ID3分类算法的C++实现
- 数据挖掘-决策树ID3分类算法的C++实现
- 数据挖掘—概念学习Candidate-Elimination算法的C++实现
- 数据挖掘—概念学习Candidate-Elimination算法的C++实现
- 数据挖掘-关联分析频繁模式挖掘Apriori、FP-Growth及Eclat算法的JAVA及C++实现
- 数据挖掘-关联分析频繁模式挖掘Apriori、FP-Growth及Eclat算法的JAVA及C++实现
- 数据挖掘-决策树ID3分类算法的C++实现
- 数据挖掘实验:决策树算法实现C++
- 数据挖掘-基于机器学习的SNS隐私策略推荐向导分类器的C++及WEKA实现与评估
- 数据挖掘-决策树ID3分类算法的C++实现