Java 如果子集合共享公共值,则将子集合合并为更大的集合

Java 如果子集合共享公共值,则将子集合合并为更大的集合,java,set,java-8,java-stream,Java,Set,Java 8,Java Stream,我正在尝试创建一个方法,该方法将返回最大的一组有邻居的卡。例如,如果我有列表rankList=[FIVE,QUEEN,KING,ACE,TEN]它将返回[KING,ACE,QUEEN]。我还没有完全实现它,但是我得到了将一个集合拆分为多个较小集合的部分 private boolean isStraight(List<Card> cards) { Set<RANK> rankList = cards.stream() .map(Car

我正在尝试创建一个方法,该方法将返回最大的一组有邻居的卡。例如,如果我有
列表rankList=[FIVE,QUEEN,KING,ACE,TEN]
它将返回
[KING,ACE,QUEEN]
。我还没有完全实现它,但是我得到了将一个集合拆分为多个较小集合的部分

private boolean isStraight(List<Card> cards) {
        Set<RANK> rankList = cards.stream()
            .map(Card::getRank)
            .distinct()
            .collect(Collectors.toSet());


        List<Set<RANK>> subSets = new ArrayList<>();

        for(RANK rank : rankList) {
            int currRankValue = rank.getValue();
            RANK prevRank = RANK.getRank(currRankValue - 1);
            RANK nextRank = RANK.getRank(currRankValue + 1);

            if(!subSets.stream().anyMatch(set -> set.contains(rank))) {
                Set<RANK> newSet = new HashSet<>();
                newSet.add(rank);
                subSets.add(newSet);
            }

            subSets.stream()
                .filter(set -> (set.contains(prevRank) || set.contains(nextRank)))
                .forEach( set -> set.add(rank));
        }

        System.out.print(subSets);
        return false;
    }
在我检查是否有5张或5张以上的卡片可以组成一条直线之前,我有至少一个公共值的组合子集。我怎么做?我想不出任何不涉及许多循环的东西

已编辑

添加了整个卡。java

 public static enum RANK {
        NONE(0),
        TWO(2), THREE(3), FOUR(4), FIVE(5),
        SIX(6), SEVEN(7), EIGHT(8), NINE(9),
        TEN(10), JACK(11), QUEEN(12), KING(13), ACE(14);

        private final int value;

        private RANK(int value) {
            this.value = value;
        }

        public int getValue() {
            return value;
        }

        public static RANK getRank(int value) {
            for(RANK r : RANK.values() ) {
                if (r.getValue() == value)
                    return r;
            }
            return NONE;
        }
    }
ppackage deck;

public class Card implements Comparable {

    public static enum SUIT {
        SPADES,
        HEARTS,
        CLUBS,
        DIAMONDS
    }

    public static enum RANK {
        NONE(0),
        TWO(2), THREE(3), FOUR(4), FIVE(5),
        SIX(6), SEVEN(7), EIGHT(8), NINE(9),
        TEN(10), JACK(11), QUEEN(12), KING(13), ACE(14);

        private final int value;

        private RANK(int value) {
            this.value = value;
        }

        public int getValue() {
            return value;
        }

        public static RANK getRank(int value) {
            for(RANK r : RANK.values() ) {
                if (r.getValue() == value)
                    return r;
            }
            return NONE;
        }
    }

    private final RANK rank;
    private final SUIT suit;

    public Card(RANK rank, SUIT suit) {
        this.rank = rank;
        this.suit = suit;
    }

    public RANK getRank() {
        return rank;
    }

    public SUIT getSuit() {
        return suit;
    }

    @Override
    public String toString() {
        return "Card{" +
            "rank=" + rank +
            ", suit=" + suit +
            '}';
    }

    @Override
    public int compareTo(Object that) {
        return this.rank.getValue() - ((Card) that).rank.getValue();
    }
}
已编辑

添加了带循环的解决方案: 在方法中返回之前添加此项

for(int index = 0; index < subSets.size(); index++) {
           for(int index2 = 1; index2 < subSets.size(); index2++) {
               if(!Collections.disjoint(subSets.get(index), subSets.get(index2)) && index != index2) {
                    subSets.get(index).addAll(subSets.remove(index2));
                    index2--;
               }
           }
       }
for(int index=0;index
(我对java函数式API的编码风格仍有问题。)我会使用一个不同的排序流,并使用列表来生成子列表。 独特列表被拆分为连续列组的子列表。我希望看到一个更好的解决方案与平面图,减少或任何

static boolean isStraight(List<Card> cards) {
    List<List<Rank>> rankList = cards.stream()
            .map(Card::getRank)
            .sorted(Comparator.comparingInt(Rank::getValue))
            .distinct()
            .collect(LinkedList::new,
                    (list, rank) -> {
                        boolean startNextList = false;
                        if (list.isEmpty()) {
                            startNextList = true;
                        } else {
                            List<Rank> priorRanks = list.get(list.size() - 1);
                            if (priorRanks.get(priorRanks.size() - 1).getValue()
                                       < rank.getValue() - 1) {
                                startNextList = true;
                            }
                        }
                        if (startNextList) {
                            List<Rank> priorRanks = new LinkedList<>();
                            list.add(priorRanks);
                        }
                        List<Rank> priorRanks = list.get(list.size() - 1);
                        priorRanks.add(rank);
                    },
                    (list1, list2) -> {
                        list1.addAll(list2);
                    });

假设“直线”由五个相邻的
RANK
s组成,我们不应该认为太复杂,只需测试我们是否找到这样一个范围:

boolean isStraight(List<Card> cards) {
    BitSet set=cards.stream().map(Card::getRank).mapToInt(Card.RANK::ordinal)
        .collect(BitSet::new, BitSet::set, BitSet::or);
    for(int s=set.nextSetBit(0), e; s>=0; s=set.nextSetBit(e)) {
        e=set.nextClearBit(s);
        if(e-s >= 5) return true;
    }
    return false;
}
不同之处在于,我们现在记住了最大的范围,而不是在找到一个足够大的范围后返回。然后,我们通过过滤该范围内所有卡的原始卡列表来重新组合这些卡

这意味着

  • 对于多个最大的集合,由于
    位集合
    上的循环,将返回具有较高秩的集合,即列表中的顺序无关紧要
  • 如果范围内存在多张相同
    等级
    的卡片,则所有卡片都包含在
    卡片
    s的
    集合
    ;不要用它的大小来决定直线的大小,你必须先转换成
    RANK
    s的
    Set
    ,或者,如果你仔细观察代码,你会发现
    RANK
    s的
    Set
    Card
    s的
    Set
    构建之前已经存在了
    Set

考虑到可能的直线太少,您是否考虑过一种简单的方法,只需预先计算所有直线

// using .ordinal() instead of .value to keep it short.  
// Shouldn't be too hard to change it to use values
final Set<EnumSet<RANK>> STRAIGHTS = EnumSet.range(TWO, TEN).stream()
        .map(rank -> EnumSet.range(rank, RANK.values()[rank.ordinal() + 4]))
        .collect(toSet());
//使用.ordinal()而不是.value使其简短。
//将其更改为使用值应该不会太难
最终设置直线=EnumSet.range(2,10).stream()
.map(rank->EnumSet.range(rank,rank.values()[rank.ordinal()+4]))
.收集(toSet());
然后

boolean isStraight(List<Card> cards) {
    EnumSet<RANK> ranks = cards.stream()
        .map(Card::getRank)
        .collect(toCollection(() -> EnumSet.noneOf(RANK.class)));

    return STRAIGHTS.contains(ranks);
}
布尔isStraight(列表卡){
EnumSet ranks=cards.stream()
.map(卡片::getRank)
.collect(toCollection(()->EnumSet.noneOf(RANK.class));
返回直道。包含(列);
}

我关注的是与“有邻居的最大卡片集”相关的问题。如果对卡片等级值列表进行排序,这将变成对列表运行进行分段,然后从这些分段中选择最长运行的问题

精明的读者会注意到这一点。我将使用类似于这个问题的技巧

首先,让我们从一个输入列表开始,该列表包含OP示例中的。不过,以下技术应该适用于任何输入

    List<Rank> rankList = [FIVE, QUEEN, KING, ACE, TEN];
[0,1,2,5]

现在,我们可以使用每对索引生成包含每个运行的子列表:

    List<List<Rank>> runs = IntStream.range(1, splits.size())
        .mapToObj(i -> list.subList(splits.get(i-1), splits.get(i)))
        .collect(toList());
[女王,国王,王牌]

综上所述,我们有:

static void split(List<Rank> rankList) {
    List<Rank> sorted = rankList.stream()
        .distinct()
        .sorted()
        .collect(toList());

    List<Integer> splits = IntStream.range(1, sorted.size())
        .filter(i -> sorted.get(i).ordinal() != sorted.get(i-1).ordinal() + 1)
        .boxed()
        .collect(toCollection(ArrayList::new));

    splits.add(0, 0);
    splits.add(sorted.size());

    List<List<Rank>> runs = IntStream.range(1, splits.size())
        .mapToObj(i -> sorted.subList(splits.get(i-1), splits.get(i)))
        .collect(toList());

    int longest = runs.stream()
        .mapToInt(list -> list.size())
        .max()
        .getAsInt();

    runs.stream()
        .filter(list -> list.size() == longest)
        .forEach(System.out::println);
}
static void split(列表rankList){
List sorted=rankList.stream()
.distinct()
.已排序()
.collect(toList());
List splits=IntStream.range(1,sorted.size())
.filter(i->sorted.get(i).ordinal()!=sorted.get(i-1).ordinal()+1)
.boxed()
.collect(toCollection(ArrayList::new));
拆分。添加(0,0);
splits.add(sorted.size());
List runs=IntStream.range(1,splits.size())
.mapToObj(i->sorted.subList(splits.get(i-1),splits.get(i)))
.collect(toList());
int longest=runs.stream()
.mapToInt(列表->列表.size())
.max()
.getAsInt();
runs.stream()
.filter(list->list.size()=最长)
.forEach(System.out::println);
}

我看到你拼了两遍。(与你的问题无关)。你能展示卡片类吗?那么,两张卡片什么时候相等?我想我可以使用类似于
.reduce()
方法的方法,该方法将对每个人进行计算1,而不是对下一个进行计算。JVM如何知道这一点?让我们看看。您可能会发现它很熟悉。:-)易读的小步骤。很好的细节。如果您只对一个最大的运行感兴趣,您可以使用
runs.stream().max(Comparator.comparingInt(List::size))
    List<Rank> sorted = rankList.stream()
        .distinct()
        .sorted()
        .collect(toList());
    List<Integer> splits = IntStream.range(1, sorted.size())
        .filter(i -> sorted.get(i).ordinal() != sorted.get(i-1).ordinal() + 1)
        .boxed()
        .collect(toCollection(ArrayList::new));
    splits.add(0, 0);
    splits.add(list.size());
    List<List<Rank>> runs = IntStream.range(1, splits.size())
        .mapToObj(i -> list.subList(splits.get(i-1), splits.get(i)))
        .collect(toList());
    int longest = runs.stream()
        .mapToInt(list -> list.size())
        .max()
        .getAsInt();
    runs.stream()
        .filter(list -> list.size() == longest)
        .forEach(System.out::println);
static void split(List<Rank> rankList) {
    List<Rank> sorted = rankList.stream()
        .distinct()
        .sorted()
        .collect(toList());

    List<Integer> splits = IntStream.range(1, sorted.size())
        .filter(i -> sorted.get(i).ordinal() != sorted.get(i-1).ordinal() + 1)
        .boxed()
        .collect(toCollection(ArrayList::new));

    splits.add(0, 0);
    splits.add(sorted.size());

    List<List<Rank>> runs = IntStream.range(1, splits.size())
        .mapToObj(i -> sorted.subList(splits.get(i-1), splits.get(i)))
        .collect(toList());

    int longest = runs.stream()
        .mapToInt(list -> list.size())
        .max()
        .getAsInt();

    runs.stream()
        .filter(list -> list.size() == longest)
        .forEach(System.out::println);
}