小结构与小算法:利用树结构求集合的幂集

来源:互联网 发布:淘宝提醒买家付款 编辑:程序博客网 时间:2024/05/01 14:22

求集合的幂集,这个问题本身并不难理解,至少在写程序时我们可以很容易就想到穷举法,然后就算是用很暴力的心态很粗暴地去编码都能够写出一个能有漂亮输出结果的程序,但闲得蛋疼的我想试下能不能让穷举法不那么粗暴,然后这个世界上就诞生了一段代码几张稿纸和一篇令人蛋疼的CSDN博文。

在弄清问题的本质后接下来就要考虑如何用计算机世界的语言去描述问题对象本身,在这里的问题对象当然就是“集合”,我们要求的是集合的幂集,也就是一个由集合的全体子集构成的集合,所以我们希望这个数据结构既能描述集合本身又同时能将集合的所有子集表现出来,就算只是很隐含地表现。只要找到这么一个数据结构,那么就可以按某种算法遍历该结构并输出其中所有元素,最后神奇地发现它输出的刚好就是集合的幂集。

数据结构很简单,是Tree

算法也很简单:树结构某种遍历算法

首先假设我们遭遇了这么一个集合{0,1,2,3},完了它说要是我们不把它的子集求出来它就要对我们怎样怎样,好吧,只能怪我RP不好遇上这种事,还连累了大家。

我们先用树结构描述该集合以及它的一切子集:

0

1

┃┣23

┃┗3

23

3


上面那棵树我们不妨称它为"幂集树",下面给出幂集树的构造方法


给定如下集合{0,1,2,3},构造一棵幂集树。

1.根据集合元素的顺序从高到低为元素设置优先级,这里0的优先级最高,3的优先级最低。

2.按优先级从低到高的顺序构造幂集树,算法伪代码如下:

 

 for(e : set.iterator().end())//逆序遍历集合元素      if(e == 集合中优先级最低的元素){         e.createSubTree(); //将该元素单独作为一棵只有一个节点的子树     }      else{         e.createSubTree();//以该元素为根节点建立一棵子树         //按优先级从高到低的顺序遍历set中比元素e优先级低的元素        Iterator begin = set.iterator(e) + 1;//begin代表位于e相邻下一位的迭代器位置        Iterator end = set.iterator().end();//end代表集合中最后一个元素        for(e1 : set.iterator_From_To(begin,end)){            e.addSubTree(e1.getTreePointer());        }      }   }

   以上算法中,假设元素的数据类型有一个tree成员。

3.取集合中第一个 元素的tree数据成员,即可得该集合的幂集树。


这样的话集合就出来了,而且该集合的所有子集也出现在了里面,不多也不少,当然要让所有子集现身我们还需要一个算法运行于该数据结构之上

这个算法剽窃于树的后序遍历算法,其实这么说也不大严格,但至少在遍历路径上我觉得它和后序遍历算法最接近,附上代码:

private void getAllSubset(TreeNode head){    route.add(head);    for(TreeNode tn :route) System.out.print(tn.data + " ");    System.out.println();    for(TreeNode kid : head.children)       getAllSubset(kid);    route.pollLast();}


我把这个函数命名为getAllSubset,其中route是一个链表,记录遍历路径,head是遍历开始的父节点,基本思想如下:从父节点开始按后序遍历的路径进行遍历,每到一个节点就将其加入route,接下来输出route的内容,然后对该节点的所有子叶节点递归调用getAllSubset方法,最后将该节点从route中删除。不过这样还不是算法的全部,因为少了该方法的调用方式,那么继续附上代码:

for(int i = 0; i <subsetTree.size(); i++)       getAllSubset(subsetTree.get(i));

subsetTree就是上面的树结构,其实是只一个链表,只是通过在每个节点中添加指向同类型数据的引用,让这些引用指向subsetTree链表中的其它节点,从而在逻辑上将一个链表变成树结构,算法最顶层的思想就是:对subsetTree中的每一个元素调用getAllSubset,无论该元素是不是树的根节点,无论该元素是谁的子节点,无论该元素有无子节点,一视同仁地全部抓去当getAllSubset的参数。

然后输出如下:

0

0 1

0 1 2

0 1 2 3

0 1 3

0 2

0 2 3

0 3

1

1 2

1 2 3

1 3

2

2 3

3

集合{0,1,2,3}的所有子集都在上面,应该没错吧。什么,还少了一个空集?好吧好吧这个空集是皇帝的新空集,只有聪明人才看得到,如果你和我一样也看不到那我也木有办法,同病相怜吧。

以下全部源代码:

import java.util.LinkedList;public class GetSubset {    private LinkedList<TreeNode>subsetTree =new LinkedList<TreeNode>();    private LinkedList<TreeNode>route =new LinkedList<TreeNode>();    public GetSubset(LinkedList<Integer> set){       for(Integer data : set){           subsetTree.add(new TreeNode(data));       }       for(int i = 0; i <subsetTree.size(); i++){           int j = i;           while(++j <subsetTree.size()){              subsetTree.get(i).addChild(j);           }       }       for(int i = 0; i <subsetTree.size(); i++){           getAllSubset(subsetTree.get(i));       }    }      privatevoid getAllSubset(TreeNode head){       //System.out.println("[" + head.data + "]");       route.add(head);       for(TreeNode tn :route) System.out.print(tn.data +" ");       System.out.println();       for(TreeNode kid : head.children)           getAllSubset(kid);       route.pollLast();    }    privateclass TreeNode{       Integer data;       LinkedList<TreeNode> children;       TreeNode(Integer data){           this.data = data;           children =new LinkedList<TreeNode>();       }       void addChild(int i){           children.add(subsetTree.get(i));       }    }    publicstaticvoid main(String[] args){       LinkedList<Integer> set =new LinkedList<Integer>();       for(int i = 0; i < 18; i++){           set.add(new Integer(i));       }       GetSubset gss = new GetSubset(set);    }}


原创粉丝点击