FP-Tree频繁模式树算法

来源:互联网 发布:二次元动态桌面软件 编辑:程序博客网 时间:2024/05/16 12:34

参考资料http://blog.csdn.net/sealyao/article/details/6460578
更多数据挖掘算法https://github.com/linyiqun/DataMiningAlgorithm

介绍

FP-Tree算法全称是FrequentPattern Tree算法,就是频繁模式树算法,他与Apriori算法一样也是用来挖掘频繁项集的,不过不同的是,FP-Tree算法是Apriori算法的优化处理,他解决了Apriori算法在过程中会产生大量的候选集的问题,而FP-Tree算法则是发现频繁模式而不产生候选集。但是频繁模式挖掘出来后,产生关联规则的步骤还是和Apriori是一样的。

算法原理

FP树,FP树,那他当然是最终被构造成一个树的形状了。所以步骤如下:

1、创建根节点,用NULL标记。

2、统计所有的事务数据,统计事务中各个类型项的总支持度(在下面的例子中就是各个商品ID的总个数)

3、依次读取每条事务,比如T1, 1, 2, 5,因为按照总支持度计数数量降序排列,输入的数据顺序就是2, 1, 5,然后挂到根节点上。

4、依次读取后面的事务,并以同样的方式加入的FP树中,顺着根节点路径添加,并更新节点上的支持度计数。

最后就会形成这样的一棵树:

image

然后还要新建一个项头表,代表所有节点的类型和支持度计数。这个东西在后面会有大用处。如果你以为FP树的算法过程到这里就结束了,你就大错特错了,算法的终结过程为最后的FP树只包括但路径,就是树呈现直线形式,也就是节点都只有1个孩子或没有孩子,顺着一条线下来,没有其他的分支。这就算是一条挖掘出的频繁模式。所以上面的算法还要继续递归的构造FP树,递归构造FP树的过程:

1、这时我们从最下面的I5开始取出。把I5加入到后缀模式中。后缀模式到时会于频繁模式组合出现构成最终的频繁模式。

2、获取频繁模式基,<I2, Ii>,<I2, I1, I3>,计数为I5节点的count值,然后以这2条件模式基为输入的事务,继续构造一个新的FP树

绘图1

3、这就是我们要达到的FP树单路径的目标了,不过这里个要求,要把支持度计数不够的点排除,这里的I3:1就不符号,所以最后I5后缀模式下的<I2, I1>与I5的组合模式了,就为<I2, I5>, <I1, I5>,<I1, I2, I5>。

I5下的挖掘频繁模式是比较简单的,没有出现递归,看一下I3下的递归构造,这就不简单了,同样的操作,最后就会出现下面这幅图的样子:

      image         

发现还不是单条路径,继续递归构造,此时的后缀模式硬卧I3+I1,就是<I3, I1>,然后就来到了下面这幅图的情形了。

绘图2

后面的例子会有更详细的说明。

算法的实现

输入数据如下:

交易ID

商品ID列表

T100

I1I2I5

T200

I2I4

T300

I2I3

T400

I1I2I4

T500

I1I3

T600

I2I3

T700

I1I3

T800

I1I2I3I5

T900

I1I2I3

在文件中的形式就是:

[java] view plain copy
 print?
  1. T1 1 2 5    
  2. T2 2 4    
  3. T3 2 3    
  4. T4 1 2 4    
  5. T5 1 3    
  6. T6 2 3    
  7. T7 1 3    
  8. T8 1 2 3 5    
  9. T9 1 2 3   
算法的树节点类:

[java] view plain copy
 print?
  1. /** 
  2.  * FP树节点 
  3.  *  
  4.  * @author lyq 
  5.  *  
  6.  */  
  7. public class TreeNode implements Comparable<TreeNode>, Cloneable{  
  8.     // 节点类别名称  
  9.     private String name;  
  10.     // 计数数量  
  11.     private Integer count;  
  12.     // 父亲节点  
  13.     private TreeNode parentNode;  
  14.     // 孩子节点,可以为多个  
  15.     private ArrayList<TreeNode> childNodes;  
  16.       
  17.     public TreeNode(String name, int count){  
  18.         this.name = name;  
  19.         this.count = count;  
  20.     }  
  21.   
  22.     public String getName() {  
  23.         return name;  
  24.     }  
  25.   
  26.     public void setName(String name) {  
  27.         this.name = name;  
  28.     }  
  29.   
  30.     public Integer getCount() {  
  31.         return count;  
  32.     }  
  33.   
  34.     public void setCount(Integer count) {  
  35.         this.count = count;  
  36.     }  
  37.   
  38.     public TreeNode getParentNode() {  
  39.         return parentNode;  
  40.     }  
  41.   
  42.     public void setParentNode(TreeNode parentNode) {  
  43.         this.parentNode = parentNode;  
  44.     }  
  45.   
  46.     public ArrayList<TreeNode> getChildNodes() {  
  47.         return childNodes;  
  48.     }  
  49.   
  50.     public void setChildNodes(ArrayList<TreeNode> childNodes) {  
  51.         this.childNodes = childNodes;  
  52.     }  
  53.   
  54.     @Override  
  55.     public int compareTo(TreeNode o) {  
  56.         // TODO Auto-generated method stub  
  57.         return o.getCount().compareTo(this.getCount());  
  58.     }  
  59.   
  60.     @Override  
  61.     protected Object clone() throws CloneNotSupportedException {  
  62.         // TODO Auto-generated method stub  
  63.         //因为对象内部有引用,需要采用深拷贝  
  64.         TreeNode node = (TreeNode)super.clone();   
  65.         if(this.getParentNode() != null){  
  66.             node.setParentNode((TreeNode) this.getParentNode().clone());  
  67.         }  
  68.           
  69.         if(this.getChildNodes() != null){  
  70.             node.setChildNodes((ArrayList<TreeNode>) this.getChildNodes().clone());  
  71.         }  
  72.           
  73.         return node;  
  74.     }  
  75.       
  76. }  
算法主要实现类:

[java] view plain copy
 print?
  1. package DataMining_FPTree;  
  2.   
  3. import java.io.BufferedReader;  
  4. import java.io.File;  
  5. import java.io.FileReader;  
  6. import java.io.IOException;  
  7. import java.util.ArrayList;  
  8. import java.util.Collections;  
  9. import java.util.HashMap;  
  10. import java.util.Map;  
  11.   
  12. /** 
  13.  * FPTree算法工具类 
  14.  *  
  15.  * @author lyq 
  16.  *  
  17.  */  
  18. public class FPTreeTool {  
  19.     // 输入数据文件位置  
  20.     private String filePath;  
  21.     // 最小支持度阈值  
  22.     private int minSupportCount;  
  23.     // 所有事物ID记录  
  24.     private ArrayList<String[]> totalGoodsID;  
  25.     // 各个ID的统计数目映射表项,计数用于排序使用  
  26.     private HashMap<String, Integer> itemCountMap;  
  27.   
  28.     public FPTreeTool(String filePath, int minSupportCount) {  
  29.         this.filePath = filePath;  
  30.         this.minSupportCount = minSupportCount;  
  31.         readDataFile();  
  32.     }  
  33.   
  34.     /** 
  35.      * 从文件中读取数据 
  36.      */  
  37.     private void readDataFile() {  
  38.         File file = new File(filePath);  
  39.         ArrayList<String[]> dataArray = new ArrayList<String[]>();  
  40.   
  41.         try {  
  42.             BufferedReader in = new BufferedReader(new FileReader(file));  
  43.             String str;  
  44.             String[] tempArray;  
  45.             while ((str = in.readLine()) != null) {  
  46.                 tempArray = str.split(" ");  
  47.                 dataArray.add(tempArray);  
  48.             }  
  49.             in.close();  
  50.         } catch (IOException e) {  
  51.             e.getStackTrace();  
  52.         }  
  53.   
  54.         String[] temp;  
  55.         int count = 0;  
  56.         itemCountMap = new HashMap<>();  
  57.         totalGoodsID = new ArrayList<>();  
  58.         for (String[] a : dataArray) {  
  59.             temp = new String[a.length - 1];  
  60.             System.arraycopy(a, 1, temp, 0, a.length - 1);  
  61.             totalGoodsID.add(temp);  
  62.             for (String s : temp) {  
  63.                 if (!itemCountMap.containsKey(s)) {  
  64.                     count = 1;  
  65.                 } else {  
  66.                     count = ((int) itemCountMap.get(s));  
  67.                     // 支持度计数加1  
  68.                     count++;  
  69.                 }  
  70.                 // 更新表项  
  71.                 itemCountMap.put(s, count);  
  72.             }  
  73.         }  
  74.     }  
  75.   
  76.     /** 
  77.      * 根据事物记录构造FP树 
  78.      */  
  79.     private void buildFPTree(ArrayList<String> suffixPattern,  
  80.             ArrayList<ArrayList<TreeNode>> transctionList) {  
  81.         // 设置一个空根节点  
  82.         TreeNode rootNode = new TreeNode(null0);  
  83.         int count = 0;  
  84.         // 节点是否存在  
  85.         boolean isExist = false;  
  86.         ArrayList<TreeNode> childNodes;  
  87.         ArrayList<TreeNode> pathList;  
  88.         // 相同类型节点链表,用于构造的新的FP树  
  89.         HashMap<String, ArrayList<TreeNode>> linkedNode = new HashMap<>();  
  90.         HashMap<String, Integer> countNode = new HashMap<>();  
  91.         // 根据事物记录,一步步构建FP树  
  92.         for (ArrayList<TreeNode> array : transctionList) {  
  93.             TreeNode searchedNode;  
  94.             pathList = new ArrayList<>();  
  95.             for (TreeNode node : array) {  
  96.                 pathList.add(node);  
  97.                 nodeCounted(node, countNode);  
  98.                 searchedNode = searchNode(rootNode, pathList);  
  99.                 childNodes = searchedNode.getChildNodes();  
  100.   
  101.                 if (childNodes == null) {  
  102.                     childNodes = new ArrayList<>();  
  103.                     childNodes.add(node);  
  104.                     searchedNode.setChildNodes(childNodes);  
  105.                     node.setParentNode(searchedNode);  
  106.                     nodeAddToLinkedList(node, linkedNode);  
  107.                 } else {  
  108.                     isExist = false;  
  109.                     for (TreeNode node2 : childNodes) {  
  110.                         // 如果找到名称相同,则更新支持度计数  
  111.                         if (node.getName().equals(node2.getName())) {  
  112.                             count = node2.getCount() + node.getCount();  
  113.                             node2.setCount(count);  
  114.                             // 标识已找到节点位置  
  115.                             isExist = true;  
  116.                             break;  
  117.                         }  
  118.                     }  
  119.   
  120.                     if (!isExist) {  
  121.                         // 如果没有找到,需添加子节点  
  122.                         childNodes.add(node);  
  123.                         node.setParentNode(searchedNode);  
  124.                         nodeAddToLinkedList(node, linkedNode);  
  125.                     }  
  126.                 }  
  127.   
  128.             }  
  129.         }  
  130.   
  131.         // 如果FP树已经是单条路径,则输出此时的频繁模式  
  132.         if (isSinglePath(rootNode)) {  
  133.             printFrequentPattern(suffixPattern, rootNode);  
  134.             System.out.println("-------");  
  135.         } else {  
  136.             ArrayList<ArrayList<TreeNode>> tList;  
  137.             ArrayList<String> sPattern;  
  138.             if (suffixPattern == null) {  
  139.                 sPattern = new ArrayList<>();  
  140.             } else {  
  141.                 // 进行一个拷贝,避免互相引用的影响  
  142.                 sPattern = (ArrayList<String>) suffixPattern.clone();  
  143.             }  
  144.   
  145.             // 利用节点链表构造新的事务  
  146.             for (Map.Entry entry : countNode.entrySet()) {  
  147.                 // 添加到后缀模式中  
  148.                 sPattern.add((String) entry.getKey());  
  149.                 //获取到了条件模式机,作为新的事务  
  150.                 tList = getTransactionList((String) entry.getKey(), linkedNode);  
  151.                   
  152.                 System.out.print("[后缀模式]:{");  
  153.                 for(String s: sPattern){  
  154.                     System.out.print(s + ", ");  
  155.                 }  
  156.                 System.out.print("}, 此时的条件模式基:");  
  157.                 for(ArrayList<TreeNode> tnList: tList){  
  158.                     System.out.print("{");  
  159.                     for(TreeNode n: tnList){  
  160.                         System.out.print(n.getName() + ", ");  
  161.                     }  
  162.                     System.out.print("}, ");  
  163.                 }  
  164.                 System.out.println();  
  165.                 // 递归构造FP树  
  166.                 buildFPTree(sPattern, tList);  
  167.                 // 再次移除此项,构造不同的后缀模式,防止对后面造成干扰  
  168.                 sPattern.remove((String) entry.getKey());  
  169.             }  
  170.         }  
  171.     }  
  172.   
  173.     /** 
  174.      * 将节点加入到同类型节点的链表中 
  175.      *  
  176.      * @param node 
  177.      *            待加入节点 
  178.      * @param linkedList 
  179.      *            链表图 
  180.      */  
  181.     private void nodeAddToLinkedList(TreeNode node,  
  182.             HashMap<String, ArrayList<TreeNode>> linkedList) {  
  183.         String name = node.getName();  
  184.         ArrayList<TreeNode> list;  
  185.   
  186.         if (linkedList.containsKey(name)) {  
  187.             list = linkedList.get(name);  
  188.             // 将node添加到此队列中  
  189.             list.add(node);  
  190.         } else {  
  191.             list = new ArrayList<>();  
  192.             list.add(node);  
  193.             linkedList.put(name, list);  
  194.         }  
  195.     }  
  196.   
  197.     /** 
  198.      * 根据链表构造出新的事务 
  199.      *  
  200.      * @param name 
  201.      *            节点名称 
  202.      * @param linkedList 
  203.      *            链表 
  204.      * @return 
  205.      */  
  206.     private ArrayList<ArrayList<TreeNode>> getTransactionList(String name,  
  207.             HashMap<String, ArrayList<TreeNode>> linkedList) {  
  208.         ArrayList<ArrayList<TreeNode>> tList = new ArrayList<>();  
  209.         ArrayList<TreeNode> targetNode = linkedList.get(name);  
  210.         ArrayList<TreeNode> singleTansaction;  
  211.         TreeNode temp;  
  212.   
  213.         for (TreeNode node : targetNode) {  
  214.             singleTansaction = new ArrayList<>();  
  215.   
  216.             temp = node;  
  217.             while (temp.getParentNode().getName() != null) {  
  218.                 temp = temp.getParentNode();  
  219.                 singleTansaction.add(new TreeNode(temp.getName(), 1));  
  220.             }  
  221.   
  222.             // 按照支持度计数得反转一下  
  223.             Collections.reverse(singleTansaction);  
  224.   
  225.             for (TreeNode node2 : singleTansaction) {  
  226.                 // 支持度计数调成与模式后缀一样  
  227.                 node2.setCount(node.getCount());  
  228.             }  
  229.   
  230.             if (singleTansaction.size() > 0) {  
  231.                 tList.add(singleTansaction);  
  232.             }  
  233.         }  
  234.   
  235.         return tList;  
  236.     }  
  237.   
  238.     /** 
  239.      * 节点计数 
  240.      *  
  241.      * @param node 
  242.      *            待加入节点 
  243.      * @param nodeCount 
  244.      *            计数映射图 
  245.      */  
  246.     private void nodeCounted(TreeNode node, HashMap<String, Integer> nodeCount) {  
  247.         int count = 0;  
  248.         String name = node.getName();  
  249.   
  250.         if (nodeCount.containsKey(name)) {  
  251.             count = nodeCount.get(name);  
  252.             count++;  
  253.         } else {  
  254.             count = 1;  
  255.         }  
  256.   
  257.         nodeCount.put(name, count);  
  258.     }  
  259.   
  260.     /** 
  261.      * 显示决策树 
  262.      *  
  263.      * @param node 
  264.      *            待显示的节点 
  265.      * @param blankNum 
  266.      *            行空格符,用于显示树型结构 
  267.      */  
  268.     private void showFPTree(TreeNode node, int blankNum) {  
  269.         System.out.println();  
  270.         for (int i = 0; i < blankNum; i++) {  
  271.             System.out.print("\t");  
  272.         }  
  273.         System.out.print("--");  
  274.         System.out.print("--");  
  275.   
  276.         if (node.getChildNodes() == null) {  
  277.             System.out.print("[");  
  278.             System.out.print("I" + node.getName() + ":" + node.getCount());  
  279.             System.out.print("]");  
  280.         } else {  
  281.             // 递归显示子节点  
  282.             // System.out.print("【" + node.getName() + "】");  
  283.             for (TreeNode childNode : node.getChildNodes()) {  
  284.                 showFPTree(childNode, 2 * blankNum);  
  285.             }  
  286.         }  
  287.   
  288.     }  
  289.   
  290.     /** 
  291.      * 待插入节点的抵达位置节点,从根节点开始向下寻找待插入节点的位置 
  292.      *  
  293.      * @param root 
  294.      * @param list 
  295.      * @return 
  296.      */  
  297.     private TreeNode searchNode(TreeNode node, ArrayList<TreeNode> list) {  
  298.         ArrayList<TreeNode> pathList = new ArrayList<>();  
  299.         TreeNode tempNode = null;  
  300.         TreeNode firstNode = list.get(0);  
  301.         boolean isExist = false;  
  302.         // 重新转一遍,避免出现同一引用  
  303.         for (TreeNode node2 : list) {  
  304.             pathList.add(node2);  
  305.         }  
  306.   
  307.         // 如果没有孩子节点,则直接返回,在此节点下添加子节点  
  308.         if (node.getChildNodes() == null) {  
  309.             return node;  
  310.         }  
  311.   
  312.         for (TreeNode n : node.getChildNodes()) {  
  313.             if (n.getName().equals(firstNode.getName()) && list.size() == 1) {  
  314.                 tempNode = node;  
  315.                 isExist = true;  
  316.                 break;  
  317.             } else if (n.getName().equals(firstNode.getName())) {  
  318.                 // 还没有找到最后的位置,继续找  
  319.                 pathList.remove(firstNode);  
  320.                 tempNode = searchNode(n, pathList);  
  321.                 return tempNode;  
  322.             }  
  323.         }  
  324.   
  325.         // 如果没有找到,则新添加到孩子节点中  
  326.         if (!isExist) {  
  327.             tempNode = node;  
  328.         }  
  329.   
  330.         return tempNode;  
  331.     }  
  332.   
  333.     /** 
  334.      * 判断目前构造的FP树是否是单条路径的 
  335.      *  
  336.      * @param rootNode 
  337.      *            当前FP树的根节点 
  338.      * @return 
  339.      */  
  340.     private boolean isSinglePath(TreeNode rootNode) {  
  341.         // 默认是单条路径  
  342.         boolean isSinglePath = true;  
  343.         ArrayList<TreeNode> childList;  
  344.         TreeNode node;  
  345.         node = rootNode;  
  346.   
  347.         while (node.getChildNodes() != null) {  
  348.             childList = node.getChildNodes();  
  349.             if (childList.size() == 1) {  
  350.                 node = childList.get(0);  
  351.             } else {  
  352.                 isSinglePath = false;  
  353.                 break;  
  354.             }  
  355.         }  
  356.   
  357.         return isSinglePath;  
  358.     }  
  359.   
  360.     /** 
  361.      * 开始构建FP树 
  362.      */  
  363.     public void startBuildingTree() {  
  364.         ArrayList<TreeNode> singleTransaction;  
  365.         ArrayList<ArrayList<TreeNode>> transactionList = new ArrayList<>();  
  366.         TreeNode tempNode;  
  367.         int count = 0;  
  368.   
  369.         for (String[] idArray : totalGoodsID) {  
  370.             singleTransaction = new ArrayList<>();  
  371.             for (String id : idArray) {  
  372.                 count = itemCountMap.get(id);  
  373.                 tempNode = new TreeNode(id, count);  
  374.                 singleTransaction.add(tempNode);  
  375.             }  
  376.   
  377.             // 根据支持度数的多少进行排序  
  378.             Collections.sort(singleTransaction);  
  379.             for (TreeNode node : singleTransaction) {  
  380.                 // 支持度计数重新归为1  
  381.                 node.setCount(1);  
  382.             }  
  383.             transactionList.add(singleTransaction);  
  384.         }  
  385.   
  386.         buildFPTree(null, transactionList);  
  387.     }  
  388.   
  389.     /** 
  390.      * 输出此单条路径下的频繁模式 
  391.      *  
  392.      * @param suffixPattern 
  393.      *            后缀模式 
  394.      * @param rootNode 
  395.      *            单条路径FP树根节点 
  396.      */  
  397.     private void printFrequentPattern(ArrayList<String> suffixPattern,  
  398.             TreeNode rootNode) {  
  399.         ArrayList<String> idArray = new ArrayList<>();  
  400.         TreeNode temp;  
  401.         temp = rootNode;  
  402.         // 用于输出组合模式  
  403.         int length = 0;  
  404.         int num = 0;  
  405.         int[] binaryArray;  
  406.   
  407.         while (temp.getChildNodes() != null) {  
  408.             temp = temp.getChildNodes().get(0);  
  409.   
  410.             // 筛选支持度系数大于最小阈值的值  
  411.             if (temp.getCount() >= minSupportCount) {  
  412.                 idArray.add(temp.getName());  
  413.             }  
  414.         }  
  415.   
  416.         length = idArray.size();  
  417.         num = (int) Math.pow(2, length);  
  418.         for (int i = 0; i < num; i++) {  
  419.             binaryArray = new int[length];  
  420.             numToBinaryArray(binaryArray, i);  
  421.   
  422.             // 如果后缀模式只有1个,不能输出自身  
  423.             if (suffixPattern.size() == 1 && i == 0) {  
  424.                 continue;  
  425.             }  
  426.   
  427.             System.out.print("频繁模式:{【后缀模式:");  
  428.             // 先输出固有的后缀模式  
  429.             if (suffixPattern.size() > 1  
  430.                     || (suffixPattern.size() == 1 && idArray.size() > 0)) {  
  431.                 for (String s : suffixPattern) {  
  432.                     System.out.print(s + ", ");  
  433.                 }  
  434.             }  
  435.             System.out.print("】");  
  436.             // 输出路径上的组合模式  
  437.             for (int j = 0; j < length; j++) {  
  438.                 if (binaryArray[j] == 1) {  
  439.                     System.out.print(idArray.get(j) + ", ");  
  440.                 }  
  441.             }  
  442.             System.out.println("}");  
  443.         }  
  444.     }  
  445.   
  446.     /** 
  447.      * 数字转为二进制形式 
  448.      *  
  449.      * @param binaryArray 
  450.      *            转化后的二进制数组形式 
  451.      * @param num 
  452.      *            待转化数字 
  453.      */  
  454.     private void numToBinaryArray(int[] binaryArray, int num) {  
  455.         int index = 0;  
  456.         while (num != 0) {  
  457.             binaryArray[index] = num % 2;  
  458.             index++;  
  459.             num /= 2;  
  460.         }  
  461.     }  
  462.   
  463. }  
算法调用测试类:

[java] view plain copy
 print?
  1. /** 
  2.  * FPTree频繁模式树算法 
  3.  * @author lyq 
  4.  * 
  5.  */  
  6. public class Client {  
  7.     public static void main(String[] args){  
  8.         String filePath = "C:\\Users\\lyq\\Desktop\\icon\\testInput.txt";  
  9.         //最小支持度阈值  
  10.         int minSupportCount = 2;  
  11.           
  12.         FPTreeTool tool = new FPTreeTool(filePath, minSupportCount);  
  13.         tool.startBuildingTree();  
  14.     }  
  15. }  
输出的结果为:

[java] view plain copy
 print?
  1. [后缀模式]:{3, }, 此时的条件模式基:{2, }, {1, }, {21, },   
  2. [后缀模式]:{32, }, 此时的条件模式基:  
  3. 频繁模式:{【后缀模式:32, 】}  
  4. -------  
  5. [后缀模式]:{31, }, 此时的条件模式基:{2, },   
  6. 频繁模式:{【后缀模式:31, 】}  
  7. 频繁模式:{【后缀模式:31, 】2, }  
  8. -------  
  9. [后缀模式]:{2, }, 此时的条件模式基:  
  10. -------  
  11. [后缀模式]:{1, }, 此时的条件模式基:{2, },   
  12. 频繁模式:{【后缀模式:1, 】2, }  
  13. -------  
  14. [后缀模式]:{5, }, 此时的条件模式基:{21, }, {213, },   
  15. 频繁模式:{【后缀模式:5, 】2, }  
  16. 频繁模式:{【后缀模式:5, 】1, }  
  17. 频繁模式:{【后缀模式:5, 】21, }  
  18. -------  
  19. [后缀模式]:{4, }, 此时的条件模式基:{2, }, {21, },   
  20. 频繁模式:{【后缀模式:4, 】2, }  
  21. -------  
读者可以自己手动的构造一下,可以更深的理解这个过程,然后对照本人的代码做对比。

算法编码时的难点

1、在构造树的时候要重新构建一棵树的时候,要不能对原来的树做更改,在此期间用了老的树的对象,又造成了重复引用的问题了,于是果断又new了一个TreeNode,只把原树的name,和count值拿了过来,父子节点关系完全重新构造。

2、在事务生产树的过程中,把事务映射到TreeNode数组中,然后过程就是加Node节点或者更新Node节点的count值,过程简单许多,也许会让人很难理解,应该个人感觉这样比较方便,如果是死板的String[]字符串数组的形式,中间还要与TreeNode各种转化非常麻烦。

3、在计算条件模式基的时候,我是存在了HashMap<String, ArrayList<TreeNode>>map中,并并没有搞成链表的形式,直接在生成树的时候就全部统计好。

4、此处算法用了2处递归,一个地方是在添加树节点的时候,搜索要在哪个node上做添加的方法,searchNode(TreeNode node, ArrayList<TreeNode> list),还有一个是整个的buildFPTree()算法,都不是能够一眼就能看明白的地方。希望大家能够理解我的用意。

FP-Tree算法的缺点

尽管FP-Tree算法在挖掘频繁模式的过程中相较Apriori算法里没有产生候选集了,比Apriori也快了一个数量级上了,但是整体上FP-Tree算法的时间,空间消耗开销上还是挺大的。

0 0