华为OD机试真题——斗地主之顺子(2025A卷:100分)Java/python/JavaScript/C/C++/GO最佳实现
2025 A卷 100分 题型
本专栏内全部题目均提供Java、python、JavaScript、C、C++、GO六种语言的最佳实现方式;
并且每种语言均涵盖详细的问题分析、解题思路、代码实现、代码详解、3个测试用例以及综合分析;
本文收录于专栏:《2025华为OD真题目录+全流程解析+备考攻略+经验分享》
华为OD机试真题《斗地主之顺子》:
文章快捷目录
题目描述及说明
Java
python
JavaScript
C
GO
更多内容
题目名称:斗地主之顺子
- 知识点:字符串处理、排序算法、逻辑判断
- 时间限制:1秒
- 空间限制:256MB
- 限定语言:不限
题目描述
在斗地主扑克牌游戏中,扑克牌由小到大的顺序为:3, 4, 5, 6, 7, 8, 9, 10, J, Q, K, A, 2。玩家可以出的牌型包括单张、对子、顺子、飞机、炸弹等。
顺子的规则:
- 由至少5张连续递增的牌组成(例如:{3,4,5,6,7}或{3,4,5,…,K,A})。
- 不能包含2(如{J,Q,K,A,2}无效)。
- 不允许非连续或重复牌(如{3,4,5,6,8}无效)。
输入:
13张合法扑克牌(空格隔开,不含大小王),例如:2 9 J 2 3 4 K A 7 9 A 5 6
。
输出:
- 若有顺子,按首个牌大小升序逐行输出(如
3 4 5 6 7
)。 - 多个顺子时分行输出(如示例2)。
- 无顺子则输出
No
。
示例:
- 输入:
2 9 J 2 3 4 K A 7 9 A 5 6
→ 输出: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
。
Java
问题分析
我们需要在斗地主游戏中找出所有可能的顺子牌型。顺子由至少5张连续递增的牌组成,不能包含2,且牌不可重复。输入为13张牌,需输出所有可能的顺子,按首个牌的大小升序排列。
解题思路
-
数据预处理:
- 将牌面转换为数值,过滤掉2。
- 去重并排序,得到有序的数值列表。
-
寻找连续序列:
- 遍历排序后的数值列表,找出所有长度≥5的连续递增序列。
-
结果转换与输出:
- 将数值序列转换回牌面,按起始牌升序输出。
代码实现
import java.util.*;public class Main {public static void main(String[] args) {Scanner scanner = new Scanner(System.in);String[] cards = scanner.nextLine().split(" ");// 建立牌面到数值的映射(3→0,A→11,2→12但会被过滤)Map<String, Integer> cardToValue = new HashMap<>();String[] order = {"3", "4", "5", "6", "7", "8", "9", "10", "J", "Q", "K", "A"};for (int i = 0; i < order.length; i++) {cardToValue.put(order[i], i);}// 过滤掉2,并转换为不重复的数值集合Set<Integer> valueSet = new TreeSet<>();for (String card : cards) {if (cardToValue.containsKey(card)) { // 忽略2valueSet.add(cardToValue.get(card));}}List<Integer> sortedValues = new ArrayList<>(valueSet);if (sortedValues.size() < 5) { // 无足够牌形成顺子System.out.println("No");return;}List<List<Integer>> sequences = new ArrayList<>();int start = 0; // 当前连续序列的起始索引// 遍历数值列表,寻找连续序列for (int i = 1; i <= sortedValues.size(); i++) {if (i < sortedValues.size() && sortedValues.get(i) == sortedValues.get(i-1) + 1) {continue; // 继续连续序列} else {int length = i - start;if (length >= 5) {List<Integer> sub = sortedValues.subList(start, i); // 截取连续序列sequences.add(new ArrayList<>(sub)); // 避免引用问题}start = i; // 重置起始索引}}// 转换为牌面并输出List<List<String>> result = new ArrayList<>();for (List<Integer> seq : sequences) {List<String> sequence = new ArrayList<>();for (int val : seq) {sequence.add(order[val]);}result.add(sequence);}if (result.isEmpty()) {System.out.println("No");} else {for (List<String> seq : result) {System.out.println(String.join(" ", seq));}}}
}
代码详细解析
-
输入处理:
- 使用
Scanner
读取输入并分割为牌面数组。
- 使用
-
牌面到数值的映射:
- 使用数组
order
定义牌面顺序,建立cardToValue
映射。
- 使用数组
-
过滤与去重:
- 遍历输入的牌,过滤掉2,并将牌面转换为数值存入
TreeSet
(自动排序并去重)。
- 遍历输入的牌,过滤掉2,并将牌面转换为数值存入
-
寻找连续序列:
- 遍历排序后的数值列表,通过比较相邻数值判断是否连续。
- 发现不连续时,若当前序列长度≥5,则记录该序列。
-
结果转换与输出:
- 将数值序列转换为对应的牌面,按顺序输出。
示例测试
示例1:
输入:2 9 J 2 3 4 K A 7 9 A 5 6
输出:
3 4 5 6 7
解析:数值序列0,1,2,3,4
对应牌面3-7。
示例2:
输入:2 9 J 10 3 4 K A 7 Q A 5 6
输出:
3 4 5 6 7
9 10 J Q K A
解析:两个连续序列0-4
(3-7)和6-11
(9-A)。
示例3:
输入:2 2 2 2 2 2 2 2 2 2 2 2 2
输出:
No
解析:所有牌都是2,无法组成顺子。
综合分析
-
时间复杂度:
- 预处理:O(n),其中n为输入牌数。
- 寻找连续序列:O(m),m为去重后的牌数。
-
空间复杂度:
- 使用
TreeSet
和列表存储数值,空间复杂度为O(m)。
- 使用
-
正确性:
- 通过去重和排序确保每个顺子唯一且连续,遍历过程准确捕捉所有可能序列。
-
适用性:
- 处理任意合法输入,包括重复牌和复杂分布,确保结果正确。
python
问题分析
我们需要在斗地主游戏中找出所有可能的顺子牌型。顺子由至少5张连续递增的牌组成,不能包含2,且牌不可重复。输入为13张牌,需输出所有可能的顺子,按首个牌的大小升序排列。
解题思路
-
数据预处理:
- 将牌面转换为数值,过滤掉2。
- 去重并排序,得到有序的数值列表。
-
寻找连续序列:
- 遍历排序后的数值列表,找出所有长度≥5的连续递增序列。
-
结果转换与输出:
- 将数值序列转换回牌面,按起始牌升序输出。
代码实现
def main():# 读取输入并分割成牌列表input_cards = input().split()# 定义牌面顺序映射:3->0, 4->1,..., A->11,2被过滤card_order = ["3", "4", "5", "6", "7", "8", "9", "10", "J", "Q", "K", "A"]card_value = {card: idx for idx, card in enumerate(card_order)}# 过滤2并将牌转换为数值,去重后排序values = []seen = set()for card in input_cards:if card in card_value: # 过滤掉2val = card_value[card]if val not in seen:seen.add(val)values.append(val)values.sort()if len(values) < 5:print("No")return# 寻找所有连续序列sequences = []start = 0for i in range(1, len(values) + 1):if i < len(values) and values[i] == values[i-1] + 1:continueelse:if i - start >= 5:sequences.append(values[start:i])start = iif not sequences:print("No")return# 按序列起始值排序sequences.sort(key=lambda x: x[0])# 转换回牌面并输出for seq in sequences:converted = [card_order[val] for val in seq]print(" ".join(converted))if __name__ == "__main__":main()
代码详细解析
-
输入处理:
input().split()
读取输入并分割为牌列表。
-
牌面映射:
card_order
列表定义牌面顺序,card_value
字典将牌面映射到数值(3→0,A→11)。
-
过滤与去重:
- 遍历输入的牌,过滤掉2,转换为数值存入
values
列表,同时用集合seen
去重。 - 对
values
排序,得到升序排列的数值列表。
- 遍历输入的牌,过滤掉2,转换为数值存入
-
寻找连续序列:
- 初始化
start
记录当前连续序列的起始索引。 - 遍历数值列表,当发现不连续时,检查当前序列长度是否≥5,若是则记录。
- 示例:输入
[0,1,2,3,4,6,7,8,9,10,11]
,找到0-4
和6-11
两个序列。
- 初始化
-
结果处理:
- 若无有效序列,输出"No"。
- 按每个序列的起始值排序,确保输出顺序正确。
- 将数值序列转换回牌面字符串,用空格连接后逐行输出。
示例测试
示例1:
输入:2 9 J 2 3 4 K A 7 9 A 5 6
输出:
3 4 5 6 7
解析:数值序列[0,1,2,3,4]
对应3-7。
示例2:
输入:2 9 J 10 3 4 K A 7 Q A 5 6
输出:
3 4 5 6 7
9 10 J Q K A
解析:数值序列[0,1,2,3,4]
和[6,7,8,9,10,11]
,分别对应3-7和9-A。
示例3:
输入:2 2 2 2 2 2 2 2 2 2 2 2 2
输出:
No
解析:所有牌都是2,无法形成顺子。
综合分析
-
时间复杂度:
- 排序:O(n log n),n为去重后的牌数(最多12种)。
- 遍历找序列:O(n),线性扫描。
- 整体复杂度为O(n log n),高效处理题目限制。
-
空间复杂度:
- 存储数值列表和序列列表,空间复杂度O(n),满足题目要求。
-
正确性:
- 去重和排序确保每个顺子唯一且连续,严格遵循顺子规则。
- 边界情况处理完善(如全为2或无足够牌)。
-
适用性:
- 直接处理输入的字符串,适应各种合法输入组合。
- 代码简洁高效,逻辑清晰易于扩展。
JavaScript
问题分析
我们需要在斗地主游戏中找出所有可能的顺子牌型。顺子由至少5张连续递增的牌组成,不能包含2,且牌不可重复。输入为13张牌,需输出所有可能的顺子,按首个牌的大小升序排列。
解题思路
-
数据预处理:
- 将牌面转换为数值(例如
3→0
,4→1
,A→11
)。 - 过滤掉所有
2
,并去重后按数值排序。
- 将牌面转换为数值(例如
-
寻找连续序列:
- 遍历排序后的数值列表,找出所有长度≥5的连续递增序列。
-
结果转换与输出:
- 将数值序列转换回牌面,按起始牌升序输出。
代码实现
const readline = require('readline');const rl = readline.createInterface({input: process.stdin,output: process.stdout
});rl.on('line', (input) => {const cards = input.split(' ');// 定义牌面到数值的映射(3→0,A→11,2被过滤)const cardOrder = ["3", "4", "5", "6", "7", "8", "9", "10", "J", "Q", "K", "A"];const cardValue = {};cardOrder.forEach((card, idx) => cardValue[card] = idx);// 1. 过滤掉2,转换为数值并去重const seen = new Set();const values = [];for (const card of cards) {if (card in cardValue) { // 过滤掉2const val = cardValue[card];if (!seen.has(val)) { // 去重seen.add(val);values.push(val);}}}values.sort((a, b) => a - b); // 按数值升序// 2. 寻找连续序列const sequences = [];let start = 0;for (let i = 1; i <= values.length; i++) {// 检查是否连续if (i < values.length && values[i] === values[i - 1] + 1) {continue;} else {const length = i - start;if (length >= 5) {sequences.push(values.slice(start, i));}start = i; // 重置起始位置}}// 3. 转换回牌面并按起始牌升序排序const result = sequences.map(seq => seq.map(val => cardOrder[val])) // 数值转牌面.sort((a, b) => cardValue[a[0]] - cardValue[b[0]]); // 按起始牌排序// 输出结果if (result.length === 0) {console.log("No");} else {result.forEach(seq => console.log(seq.join(' ')));}
});
代码详细解析
-
输入处理:
input.split(' ')
将输入字符串按空格分割为牌面数组。- 示例输入:
2 9 J 2 3 4 K A 7 9 A 5 6
→ 分割为['2', '9', 'J', ..., '6']
。
-
牌面到数值的映射:
cardOrder
定义顺序,cardValue
将牌面映射到数值(例如'3'→0
,'A'→11
)。
-
过滤与去重:
- 遍历输入牌面,过滤掉
2
,并将牌面转换为数值。 - 使用
Set
去重,确保数值唯一。 - 示例过滤后:
['9', 'J', '3', '4', ...]
→ 转换为[6, 8, 0, 1, ...]
→ 去重后排序为[0, 1, 2, 3, 4, 6, 8, 10, 11]
。
- 遍历输入牌面,过滤掉
-
寻找连续序列:
- 遍历排序后的数值,记录连续递增的起始索引
start
。 - 当发现不连续时,检查当前序列长度是否≥5。
- 示例排序后的数值
[0, 1, 2, 3, 4]
→ 记录为连续序列。
- 遍历排序后的数值,记录连续递增的起始索引
-
结果转换与输出:
- 将数值序列转换回牌面(例如
0→'3'
)。 - 按起始牌升序排序(例如
3
在9
前)。 - 输出结果,若无顺子则输出
No
。
- 将数值序列转换回牌面(例如
示例测试
示例1:
输入:
2 9 J 2 3 4 K A 7 9 A 5 6
输出:
3 4 5 6 7
示例2:
输入:
2 9 J 10 3 4 K A 7 Q A 5 6
输出:
3 4 5 6 7
9 10 J Q K A
示例3:
输入:
2 2 2 2 2 2 2 2 2 2 2 2 2
输出:
No
综合分析
-
时间复杂度:
- 过滤和去重:O(n),线性遍历输入。
- 排序:O(m log m),m 为去重后的牌数(最多12)。
- 寻找连续序列:O(m),线性扫描。
-
空间复杂度:
- 存储数值和序列,空间复杂度为 O(m)。
-
正确性:
- 严格过滤
2
和重复牌。 - 正确检测连续递增序列,确保顺子规则。
- 严格过滤
-
适用性:
- 支持所有合法输入,处理复杂情况如多个顺子和边界条件。
C++
问题分析
需求:从13张牌中找出所有满足以下条件的顺子:
- 至少5张连续递增的牌(如3,4,5,6,7)。
- 不包含2(如J,Q,K,A,2无效)。
- 不允许重复或非连续牌(如3,4,5,6,8无效)。
输入:13张合法扑克牌(如2 9 J 2 3 4 K A 7 9 A 5 6
)。
输出:按首个牌升序输出所有顺子,无顺子则输出No
。
解题思路
- 数据预处理:
- 过滤掉所有
2
,并将牌面转换为数值(3→0,A→11)。 - 去重后按数值排序。
- 过滤掉所有
- 连续序列检测:
- 遍历排序后的数值,找出所有长度≥5的连续区间。
- 结果处理:
- 转换回牌面字符串,按起始牌升序输出。
代码实现
#include <iostream>
#include <vector>
#include <string>
#include <sstream>
#include <algorithm>
#include <map>
#include <set>using namespace std;// 分割字符串为牌面数组
vector<string> split(const string &s) {vector<string> tokens;string token;istringstream iss(s);while (iss >> token) {tokens.push_back(token);}return tokens;
}int main() {// 1. 读取输入并分割为牌面数组string input;getline(cin, input);vector<string> cards = split(input);// 2. 牌面到数值的映射(3→0,A→11,2被过滤)map<string, int> card_value = {{"3", 0}, {"4", 1}, {"5", 2}, {"6", 3},{"7", 4}, {"8", 5}, {"9", 6}, {"10", 7},{"J", 8}, {"Q", 9}, {"K", 10}, {"A", 11}};// 3. 过滤2并去重set<int> unique_values;for (const auto &card : cards) {if (card_value.find(card) != card_value.end()) {unique_values.insert(card_value[card]);}}// 4. 转换为排序后的数值数组vector<int> values(unique_values.begin(), unique_values.end());sort(values.begin(), values.end());// 5. 检查是否有足够牌数if (values.size() < 5) {cout << "No" << endl;return 0;}// 6. 寻找连续序列vector<vector<int>> sequences;int start = 0;for (int i = 1; i <= values.size(); ++i) {if (i < values.size() && values[i] == values[i - 1] + 1) {continue;} else {int length = i - start;if (length >= 5) {vector<int> seq(values.begin() + start, values.begin() + i);sequences.push_back(seq);}start = i;}}// 7. 处理结果if (sequences.empty()) {cout << "No" << endl;return 0;}// 8. 按起始牌升序排序sort(sequences.begin(), sequences.end(), [](const vector<int> &a, const vector<int> &b) {return a[0] < b[0];});// 9. 数值转牌面const vector<string> card_order = {"3", "4", "5", "6", "7", "8", "9", "10", "J", "Q", "K", "A"};vector<vector<string>> result;for (const auto &seq : sequences) {vector<string> converted;for (int val : seq) {converted.push_back(card_order[val]);}result.push_back(converted);}// 10. 输出结果for (const auto &seq : result) {for (size_t i = 0; i < seq.size(); ++i) {if (i > 0) cout << " ";cout << seq[i];}cout << endl;}return 0;
}
代码详细解析
- 输入处理:
split(input)
:将输入字符串分割为牌面数组(如["2", "9", "J", ...]
)。
- 牌面映射:
card_value
:建立牌面到数值的映射(如"3"→0
)。
- 过滤与去重:
unique_values
:用set
过滤掉2
并去重。
- 排序数值:
sort(values)
:将数值升序排列(如[0,1,2,3,4]
)。
- 连续序列检测:
- 遍历数值数组,维护
start
记录连续区间的起始位置。 - 当发现不连续时,若当前区间长度≥5,保存序列。
- 遍历数值数组,维护
- 结果处理:
- 将数值序列转换回牌面,并按起始牌升序排序输出。
示例测试
示例1:输入
2 9 J 2 3 4 K A 7 9 A 5 6
输出
3 4 5 6 7
示例2:输入
2 9 J 10 3 4 K A 7 Q A 5 6
输出
3 4 5 6 7
9 10 J Q K A
示例3:输入
2 2 2 2 2 2 2 2 2 2 2 2 2
输出
No
综合分析
-
时间复杂度:
- 过滤去重:O(n),n为输入牌数(13)。
- 排序:O(m log m),m为去重后的牌数(最多12)。
- 序列检测:O(m),线性遍历。
- 总时间复杂度:O(n + m log m),高效处理题目限制。
-
空间复杂度:
- 存储数值数组和序列结果,空间复杂度O(m)。
-
正确性:
- 严格过滤
2
和重复牌,确保连续序列的正确性。
- 严格过滤
-
适用性:
- 可处理任意合法输入,包括多个顺子或无顺子的边界情况。
C
问题分析
需求:从13张牌中找出所有满足以下条件的顺子:
- 至少5张连续递增的牌(如
3,4,5,6,7
)。 - 不包含
2
(如J,Q,K,A,2
无效)。 - 不允许重复或非连续牌(如
3,4,5,6,8
无效)。
输入:13张合法扑克牌(如 2 9 J 2 3 4 K A 7 9 A 5 6
)。
输出:按首个牌升序输出所有顺子,无顺子则输出 No
。
解题思路
- 输入处理:分割输入字符串为牌面数组。
- 牌面映射:将牌面(如
3
)映射为数值(如0
)。 - 过滤与去重:过滤掉
2
,去重后按数值排序。 - 连续序列检测:遍历数值数组,找出长度≥5的连续区间。
- 结果处理:转换回牌面字符串,按起始牌升序输出。
代码实现
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdbool.h>// 牌面顺序数组(3→0,A→11)
const char *CARD_ORDER[] = {"3", "4", "5", "6", "7", "8", "9", "10", "J", "Q", "K", "A"};// 牌面到数值的映射结构体
typedef struct {char name[3]; // 牌面字符串(如"10"需要2字符)int value; // 对应的数值
} CardMap;// 初始化牌面映射表
CardMap card_map[] = {{"3", 0}, {"4", 1}, {"5", 2}, {"6", 3},{"7", 4}, {"8", 5}, {"9", 6}, {"10",7},{"J", 8}, {"Q", 9}, {"K", 10}, {"A", 11}
};// 判断牌是否有效(非2)
bool is_valid_card(const char *card) {for (int i = 0; i < 12; i++) {if (strcmp(card, card_map[i].name) == 0) {return true;}}return false;
}// 获取牌对应的数值
int get_card_value(const char *card) {for (int i = 0; i < 12; i++) {if (strcmp(card, card_map[i].name) == 0) {return card_map[i].value;}}return -1; // 无效牌(如2)
}// 检查数组中是否包含某个值
bool contains(int *arr, int size, int val) {for (int i = 0; i < size; i++) {if (arr[i] == val) return true;}return false;
}// 比较函数用于排序
int compare(const void *a, const void *b) {return (*(int *)a - *(int *)b);
}// 动态数组结构体(用于存储顺子序列)
typedef struct {int *data;int size;
} IntArray;// 创建动态数组
IntArray create_int_array() {IntArray arr;arr.data = NULL;arr.size = 0;return arr;
}// 添加元素到动态数组
void append_int_array(IntArray *arr, int value) {arr->data = realloc(arr->data, (arr->size + 1) * sizeof(int));arr->data[arr->size++] = value;
}// 释放动态数组内存
void free_int_array(IntArray *arr) {free(arr->data);arr->size = 0;
}int main() {char input[100];fgets(input, sizeof(input), stdin);// 1. 分割输入字符串为牌面数组char *token = strtok(input, " \n");char *cards[13];int card_count = 0;while (token != NULL && card_count < 13) {cards[card_count] = malloc(strlen(token) + 1);strcpy(cards[card_count], token);card_count++;token = strtok(NULL, " \n");}// 2. 过滤2并收集去重的数值int values[13];int value_count = 0;for (int i = 0; i < card_count; i++) {if (is_valid_card(cards[i])) {int val = get_card_value(cards[i]);if (!contains(values, value_count, val)) {values[value_count++] = val;}}free(cards[i]); // 释放临时内存}// 3. 排序数值数组qsort(values, value_count, sizeof(int), compare);// 4. 寻找连续序列IntArray sequences[13]; // 存储所有顺子序列int seq_count = 0;int start = 0;for (int i = 1; i <= value_count; i++) {if (i < value_count && values[i] == values[i-1] + 1) {continue;} else {int length = i - start;if (length >= 5) {sequences[seq_count] = create_int_array();for (int j = start; j < i; j++) {append_int_array(&sequences[seq_count], values[j]);}seq_count++;}start = i;}}// 5. 按起始牌升序排序序列for (int i = 0; i < seq_count; i++) {for (int j = i+1; j < seq_count; j++) {if (sequences[i].data[0] > sequences[j].data[0]) {IntArray temp = sequences[i];sequences[i] = sequences[j];sequences[j] = temp;}}}// 6. 输出结果if (seq_count == 0) {printf("No\n");} else {for (int i = 0; i < seq_count; i++) {for (int j = 0; j < sequences[i].size; j++) {printf("%s", CARD_ORDER[sequences[i].data[j]]);if (j != sequences[i].size - 1) printf(" ");}printf("\n");free_int_array(&sequences[i]); // 释放内存}}return 0;
}
代码详细解析
-
输入处理:
fgets
读取输入行,strtok
分割字符串为牌面数组。- 示例输入:
2 9 J 2 3 4 K A 7 9 A 5 6
→ 分割为["2", "9", "J", ...]
。
-
过滤与去重:
is_valid_card
判断牌是否为非2
。get_card_value
将牌面转换为数值(如"3"→0
)。contains
检查是否已存在重复值。
-
排序数值:
qsort
对数值数组升序排序。
-
连续序列检测:
- 维护
start
记录连续区间的起始位置。 - 当发现不连续时,若长度≥5,保存序列到动态数组。
- 维护
-
结果排序:
- 冒泡排序按顺子的起始牌升序排列。
-
输出处理:
- 将数值转换为牌面字符串(如
0→"3"
),按格式输出。
- 将数值转换为牌面字符串(如
示例测试
示例1:输入
2 9 J 2 3 4 K A 7 9 A 5 6
输出
3 4 5 6 7
示例2:输入
2 9 J 10 3 4 K A 7 Q A 5 6
输出
3 4 5 6 7
9 10 J Q K A
示例3:输入
2 2 2 2 2 2 2 2 2 2 2 2 2
输出
No
综合分析
-
时间复杂度:
- 过滤去重:O(n²),
contains
需要线性查找。 - 排序:O(n log n),使用快速排序。
- 序列检测:O(n),线性遍历。
- 总时间复杂度:O(n² + n log n),适用于小规模输入(n ≤13)。
- 过滤去重:O(n²),
-
空间复杂度:
- 数值数组和动态序列数组,空间复杂度为 O(n²)。
-
正确性:
- 严格过滤
2
和重复牌,正确检测连续序列。
- 严格过滤
-
适用性:
- 处理输入边界情况(如全为
2
或重复牌)。 - 动态内存管理确保资源有效利用。
- 处理输入边界情况(如全为
GO
问题分析
需求:从13张牌中找出所有满足以下条件的顺子:
- 至少5张连续递增的牌(如
3,4,5,6,7
)。 - 不包含
2
(如J,Q,K,A,2
无效)。 - 不允许重复或非连续牌(如
3,4,5,6,8
无效)。
输入:13张合法扑克牌(如 2 9 J 2 3 4 K A 7 9 A 5 6
)。
输出:按首个牌升序输出所有顺子,无顺子则输出 No
。
解题思路
- 输入处理:读取输入字符串并分割为牌面数组。
- 牌面映射:将牌面转换为数值(
3→0
,A→11
),过滤2
并去重。 - 排序数值:按数值升序排列。
- 连续序列检测:遍历排序后的数值,找出所有长度≥5的连续区间。
- 结果处理:转换回牌面字符串,按起始牌升序输出。
代码实现
package mainimport ("bufio""fmt""os""sort""strings"
)func main() {// 定义牌面顺序和映射cardOrder := []string{"3", "4", "5", "6", "7", "8", "9", "10", "J", "Q", "K", "A"}cardValue := make(map[string]int)for i, card := range cardOrder {cardValue[card] = i}// 读取输入scanner := bufio.NewScanner(os.Stdin)scanner.Scan()input := scanner.Text()// 分割输入并过滤2cards := strings.Fields(input)uniqueValues := make(map[int]bool)values := make([]int, 0)for _, card := range cards {if val, exists := cardValue[card]; exists {if !uniqueValues[val] {uniqueValues[val] = truevalues = append(values, val)}}}// 检查是否有足够牌数if len(values) < 5 {fmt.Println("No")return}// 排序数值sort.Ints(values)// 寻找连续序列sequences := make([][]int, 0)start := 0for i := 1; i <= len(values); i++ {if i < len(values) && values[i] == values[i-1]+1 {continue} else {if i-start >= 5 {seq := make([]int, i-start)copy(seq, values[start:i])sequences = append(sequences, seq)}start = i}}// 处理结果if len(sequences) == 0 {fmt.Println("No")return}// 按起始牌排序sort.Slice(sequences, func(i, j int) bool {return sequences[i][0] < sequences[j][0]})// 转换回牌面并输出for _, seq := range sequences {converted := make([]string, len(seq))for i, val := range seq {converted[i] = cardOrder[val]}fmt.Println(strings.Join(converted, " "))}
}
代码详细解析
-
牌面映射:
cardOrder := []string{"3", "4", "5", "6", "7", "8", "9", "10", "J", "Q", "K", "A"} cardValue := make(map[string]int) for i, card := range cardOrder {cardValue[card] = i // 3→0, 4→1, ..., A→11 }
- 创建牌面到数值的映射表,
"3"
对应0
,"A"
对应11
。
- 创建牌面到数值的映射表,
-
输入处理:
scanner := bufio.NewScanner(os.Stdin) scanner.Scan() input := scanner.Text() cards := strings.Fields(input)
- 读取输入行并分割为牌面数组(如
["2", "9", "J", ...]
)。
- 读取输入行并分割为牌面数组(如
-
过滤与去重:
uniqueValues := make(map[int]bool) values := make([]int, 0) for _, card := range cards {if val, exists := cardValue[card]; exists {if !uniqueValues[val] {uniqueValues[val] = truevalues = append(values, val)}} }
- 过滤掉
2
并去重,保留唯一数值。
- 过滤掉
-
排序数值:
sort.Ints(values)
- 将数值按升序排列(如
[0,1,2,3,4]
)。
- 将数值按升序排列(如
-
连续序列检测:
sequences := make([][]int, 0) start := 0 for i := 1; i <= len(values); i++ {if i < len(values) && values[i] == values[i-1]+1 {continue} else {if i-start >= 5 {seq := make([]int, i-start)copy(seq, values[start:i])sequences = append(sequences, seq)}start = i} }
- 遍历排序后的数值,记录所有长度≥5的连续区间。
-
结果处理:
sort.Slice(sequences, func(i, j int) bool {return sequences[i][0] < sequences[j][0] }) for _, seq := range sequences {converted := make([]string, len(seq))for i, val := range seq {converted[i] = cardOrder[val]}fmt.Println(strings.Join(converted, " ")) }
- 按起始牌升序排序后,转换回牌面字符串输出。
示例测试
示例1:输入
2 9 J 2 3 4 K A 7 9 A 5 6
输出
3 4 5 6 7
示例2:输入
2 9 J 10 3 4 K A 7 Q A 5 6
输出
3 4 5 6 7
9 10 J Q K A
示例3:输入
2 2 2 2 2 2 2 2 2 2 2 2 2
输出
No
综合分析
-
时间复杂度:
- 过滤去重:O(n),线性遍历输入牌面。
- 排序:O(m log m),m为去重后的牌数(最多12)。
- 连续序列检测:O(m),线性遍历。
- 总时间复杂度:O(n + m log m),高效处理题目限制。
-
空间复杂度:
- 存储数值数组和序列结果,空间复杂度为O(m)。
-
正确性:
- 严格过滤
2
和重复牌,确保连续序列的正确性。
- 严格过滤
-
适用性:
- 处理所有合法输入,包括多个顺子或无顺子的边界情况。
- 代码简洁高效,逻辑清晰易于维护。
更多内容:
https://www.kdocs.cn/l/cvk0eoGYucWA
本文发表于【纪元A梦】,关注我,获取更多实用教程/资源!