斗地主之顺子

一、题目描述

在斗地主扑克牌游戏中, 扑克牌由小到大的顺序为: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)));
        }
    }
}
相关推荐
知兀2 分钟前
【MybatisPlus】后端用枚举类,数据库用tinyint,存在枚举类型转换
java
StockTV4 分钟前
印度股票实时数据 NSE和BSE的实时行情、K 线及指数数据
java·开发语言·spring boot·python
User_芊芊君子6 分钟前
【OpenAI 把 AI 玩明白了】:自主推理 + 动态知识图谱,这 4 个技术突破要颠覆行业
java·人工智能·知识图谱
七颗糖很甜15 分钟前
“十五五”气象发展规划:聚焦五大核心任务
大数据·python·算法
科研前沿21 分钟前
镜像视界浙江科技有限公司的关键技术突破有哪些?
大数据·人工智能·科技·算法·音视频·空间计算
Fuly102428 分钟前
技术经理面试相关--技术篇
面试·职场和发展
嫩萝卜头儿31 分钟前
2 - 复杂度收尾 + 链表经典OJ
数据结构·算法·链表·复杂度
c++之路39 分钟前
C++20概述
java·开发语言·c++20
星马梦缘43 分钟前
算法设计与分析 作业二 答案与解析
算法·图论·dfs·bfs·floyd-warshall·bellman_ford·多源最短路
玛丽莲茼蒿43 分钟前
Leetcode hot100 每日温度【中等】
算法·leetcode·职场和发展