题
我想找到一组整数的子集。这是带有回溯的“子集和”算法的第一步。我编写了以下代码,但它没有返回正确的答案:
BTSum(0, nums);
///**************
ArrayList<Integer> list = new ArrayList<Integer>();
public static ArrayList<Integer> BTSum(int n, ArrayList<Integer> numbers) {
if (n == numbers.size()) {
for (Integer integer : list) {
System.out.print(integer+", ");
}
System.out.println("********************");
list.removeAll(list);
System.out.println();
} else {
for (int i = n; i < numbers.size(); i++) {
if (i == numbers.size() - 1) {
list.add(numbers.get(i));
BTSum(i + 1, numbers);
} else {
list.add(numbers.get(i));
for (int j = i+1; j < numbers.size(); j++)
BTSum(j, numbers);
}
}
}
return null;
}
例如,如果我想计算set = {1、3、5}的子集,我的方法的结果是:
1, 3, 5, ********************
5, ********************
3, 5, ********************
5, ********************
3, 5, ********************
5, ********************
我希望它产生:
1, 3, 5
1, 5
3, 5
5
我认为问题是来自零件列表。removeAll(list);但我不知道如何纠正它。
解决方案
您想要的称为 Powerset. 。这是它的简单实现:
public static Set<Set<Integer>> powerSet(Set<Integer> originalSet) {
Set<Set<Integer>> sets = new HashSet<Set<Integer>>();
if (originalSet.isEmpty()) {
sets.add(new HashSet<Integer>());
return sets;
}
List<Integer> list = new ArrayList<Integer>(originalSet);
Integer head = list.get(0);
Set<Integer> rest = new HashSet<Integer>(list.subList(1, list.size()));
for (Set<Integer> set : powerSet(rest)) {
Set<Integer> newSet = new HashSet<Integer>();
newSet.add(head);
newSet.addAll(set);
sets.add(newSet);
sets.add(set);
}
return sets;
}
我将举一个例子来解释该算法如何为 {1, 2, 3}
:
- 消除
{1}
, 并执行Powerset{2, 3}
;- 消除
{2}
, 并执行Powerset{3}
;- 消除
{3}
, 并执行Powerset{}
;- powerset
{}
是{{}}
;
- powerset
- powerset
{3}
是3
结合{{}}
={ {}, {3} }
;
- 消除
- powerset
{2, 3}
是{2}
结合{ {}, {3} }
={ {}, {3}, {2}, {2, 3} }
;
- 消除
- powerset
{1, 2, 3}
是{1}
结合{ {}, {3}, {2}, {2, 3} }
={ {}, {3}, {2}, {2, 3}, {1}, {3, 1}, {2, 1}, {2, 3, 1} }
.
其他提示
只是底漆 可以 解决这个问题:
方法1
- 获取您的号码列表的第一个元素
- 产生 全部 剩余数字列表的子集(即没有选择的数字列表)=>递归!
- 对于上一个步骤中找到的每个子集,将子集本身添加和与步骤1中选择的元素连接到输出。
当然,您必须检查基本案例,即如果您的数字列表为空。
方法2
众所周知的事实是 n
元素有 2^n
子集。因此,您可以从中算上二进制 0
至 2^n
并将二进制数解释为相应的子集。请注意,此方法需要具有足够数量的二进制数来表示整个集合。
将两种方法之一转换为代码,应该不是一个太大的问题。
您的代码确实令人困惑,并且没有解释。
您可以使用BitMask进行迭代进行迭代,以确定集合中的哪个数字。从0到2^n的每个数字在其二进制表示中给出了一个唯一的子集,例如
n = 3:
i = 5-> 101在二进制中,选择第一个和最后一个元素i = 7-> 111在二进制中,选择第一个3个元素
假设有n个元素(n <64,毕竟n是否大于64,您将永远运行)。
for(long i = 0; i < (1<<n); i++){
ArrayList<Integer> subset = new ArrayList<Integer>();
for(int j = 0; j < n; j++){
if((i>>j) & 1) == 1){ // bit j is on
subset.add(numbers.get(j));
}
}
// print subset
}
考虑到这个问题(感谢Google)(感谢Google) - 像我这样的
这是一个递归解决方案,可用于简单主体:
set = {a,b,c,d,e}
然后我们可以将其分解为 {a}
+ Subset of {b,c,d,e}
public class Powerset{
String str = "abcd"; //our string
public static void main(String []args){
Powerset ps = new Powerset();
for(int i = 0; i< ps.str.length();i++){ //traverse through all characters
ps.subs("",i);
}
}
void subs(String substr,int index)
{
String s = ""+str.charAt(index); //very important, create a variable on each stack
s = substr+s; //append the subset so far
System.out.println(s); //print
for(int i=index+1;i<str.length();i++)
subs(s,i); //call recursively
}
}
输出
a
ab
abc
abcd
abd
ac
acd
ad
b
bc
bcd
bd
c
cd
d
显然,任何给定集的子集总数等于2^(集合中的元素数)。如果设置
a = {1,2,3}
然后A的子集IS:
{ }, { 1 }, { 2 }, { 3 }, { 1, 2 }, { 1, 3 }, { 2, 3 }, { 1, 2, 3 }
如果我们看起来像二进制数字。
{ 000 }, { 001 }, { 010 }, { 011 }, { 100 }, { 101 }, { 110 }, { 111 }
如果我们考虑上述:
static void subSet(char[] set) {
int c = set.length;
for (int i = 0; i < (1 << c); i++) {
System.out.print("{");
for (int j = 0; j < c; j++) {
if ((i & (1 << j)) > 0) {
System.out.print(set[j] + " ");
}
}
System.out.println("}");
}
}
public static void main(String[] args) {
char c[] = {'a', 'b', 'c'};
subSet(c);
}
private static void findSubsets(int array[])
{
int numOfSubsets = 1 << array.length;
for(int i = 0; i < numOfSubsets; i++)
{
int pos = array.length - 1;
int bitmask = i;
System.out.print("{");
while(bitmask > 0)
{
if((bitmask & 1) == 1)
System.out.print(array[pos]+",");
bitmask >>= 1;
pos--;
}
System.out.print("}");
}
}
根据我今天学到的知识,这是基于的Java解决方案 recursion
public class Powerset {
public static void main(String[] args) {
final List<List<String>> allSubsets = powerSet(Arrays.asList(1, 2, 3, 4), 0);
for (List<String> subsets : allSubsets) {
System.out.println(subsets);
}
}
private static List<List<String>> powerSet(final List<Integer> values,
int index) {
if (index == values.size()) {
return new ArrayList<>();
}
int val = values.get(index);
List<List<String>> subset = powerSet(values, index + 1);
List<List<String>> returnList = new ArrayList<>();
returnList.add(Arrays.asList(String.valueOf(val)));
returnList.addAll(subset);
for (final List<String> subsetValues : subset) {
for (final String subsetValue : subsetValues) {
returnList.add(Arrays.asList(val + "," + subsetValue));
}
}
return returnList;
}
}
运行它将给出结果
[1]
[2]
[3]
[4]
[3,4]
[2,3]
[2,4]
[2,3,4]
[1,2]
[1,3]
[1,4]
[1,3,4]
[1,2,3]
[1,2,4]
[1,2,3,4]
我实际上试图解决这个问题,并在上一篇文章中得到了算法@phimuemue。这是我实现的。希望这有效。
/**
*@Sherin Syriac
*
*/
import java.util.ArrayList;
import java.util.List;
public class SubSet {
ArrayList<List<Integer>> allSubset = new ArrayList<List<Integer>>();
/**
* @param args
*/
public static void main(String[] args) {
SubSet subSet = new SubSet();
ArrayList<Integer> set = new ArrayList<Integer>();
set.add(1);
set.add(2);
set.add(3);
set.add(4);
subSet.getSubSet(set, 0);
for (List<Integer> list : subSet.allSubset) {
System.out.print("{");
for (Integer element : list) {
System.out.print(element);
}
System.out.println("}");
}
}
public void getSubSet(ArrayList<Integer> set, int index) {
if (set.size() == index) {
ArrayList<Integer> temp = new ArrayList<Integer>();
allSubset.add(temp);
} else {
getSubSet(set, index + 1);
ArrayList<List<Integer>> tempAllSubsets = new ArrayList<List<Integer>>();
for (List subset : allSubset) {
ArrayList<Integer> newList = new ArrayList<Integer>();
newList.addAll(subset);
newList.add(set.get(index));
tempAllSubsets.add(newList);
}
allSubset.addAll(tempAllSubsets);
}
}
}
// subsets for the set of 5,9,8
import java.util.ArrayList;
import java.util.List;
public class Subset {
public static void main(String[] args) {
List<Integer> s = new ArrayList<Integer>();
s.add(9);
s.add(5);
s.add(8);
int setSize = s.size();
int finalValue = (int) (Math.pow(2, setSize));
String bValue = "";
for (int i = 0; i < finalValue; i++) {
bValue = Integer.toBinaryString(i);
int bValueSize = bValue.length();
for (int k = 0; k < (setSize - bValueSize); k++) {
bValue = "0" + bValue;
}
System.out.print("{ ");
for (int j = 0; j < setSize; j++) {
if (bValue.charAt(j) == '1') {
System.out.print((s.get(j)) + " ");
}
}
System.out.print("} ");
}
}
}
//Output : { } { 8 } { 5 } { 5 8 } { 9 } { 9 8 } { 9 5 } { 9 5 8 }
public static ArrayList<ArrayList<Integer>> powerSet(List<Integer> intList) {
ArrayList<ArrayList<Integer>> result = new ArrayList<ArrayList<Integer>>();
result.add(new ArrayList<Integer>());
for (int i : intList) {
ArrayList<ArrayList<Integer>> temp = new ArrayList<ArrayList<Integer>>();
for (ArrayList<Integer> innerList : result) {
innerList = new ArrayList<Integer>(innerList);
innerList.add(i);
temp.add(innerList);
}
result.addAll(temp);
}
return result;
}
如果您要处理大量元素,则(尽管不太可能)遇到堆栈溢出问题。我承认,在溢出堆栈之前,您更有可能用完记忆,但是无论如何我都会在这里放置这种非收回方法。
public static final <T> Set<Set<T>> powerSet(final Iterable<T> original) {
Set<Set<T>> sets = new HashSet<>();
sets.add(new HashSet<>());
for (final T value : original) {
final Set<Set<T>> newSets = new HashSet<>(sets);
for (final Set<T> set : sets) {
final Set<T> newSet = new HashSet<>(set);
newSet.add(value);
newSets.add(newSet);
}
sets = newSets;
}
return sets;
}
或者,如果您想处理数组:
@SuppressWarnings("unchecked")
public static final <T> T[][] powerSet(final T... original) {
T[][] sets = (T[][]) Array.newInstance(original.getClass(), 1);
sets[0] = Arrays.copyOf(original, 0);
for (final T value : original) {
final int oldLength = sets.length;
sets = Arrays.copyOf(sets, oldLength * 2);
for (int i = 0; i < oldLength; i++) {
final T[] oldArray = sets[i];
final T[] newArray = Arrays.copyOf(oldArray, oldArray.length + 1);
newArray[oldArray.length] = value;
sets[i + oldLength] = newArray;
}
}
return sets;
}
这是一些伪代码。您可以通过在递归呼叫之前存储每个调用的值来剪切相同的递归调用,以检查是否已经存在呼叫值。
以下算法将使所有子集不包括空集。
list * subsets(string s, list * v){
if(s.length() == 1){
list.add(s);
return v;
}
else
{
list * temp = subsets(s[1 to length-1], v);
int length = temp->size();
for(int i=0;i<length;i++){
temp.add(s[0]+temp[i]);
}
list.add(s[0]);
return temp;
}
}
这是打印给定数字集的所有子集的逻辑。这也称为集合的powerset。我已经使用了一种简单的递归方法来使用Java解决此问题,但是您也可以用其他语言代码。
import java.util.Scanner;
public class PowerSubset {
public static void main(String[] args) {
// HardCoded Input
int arr[] = { 1, 2, 3 };//original array whose subset is to be found
int n=3; //size of array
// Dynamic Input
/*Scanner sc = new Scanner(System.in);
int n = sc.nextInt();
int arr[] = new int[n];
for (int i = 0; i < n; i++) {
arr[i] = sc.nextInt();
}*/
int data[] = new int[arr.length]; // temporary array
printSubset(arr, data, n, 0, 0);
}
public static void printSubset(int arr[], int data[], int n, int dataIndex, int arrIndex) {
if (arrIndex == n) { //comparing with n since now you are at the leaf node
System.out.print("[");//watch pictorial chart in the below video link
for (int j = 0; j < n; j++) {
System.out.print(data[j] == 0 ? "" : data[j]);
}
System.out.print("]");
System.out.println();
return;
}
data[dataIndex] = arr[arrIndex];
printSubset(arr, data, n, dataIndex + 1, arrIndex + 1);//recursive call 1
data[dataIndex] = 0;
printSubset(arr, data, n, dataIndex, arrIndex + 1);//recursive call 2
}
}
上述代码的输出:
[123]
[12]
[13]
[1]
[23]
[2]
[3]
[]
为了获得该概念,您可以观看以下YouTube视频,该视频清楚地说明了代码背后使用的方法。https://www.youtube.com/watch?v=vel15c3vbve
简单的Java递归解决方案 -
private static List<List<Integer>> allsubSet(List<Integer> integers, int start, int end) {
//Base case if there is only one element so there would be two subset
// empty list and that element
if(start == end) {
List<List<Integer>> result = new ArrayList<>();
List<Integer> emptyList = new ArrayList<>();
result.add(emptyList);
List<Integer> element = new ArrayList<>();
element.add(integers.get(start));
result.add(element );
return result;
}
//I know if by recursion we can expect that we'll get the n-1 correct result
List<List<Integer>> lists = allsubSet(integers, start, end-1);
//here i copy all the n-1 results and just added the nth element in expected results
List<List<Integer>> copyList = new ArrayList<>(lists);
for (List<Integer> list : lists) {
List<Integer> copy= new ArrayList<>(list);
copy.add(integers.get(end));
copyList.add(copy);
}
return copyList;
}
为避免冗余,我们可以简单地使用设置代替列表