减一技术应用:生成排列与幂集(Java实现)

来源:互联网 发布:湖北网络安全教育 编辑:程序博客网 时间:2024/05/22 06:36


        减一技术,与二分搜索一样,是一种通用算法设计技术。它是分治法的一种特殊形式,通过建立问题实例P(n) 与问题实例P(n-1)的递推求解关系式而实现;最经典的例子莫过于插入排序了。这里,给出减一技术在生成排列组合方面的应用。

 

(一)  排列问题: 生成自然数 1,2,,,,,n 的所有排列。

 

算法描述:

        使用减一技术,建立自然数12...n的排列与12...n-1的递推关系。假设 P(n-1) 是 自然数 12...n-1的所有排列 p1, p2,..., p(m)的集合,则P(n)通过如下方式得到: 对所有排列p1, p2, ... , p(m) , 将 n 插入到 这些排列的 n 个位置上,得到的所有排列。 例如 12的排列为 12, 21, 则123的排列通过如下方式得到: 1. 将3插入到12中,得到 312,132,123;2. 将3 插入到21中,得到 321, 231, 213. 通过小例子往往能够为问题的求解指明道路。

     “最小变化”要求: 有时,要求生成的所有排列中,相邻排列只有两个相邻位置不同。比如1234和1324是满足的,而1234和1432则不满足。上述方法生成的排列 312, 132,123, 321, 231,213 , 123和321就不满足这个要求。解决方案是,当对排列pi采用从左往右插入完成时,对相邻排列pi+1采用从右往左插入。比如1中将3插入排列12是从左往右插入;则2中应该将3从右往左插入,得到213,231,321,这样,与前面的312,132,123就满足最小变化要求了。

 

详细设计:

    1. 输入: 自然数 n

    2. 输出: 所有排列的集合,每个排列用一个链表来表示(考虑到插入操作); 所有排列用链表的可变列表(ArrayList)来表示。之所以采用这样的方式,考虑到之后可能要取出排列进行求解,比如分配问题。代价是空间效率很低。

    3. 数据结构: 使用队列来存储 n-1 的所有排列; 然后取出队列中的每个元素, 将 n 插入到其中,得到 n 的一个排列。

 

Java 代码实现:

 

(二) 生成给定集合 {1,2,...,n} 的幂集,即给定自然数3,要生成其幂集: { {0}, {1}, {2}, {1,2}, {3}, {1,3},{2,3},{1,2,3} }

算法描述:

      使用减一技术,建立问题实例P(n)与问题实例P(n-1)的递推求解关系。若P(n-1)是问题实例n-1的幂集,则问题实例n的幂集通过如下方式得到: powerset(n) = powerset(n-1) + powerset(n-1)∪ {n} ,即,对于n-1的幂集的每一个集合与{n}求并集,然后将得到的集合与n-1的幂集求并集。例如 {1} 的幂集为 {{0}, {1}} ,则 {1,2} 的幂集为 { {2} {1,2}, {0}, {1} } ,其中 {0} 表示空集, {0} ∪ {n} = {n}

 

          详细设计:

1. 输入: 自然数 n

2. 输出: {1,2,..,n}的幂集,每一个子集使用一个LinkedList来表示,幂集使用LinkedList的可变列表ArrayList来表示和存储。


Java代码实现:

 

算法分析:

       减一技术的时间复杂度通常是: T(n) = T(n-1) + G(n) .

       ①  若 G(n) 为常数,则 T(n) = O(n) ;    (连续子数组的最大和)

       ②  若 G(n) 为 O(logn), 则 T(n) = O(nlogn) ;  (堆排序)

       ③   若 G(n) = an+b(a!=0) ,则 T(n) = O(n^2) .     (插入排序)

       因此, 当 G(n) 为常数或对数时, 减一技术是比较高效的。  


 

原创粉丝点击