斗地主之顺子

一、题目描述

在斗地主扑克牌游戏中, 扑克牌由小到大的顺序为:3,4,5,6,7,8,9,10,J,Q,K,A,2,玩家可以出的扑克牌阵型有:单张、对子、顺子、飞机、炸弹等。

其中顺子的出牌规则为:由至少5张由小到大连续递增的扑克牌组成,且不能包含2。

例如:{3,4,5,6,7}、{3,4,5,6,7,8,9,10,J,Q,K,A}都是有效的顺子;而{J,Q,K,A,2}、 {2,3,4,5,6}、{3,4,5,6}、{3,4,5,6,8}等都不是顺子。

给定一个包含13张牌的数组,如果有满足出牌规则的顺子,请输出顺子。

如果存在多个顺子,请每行输出一个顺子,且需要按顺子的第一张牌的大小(必须从小到大)依次输出。

如果没有满足出牌规则的顺子,请输出No。

二、输入输出描述

输入描述
  • 13张任意顺序的扑克牌,每张扑克牌数字用空格隔开,每张扑克牌的数字都是合法的,并且不包括大小王。
  • 不需要考虑输入为异常字符的情况
输出描述
  • 每行输出一个合法顺子(牌数字空格分隔),无则输出 No。

三、示例

|----|------------------------------|
| 输入 | 2 9 J 2 3 4 K A 7 9 A 5 6 |
| 输出 | 3 4 5 6 7 |
| 说明 | 13张牌中,可以组成的顺子只有1组:3 4 5 6 7。 |

|----|-------------------------------------------------|
| 输入 | 2 9 J 10 3 4 K A 7 Q A 5 6 |
| 输出 | 3 4 5 6 7 9 10 J Q K A |
| 说明 | 13张牌中,可以组成2组顺子,从小到大分别为:3 4 5 6 7 和 9 10 J Q K A |

四、解题思路

1. 核心思想

将扑克牌面数字化后排序,通过 "贪心匹配连续数值" 构建顺子序列,筛选出长度≥5 的有效顺子,最终按起始牌升序输出 ------ 核心是 "数值化简化比较 + 排序保证顺序 + 贪心构建连续序列"。

2. 问题本质分析
  • 表层问题:从无序扑克牌中识别并输出所有有效顺子(长度≥5、牌面连续);
  • 深层问题:
    1. 非数值型数据的比较:将字符牌面(如 J、Q)转化为数值,解决 "连续关系" 的判断问题;
    2. 连续序列的构建:在排序后的数组中,贪心匹配每个元素的连续后继,避免重复 / 遗漏顺子;
    3. 有效序列的筛选与排序:按长度和起始值筛选、排序,满足输出规则。
3. 核心逻辑
  • 数值化映射:将牌面转化为整数,把 "字符连续" 问题转化为 "数值差 = 1" 的数学判断,降低比较复杂度;
  • 排序预处理:对牌数组按数值升序排序,保证遍历顺序是 "从小到大",为贪心构建顺子提供基础;
  • 贪心构建顺子:遍历排序后的牌,优先将当前牌加入已有顺子的末尾(若连续),否则新建顺子,确保每个牌仅属于一个顺子;
  • 筛选与排序输出:保留长度≥5 的顺子,按起始牌数值升序输出。
4. 步骤拆解
  1. 牌面数值化映射

    • 定义静态 HashMap,将每张牌面映射为唯一整数(2 映射为 16,避免参与顺子)。
  2. 输入处理与排序

    • 读取输入的牌数组,按数值升序排序,保证遍历顺序为 "从小到大"。
  3. 贪心构建顺子序列

    • 初始化首个顺子(含第一张牌),存入顺子列表;
    • 遍历剩余牌,尝试将其加入已有顺子的末尾(需数值连续),无法加入则新建顺子。
  4. 筛选有效顺子

    • 过滤出长度≥5 的顺子,得到有效顺子列表。
  5. 结果输出

    • 无有效顺子则输出 "No";
    • 有则按起始牌数值升序,拼接并输出每个顺子。

五、代码实现

java 复制代码
import java.util.*;
import java.util.stream.Collectors;

public class Main {
    // 静态初始化一个用于映射扑克牌面到数字的HashMap,以方便后续比较大小
    private static final Map<String, Integer> CARD_TO_NUMBER;

    static {
        // 初始化HashMap
        CARD_TO_NUMBER = new HashMap<>();
        // 将每张扑克牌对应的面值映射到一个整数,其中2被认为是最大的牌
        CARD_TO_NUMBER.put("3", 3);
        CARD_TO_NUMBER.put("4", 4);
        CARD_TO_NUMBER.put("5", 5);
        CARD_TO_NUMBER.put("6", 6);
        CARD_TO_NUMBER.put("7", 7);
        CARD_TO_NUMBER.put("8", 8);
        CARD_TO_NUMBER.put("9", 9);
        CARD_TO_NUMBER.put("10", 10);
        CARD_TO_NUMBER.put("J", 11);
        CARD_TO_NUMBER.put("Q", 12);
        CARD_TO_NUMBER.put("K", 13);
        CARD_TO_NUMBER.put("A", 14);
        CARD_TO_NUMBER.put("2", 16);
    }

    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);  // 创建Scanner对象用于读取输入
        String[] cards = sc.nextLine().split(" ");  // 读取一行输入,并按空格分割成数组
        // 对输入的扑克牌按照定义的牌面大小进行排序
        Arrays.sort(cards, (a, b) -> CARD_TO_NUMBER.get(a) - CARD_TO_NUMBER.get(b));

        ArrayList<LinkedList<String>> straights = new ArrayList<>();  // 用于存储所有可能的顺子序列
        LinkedList<String> currentStraight = new LinkedList<>();  // 初始化当前正在检查的顺子序列
        currentStraight.add(cards[0]);  // 将排序后的第一张牌加入到当前顺子序列中
        straights.add(currentStraight);  // 将当前顺子序列加入到顺子列表中

        // 从第二张牌开始遍历所有牌
        for (int i = 1; i < cards.length; i++) {
            String card = cards[i];
            boolean added = false;  // 标记当前牌是否已被添加到某个顺子中

            // 遍历当前已存在的所有顺子序列,尝试将当前牌加入
            for (LinkedList<String> straight : straights) {
                // 判断当前牌是否可以追加到顺子的末尾
                if (CARD_TO_NUMBER.get(card) - CARD_TO_NUMBER.get(straight.getLast()) == 1) {
                    straight.add(card);
                    added = true;
                    break;
                }
            }

            // 如果当前牌没有加入到任何顺子中,创建一个新的顺子序列
            if (!added) {
                LinkedList<String> newStraight = new LinkedList<>();
                newStraight.add(card);
                straights.add(newStraight);
            }
        }

        // 筛选出长度至少为5的有效顺子序列
        List<LinkedList<String>> validStraights =
            straights.stream().filter(straight -> straight.size() >= 5).collect(Collectors.toList());

        // 如果没有找到有效的顺子序列,输出"No"
        if (validStraights.isEmpty()) {
            System.out.println("No");
        } else {
            // 将所有有效的顺子按照起始牌的大小进行排序并输出
            validStraights.stream()
                .sorted((a, b) -> CARD_TO_NUMBER.get(a.getFirst()) - CARD_TO_NUMBER.get(b.getFirst()))
                .forEach(straight -> System.out.println(String.join(" ", straight)));
        }
    }
}
相关推荐
CC.GG2 小时前
【C++】AVL树
java·开发语言·c++
闲人编程2 小时前
基础设施即代码(IaC)工具比较:Pulumi vs Terraform
java·数据库·terraform·iac·codecapsule·pulumi
墨上烟雨2 小时前
Pandas 数据结构 - Series
数据结构·pandas
鸽鸽程序猿2 小时前
【刷题册】二
算法
子榆.2 小时前
Flutter 与开源鸿蒙(OpenHarmony)深度集成:从原理到实战进阶
flutter·华为·开源·harmonyos
QQ_21696290962 小时前
Spring Boot大学生社团管理平台 【部署教程+可完整运行源码+数据库】
java·数据库·spring boot·微信小程序
Ahtacca2 小时前
Maven 入门:项目管理与依赖管理的核心玩法
java·maven
CoderCodingNo2 小时前
【GESP】C++四级真题 luogu-B4416 [GESP202509 四级] 最长连续段
开发语言·c++·算法
xjxijd2 小时前
工业元宇宙 IDC 支撑:数字孪生算法 + 边缘服务器,生产调度响应速度提 3 倍
运维·服务器·算法