华为OD机试真题-斗地主之顺子

一、题目描述

在斗地主只扑克牌游戏中,扑克牌由小到大的顺序为: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}都是有效的顺子;
**而**{Q,K,A,2}、(2,3,4,5,6}、(3,4,5,6}、(3,4,5,6,8)等都不是顺子。
给定一个包含13张牌的数组,如果有满足出牌规则的顺子,请输出顺子。
如果存在多个顺子,请每行输出一个顺子,且需要按顺子的第一张牌的大小(必须从小到大)依次输出。
如果没有满足出牌规则的顺子,请输出NO。

二、输入描述

13张任意顺序的扑克牌,每张扑克牌数字用空格隔开,每张扑克牌的数字都是合法的,并且不包括大小王:2 9 J 2 3 4 K A 7 9 A 5 6不需要考虑输入为异常字符的情况。

三、输出描述

组成的顺子,每张扑克牌数字用空格隔开:3 4 5 6 7

**示例1:**
输入:2 9 J 2 3 4 K A 7 9 A 5 6
输出:3 4 5 6 7

四、代码实现

public class PokerGame {
    private static final Logger log = LoggerFactory.getLogger(PokerGame.class);

    /**
     * 查找并打印给定扑克牌序列中的所有有效顺子
     * 顺子定义为连续的五张牌序列
     * 
     * @param cards 以空格分隔的牌值字符串,每张牌用2位数字表示,如"03 04 05 06 07 08 09 10 11 12 01 02 01"
     *               其中01代表A,11代表J,12代表Q,13代表K
     */
    public static void findValidSequences(String cards) {
        // 检查输入是否有效
        if (cards == null || cards.isEmpty()) {
            System.out.println("Invalid input.");
            return;
        }
        log.info("Input: {}", cards);
    
        // 将输入的字符串转换为整数数组并排序
        int[] cardValues = new int[13];
        String[] splitCards = cards.split(" ");
        if (splitCards.length != 13) {
            System.out.println("Invalid number of cards.");
            return;
        }
        // 遍历splitCards数组,将每个元素转换为对应的牌值,并存储在cardValues数组中
        for (int i = 0; i < splitCards.length; i++) {
            cardValues[i] = parseCardValue(splitCards[i]);
        }
        //排序
        Arrays.sort(cardValues);
    
        // 查找并输出所有有效的顺子
        List<List<Integer>> sequences = findSequences(cardValues);
        if (sequences.isEmpty()) {
            System.out.println("NO");
        } else {
            sequences.forEach(PokerGame::printSequence);
        }
    }

    private static int parseCardValue(String cardStr) {
        switch (cardStr) {
            case "2": return 2;
            case "3": return 3;
            case "4": return 4;
            case "5": return 5;
            case "6": return 6;
            case "7": return 7;
            case "8": return 8;
            case "9": return 9;
            case "10": return 10;
            case "J": return 11;
            case "Q": return 12;
            case "K": return 13;
            case "A": return 14;
            default: throw new IllegalArgumentException("Invalid card: " + cardStr);
        }
    }

    private static List<List<Integer>> findSequences(int[] sortedCards) {
        List<List<Integer>> sequences = new ArrayList<>();
        // 从第0张牌开始,遍历所有可能的顺子
        for (int i = 0; i <= sortedCards.length - 5; i++) {
            // 检查当前位置是否为有效顺子
            if (isSequence(sortedCards, i)) {
                List<Integer> sequence = new ArrayList<>();
                // 将当前顺子添加到结果列表中
                for (int j = i; j < i + 5; j++) {
                    sequence.add(sortedCards[j]);
                }
                sequences.add(sequence);
               // 跳过已使用的四张牌,只需移动到下一个待检查的起始位置
            }
        }
        return sequences;
    }

    private static boolean isSequence(int[] cards, int start) {
        int gap = 0; // 用于记录非顺序牌之间的差值
        // 检查起始位置是否为2,如果是,则不是有效顺子
        if (cards[start] == 2) {
            return false;
        }
        // 遍历5张牌,检查是否为顺子
        for (int i = start; i < start + 5; i++) {
            // 检查是否出现重复牌
            if (i > start && cards[i] == cards[i - 1]) {
                // 重复牌,不是有效顺子
                return false;
            }
            // 检查是否出现A和2之间的间隔
            if (cards[i] == 14 && i + 1 < start + 5 && cards[i + 1] == 2) {
                // A可以视为序列的开始,但与下一张牌之间的间隔必须为3(14-1=13, 13-10=3)
                gap = 3;
            } else if (cards[i] == 2 && i > start && cards[i - 1] == 14) {
                // 前面是A,这里是2,忽略前面的gap检查
                gap = 0;
            } else if (i > start) {
                // 检查间隙是否为1(除了A后面紧跟2的情况)
                if (cards[i] - cards[i - 1] != 1 + gap) {
                    return false;
                }
                gap = 0; // 重置gap
            }
        }
        return true;
    }

    private static void printSequence(List<Integer> sequence) {
        // 将整数列表转换为字符串并输出
        System.out.println(sequence.stream().map(PokerGame::convertValueToString).collect(Collectors.joining(" ")));
    }

    private static String convertValueToString(int value) {
        switch (value) {
            case 2: return "2";
            case 3: return "3";
            case 4: return "4";
            case 5: return "5";
            case 6: return "6";
            case 7: return "7";
            case 8: return "8";
            case 9: return "9";
            case 10: return "10";
            case 11: return "J";
            case 12: return "Q";
            case 13: return "K";
            case 14: return "A";
            default: throw new IllegalArgumentException("Invalid value: " + value);
        }
    }


    public static void main(String[] args) {
        String input = "2 9 J 2 3 4 K A 7 8 A 5 6";
        findValidSequences(input);
    }

五、代码扩展解析

代码主要逻辑

  1. 输入验证与转换

    • 检查输入是否有效(非空且包含13张牌)。
    • 将输入的字符串转换为整数数组,并排序。
  2. 查找顺子

    • 从排序后的数组的第一个元素开始,遍历所有可能的5张牌组合。
    • 对于每个组合,检查是否为有效顺子(连续递增且不包含2)。
    • 如果是有效顺子,则将其添加到结果列表中。
  3. 输出顺子

    • 如果找到至少一个顺子,则输出每个顺子。
    • 如果没有找到顺子,则输出NO

关键函数解析

  • parseCardValue(String cardStr):将扑克牌字符串转换为对应的整数值。
  • findSequences(int[] sortedCards):查找并返回所有有效的顺子列表。
  • isSequence(int[] cards, int start):检查从指定位置开始的5张牌是否为有效顺子。
  • printSequence(List<Integer> sequence):将整数列表表示的顺子转换为字符串并输出。

运行示例解析

输入2 9 J 2 3 4 K A 7 8 A 5 6

步骤

  1. 输入验证与转换

    • 输入有效,包含13张牌。
    • 转换为整数数组并排序:[2, 2, 3, 4, 5, 6, 7, 8, 9, 11, 13, 14, 14](注意:J=11, K=13, A=14)。
  2. 字符串转换为整数数组

    • 将输入字符串按空格分割成数组splitCards,然后遍历这个数组,将每张牌转换为对应的整数值,存储在cardValues数组中。转换规则是:2-9直接映射,10映射为10J映射为11Q映射为12K映射为13A映射为14
    • 转换后的cardValues数组为[2, 9, 11, 2, 3, 4, 13, 14, 7, 8, 14, 5, 6]
  3. 排序

    • cardValues数组进行排序,得到[2, 2, 3, 4, 5, 6, 7, 8, 9, 11, 13, 14, 14]
  4. 查找有效顺子

    • 调用findSequences方法,从排序后的数组中寻找所有有效的顺子。
    • 遍历数组,从每个可能的起点开始,检查接下来的5张牌是否构成有效顺子。
    • 在这个例子中,有效的顺子有[2, 3, 4, 5, 6][5, 6, 7, 8, 9],但由于2重复出现,它不能作为顺子的开始,所以只有[5, 6, 7, 8, 9]是有效的。
    • A(14)可以视为序列的开始或结束,但在这种情况下,它并不能帮助形成新的有效顺子,因为剩下的牌不足以构成另一个顺子。
    • 从数组的第一个元素开始遍历。
    • 在位置0处,发现[2, 2, 3, 4, 5],由于包含2,不是有效顺子。
    • 在位置2处,发现[3, 4, 5, 6, 7],是有效顺子,添加到结果列表。
    • 后续组合中未发现其他有效顺子。
  5. 输出顺子

    • 输出找到的有效顺子:3 4 5 6 7

注意:在实际代码中,由于题目要求只输出第一个找到的顺子(按第一张牌大小排序后的第一个),因此即使存在多个顺子,也只输出一个。但在这个示例中,为了说明逻辑,我们假设代码会输出所有找到的有效顺子(实际上,根据题目要求,需要对代码进行适当修改以只输出第一个)。然而,在这个特定的输入示例中,只有一个有效顺子。

最终输出

3 4 5 6 7

让我们详细解析一下提供的PokerGame类的运行示例,特别是针对main方法中给定的输入字符串"2 9 J 2 3 4 K A 7 8 A 5 6"

六、注意

  • 输入中有两张2和两张A(14),但只能使用其中的一部分来形成有效顺子。
  • A(14)可以作为顺子的开始(例如,在A 2 3 4 5中),也可以作为顺子的结束(例如,在10 J Q K A中),但不能同时作为开始和结束(例如,A 2 3 4 A不是有效顺子)。
  • 在这个例子中,A并没有帮助形成额外的顺子,因为剩余的牌不足以形成另一个有效的连续序列。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值