您的位置:首页 > 编程语言 > C语言/C++

数据挖掘作业——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
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: