将集合映射到所有组合的列表中

时间:2011-11-17 20:31:05

标签: java algorithm recursion

我遇到了一个简单的任务。我想要做的是将Map<K,Set<V>>转换为List<Map<K,V>>获取所有可能的组合:

Map {
  {'k1' => set{'v11', 'v12'}},
  {'k2' => set{'v21', 'v22', 'v23'}},
  {'k3' => set{'v31'}}
}

预期结果:

List
{
  Map{'k1'=>'v11', 'k2'=>'v21', 'k3'=>'v31'}, 
  Map{'k1'=>'v11', 'k2'=>'v22', 'k3'=>'v31'},  
  Map{'k1'=>'v11', 'k2'=>'v23', 'k3'=>'v31'}, 
  Map{'k1'=>'v12', 'k2'=>'v21', 'k3'=>'v31'}, 
  Map{'k1'=>'v12', 'k2'=>'v22', 'k3'=>'v31'}, 
  Map{'k1'=>'v12', 'k2'=>'v23', 'k3'=>'v31'}
}

对我感到羞耻,需要你的帮助。

为downvoters更新

我不是一个要求社区为我做功课的学生,我不会问在经过一周的努力之后我是否真的不需要帮助。好吧,原则上我会在符合条件的情况下立即为此问题分配全部代表赏金(800左右)。

更新2

在我之前的更新中尊重我的承诺,我开始了两个赏金的序列,其中最大的一个归于我的救世主,剩下的就是对其他可能的解决方案的奖励。

更新3

这是技术性的,交付承诺的消息(请参阅之前的更新)。我要感谢所有SO社区在真正需要时提供帮助。非常特别感谢“Tudor”和“Ed Staub”及时参与和响应。第二个赏金将分配给最优雅的解决方案,为新人和每个感兴趣的人提供良好的机会。

6 个答案:

答案 0 :(得分:6)

使用递归!因此,在递归的每个级别,您都会查看地图keyset()中的另一个键。您可以在该Set<V>的迭代添加元素中添加要添加到列表中的当前地图。

你可以把它想象成一棵树。在根节点,您有一个空列表。然后树i的每个后续级别表示从i集合中选择哪个元素。

以下是代码以及包含测试用例的主要方法:

import java.util.*;

public class Test {
    // method called to generate combinations using map, putting the combinations in list
    public static <K,V> void combinations( Map<K,Set<V>> map, List<Map<K,V>> list ) {
        recurse( map, new LinkedList<K>( map.keySet() ).listIterator(), new HashMap<K,V>(), list );
    }

    // helper method to do the recursion
    private static <K,V> void recurse( Map<K,Set<V>> map, ListIterator<K> iter, Map<K,V> cur, List<Map<K,V>> list ) {
            // we're at a leaf node in the recursion tree, add solution to list
        if( !iter.hasNext() ) {
            Map<K,V> entry = new HashMap<K,V>();

            for( K key : cur.keySet() ) {
                entry.put( key, cur.get( key ) );
            }

            list.add( entry );
        } else {
            K key = iter.next();
            Set<V> set = map.get( key );

            for( V value : set ) {
                cur.put( key, value );
                recurse( map, iter, cur, list );
                cur.remove( key );
            }

            iter.previous();
        }
    }

    public static void main( String[] args ) {
        Map<Integer,Set<Integer>> map = new HashMap<Integer,Set<Integer>>() {{
            put( 1, new HashSet<Integer>() {{
                add( 11 );
                add( 12 );
            }} );
            put( 2, new HashSet<Integer>() {{
                add( 21 );
                add( 22 );
                add( 23 );
            }} );
            put( 3, new HashSet<Integer>() {{
                add( 31 );
            }} );
        }};
        List<Map<Integer,Integer>> list = new LinkedList<Map<Integer,Integer>>();
        combinations( map, list );

        for( Map<Integer,Integer> combination : list ) {
            System.out.println( combination );
        }
    }
}

答案 1 :(得分:2)

提示:使用递归生成组合“树”。

编辑:好的,我有空闲时间,并决定试一试:

/**
 * 
 * @param <K> The type of the key
 * @param <V> The type of the value
 * @param index The index of the current key to inspect
 * @param current The current map being built by recursion
 * @param map The original input
 * @param list The result
 */ 
public static <K, V> void Combine(int index, Map<K, V> current, 
                                  Map<K, Set<V>> map, 
                                  List<Map<K, V>> list) {

    if(index == map.size()) { // if we have gone through all keys in the map
        Map<K, V> newMap = new HashMap<K, V>();
        System.out.println(current);
        for(K key: current.keySet()) {          // copy contents to new map.    
            newMap.put(key, current.get((K)key));               
        }           
        list.add(newMap); // add to result.
    } else {
        Object currentKey = map.keySet().toArray()[index]; // take the current key
        for(V value: map.get(currentKey)) {
            current.put((K)currentKey, value); // put each value into the temporary map
            Combine(index + 1, current, map, list); // recursive call
            current.remove(currentKey); // discard and try a new value
        }
    }
}

我已经测试了几个案例,我认为这是正确的。让我知道。 您可以从仅采用map作为输入的其他方法调用此方法,创建默认参数indexcurrentlist并返回list作为输出。< / p>

答案 2 :(得分:1)

非常更新。

这是Guava头的解决方案。如果番石榴不是你的东西,它应该很容易转换。这个不是递归的。它输出一对字符串对的列表,我认为这是你真正想要的。

static ImmutableMap<String,ImmutableSet<String>> input = ImmutableMap.of(
    "k1", ImmutableSet.of("v11", "v12"),
    "k2", ImmutableSet.of("v21", "v22", "v23"),
    "k3", ImmutableSet.of("v31", "v32", "v33", "v34"));

static class Pair<T,U>
{
    T left;        U right;
    static <T,U> Pair of(T l, U r)
    {   return new Pair(l, r);      }
    Pair(T l, U r)    { left = l; right = r; }
}

static List<Pair<Pair<String, String>,Pair<String, String>>> transform(Map<String,ImmutableSet<String>> input)
{
    List<Pair<Pair<String, String>,Pair<String, String>>> output = Lists.newArrayList();
    // Not a real copy - very cheap.
    ImmutableList<Map.Entry<String, ImmutableSet<String>>> entryList = ImmutableList.copyOf(input.entrySet());
    for ( int i = 0; i < entryList.size(); i++ )
    {
        final Map.Entry<String, ? extends Set<String>> leftEntry = entryList.get(i);
        for ( int j = i+1; j < entryList.size(); j++ )
        {
            final Map.Entry<String, ? extends Set<String>> rightEntry = entryList.get(j);
            for ( String v1 : leftEntry.getValue() )
                for ( String v2 : rightEntry.getValue() )
                    output.add(Pair.of(
                        Pair.of(leftEntry.getKey(), v1),
                        Pair.of(rightEntry.getKey(), v2)));
        }

    }
    return output;
}

答案 3 :(得分:0)

首先感谢您提供的案例和提供的实施!我们遇到了同样的问题,为我们节省了大量时间!我们留下了一个python实现,对于那些可能用另一种语言来处理相同问题的人:)

def combinations(themap, maplist):
    return recurse(themap, themap.keys(), {}, maplist, 0)

def recurse(themap, iterator, curmap, maplist, iteratoridx):
    if(iteratoridx>=len(iterator)):
        entry = {}
        for key in curmap.keys():
            entry[key]=curmap[key]
        maplist.append(entry)
    else:
        key = iterator[iteratoridx]
        theset = themap.get(key)
        for value in theset:
            curmap[key]=value
            recurse(themap, iterator, curmap, maplist, iteratoridx+1)
            curmap.pop(key, None)
        iteratoridx=iteratoridx-1


maplist=[]
grid={"A":[1, 2, 3], "B":[4, 5, 6], "C":[7, 8, 9]}
combinations(grid, maplist)
print(maplist)

答案 4 :(得分:-1)

假设我们有变量&#34; source&#34;从类型Map<K,Set<V>>将源转换为所需类型,您可以使用下一个代码:

List<Map<K,V>> target = new ArrayList<Map<K,V>>();
for(K key : source.keySet()){
    Map<K,V> map = new HashMap<K, V>();
    for(V val : source.get(key)){
       map.put(key, val)
    }
    target.add(map);
}

答案 5 :(得分:-1)

List<Map<K,V>> target = new ArrayList<Map<K,V>>();
for(K key : source.keySet()){
    for(V val : source.get(key)){
       Map<K,V> map = new HashMap<K, V>();
       map.put(key, val)
       target.add(map);
    }
}