
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的连续递增序列。
-
结果转换与输出:
- 将数值序列转换回牌面,按起始牌升序输出。
代码实现
java
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)) { // 忽略2
valueSet.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的连续递增序列。
-
结果转换与输出:
- 将数值序列转换回牌面,按起始牌升序输出。
代码实现
python
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: # 过滤掉2
val = 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 = 0
for i in range(1, len(values) + 1):
if i < len(values) and values[i] == values[i-1] + 1:
continue
else:
if i - start >= 5:
sequences.append(values[start:i])
start = i
if 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的连续递增序列。
-
结果转换与输出:
- 将数值序列转换回牌面,按起始牌升序输出。
代码实现
javascript
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) { // 过滤掉2
const 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的连续区间。
- 结果处理 :
- 转换回牌面字符串,按起始牌升序输出。
代码实现
cpp
#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的连续区间。
- 结果处理:转换回牌面字符串,按起始牌升序输出。
代码实现
c
#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的连续区间。
- 结果处理:转换回牌面字符串,按起始牌升序输出。
代码实现
go
package main
import (
"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()
// 分割输入并过滤2
cards := 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] = true
values = append(values, val)
}
}
}
// 检查是否有足够牌数
if len(values) < 5 {
fmt.Println("No")
return
}
// 排序数值
sort.Ints(values)
// 寻找连续序列
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
}
}
// 处理结果
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, " "))
}
}
代码详细解析
-
牌面映射:
gocardOrder := []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
。
- 创建牌面到数值的映射表,
-
输入处理:
goscanner := bufio.NewScanner(os.Stdin) scanner.Scan() input := scanner.Text() cards := strings.Fields(input)
- 读取输入行并分割为牌面数组(如
["2", "9", "J", ...]
)。
- 读取输入行并分割为牌面数组(如
-
过滤与去重:
gouniqueValues := make(map[int]bool) values := make([]int, 0) for _, card := range cards { if val, exists := cardValue[card]; exists { if !uniqueValues[val] { uniqueValues[val] = true values = append(values, val) } } }
- 过滤掉
2
并去重,保留唯一数值。
- 过滤掉
-
排序数值:
gosort.Ints(values)
- 将数值按升序排列(如
[0,1,2,3,4]
)。
- 将数值按升序排列(如
-
连续序列检测:
gosequences := 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的连续区间。
-
结果处理:
gosort.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梦】,关注我,获取更多实用教程/资源!