华为OD机试中的“免单统计”题目是一道经典的编程题,旨在测试考生对时间处理和统计计算的能力。以下是对该题目的详细解析:
一、题目描述
华为商城举办了一个促销活动,如果某顾客是某一秒内最早时刻下单的顾客(可能是多个人),则可以获取免单。请你编程计算有多少顾客可以获取免单。
二、输入输出
输入
输入为n行数据,每一行表示一位顾客的下单时间。时间以(年-月-日 时-分-秒.毫秒)yyyy-MM-ddHH:mm:ss.fff形式给出。其中,数据范围如下:
- 0<n<50000
- 2000<yyyy<2020(注意:这个范围可能因题目版本而异,但通常会给出一个明确的范围)
- 0<MM<=12
- 0<dd<=28(注意:这里的dd范围可能因月份而异,但通常不会超过当月的天数)
- 0<=HH<=23
- 0<=mm<=59
- 0<=ss<=59
所有输入保证合法。
输出
输出一个整数,表示有多少顾客可以获取免单。
三、解题思路
-
时间解析:
- 首先,需要将输入的时间字符串解析为可比较的时间对象。可以使用Python中的
datetime模块来完成这一任务。
- 首先,需要将输入的时间字符串解析为可比较的时间对象。可以使用Python中的
-
时间排序:
- 将所有顾客的下单时间按照时间顺序进行排序。这可以通过对时间对象进行排序来实现。
-
统计免单顾客:
- 遍历排序后的时间列表,对于每个时间,检查它是否与前一个时间(在同一秒内)不同。如果不同,则说明该顾客是该秒内的第一个下单者,可以获取免单。
- 使用一个字典或哈希表来记录每一秒内已经出现过的时间(精确到秒),以便快速判断当前时间是否是该秒内的第一个时间。
-
输出结果:
- 统计出可以获取免单的顾客数量,并输出结果。
以下实现只供参考,并不是标准-----------------------------------------------------------------
四、示例代码
以下是一个可能的Python实现:
from datetime import datetime
def count_free_orders(orders):
# 解析时间并排序
order_times = sorted([datetime.strptime(order, '%Y-%m-%d%H:%M:%S.%f') for order in orders])
# 统计免单顾客数量
free_count = 0
prev_time = None
prev_second = None
for order_time in order_times:
current_second = order_time.replace(microsecond=0) # 精确到秒
if prev_time is None or current_second != prev_second:
# 如果是新一秒的开始,或者之前一秒没有记录过时间,则当前顾客可以免单
free_count += 1
prev_second = current_second
prev_time = order_time
return free_count
# 示例输入
orders = [
'2020-01-01 12:00:00.001',
'2020-01-01 12:00:00.002',
'2020-01-01 12:00:01.000',
'2020-01-01 12:00:01.001'
]
# 调用函数并输出结果
print(count_free_orders(orders)) # 输出: 2
以下是一个可能的Java实现:
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Date;
import java.util.List;
public class FreeOrderCounter {
// 日期格式
private static final SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS");
// 统计免单顾客数量
public static int countFreeOrders(List<String> orders) {
// 解析时间并存储为Date对象
List<Date> orderDates = new ArrayList<>();
for (String order : orders) {
try {
Date date = dateFormat.parse(order);
orderDates.add(date);
} catch (ParseException e) {
e.printStackTrace();
// 假设输入数据合法,这里不处理异常
}
}
// 对时间进行排序
Collections.sort(orderDates);
// 统计免单顾客数量
int freeCount = 0;
Date prevDate = null;
for (Date date : orderDates) {
// 截断到秒,比较秒级时间
Date truncatedDate = truncateToSeconds(date);
if (prevDate == null || !truncatedDate.equals(truncateToSeconds(prevDate))) {
// 如果是新一秒的开始,则当前顾客可以免单
freeCount++;
prevDate = date;
}
}
return freeCount;
}
// 将时间截断到秒(移除毫秒部分)
private static Date truncateToSeconds(Date date) {
return dateFormat.parse(dateFormat.format(date), null); // 使用格式化再解析来截断毫秒
}
public static void main(String[] args) {
// 示例输入
List<String> orders = new ArrayList<>();
orders.add("2020-01-01 12:00:00.001");
orders.add("2020-01-01 12:00:00.002");
orders.add("2020-01-01 12:00:01.000");
orders.add("2020-01-01 12:00:01.001");
// 调用函数并输出结果
int freeCount = countFreeOrders(orders);
System.out.println("Free orders count: " + freeCount); // 输出: 4
}
}
以上代码有可能其实重复计算免单人数,所以输出了4
解释一下:
第一个订单 2020-01-01 12:00:00.001 被截断到秒后为 2020-01-01 12:00:00,计为第一个免单。
第二个订单 2020-01-01 12:00:00.002 被截断到秒后也为 2020-01-01 12:00:00,与前一个订单在同一秒内,不计为新的免单。
第三个订单 2020-01-01 12:00:01.000 被截断到秒后为 2020-01-01 12:00:01,计为第二个免单。
第四个订单 2020-01-01 12:00:01.001 被截断到秒后也为 2020-01-01 12:00:01,与前一个订单在同一秒内,不计为新的免单。
因此,最终的免单数量应该是 2,而不是 4。
可能正确的实现如下:
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
public class FreeOrderCounter {
// 日期格式
private static final SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS");
// 统计免单顾客数量
public static int countFreeOrders(List<String> orders) {
// 解析时间并存储为Date对象,同时记录每个秒级时间点的最早订单
Map<Date, Date> earliestOrders = new HashMap<>();
for (String order : orders) {
try {
Date date = dateFormat.parse(order);
// 截断到秒
Date truncatedDate = truncateToSeconds(date);
// 如果该秒还没有记录过订单,或者当前订单比该秒记录的最早订单还早(理论上不应该,但为了严谨性还是检查)
if (!earliestOrders.containsKey(truncatedDate) || date.before(earliestOrders.get(truncatedDate))) {
earliestOrders.put(truncatedDate, date);
}
} catch (ParseException e) {
e.printStackTrace();
// 假设输入数据合法,这里不处理异常
}
}
// 免单顾客数量就是最早订单的数量(即秒级时间点的数量)
return earliestOrders.size();
}
// 将时间截断到秒(移除毫秒部分)
private static Date truncateToSeconds(Date date) {
Calendar calendar = Calendar.getInstance();
calendar.setTime(date);
calendar.set(Calendar.MILLISECOND, 0);
return calendar.getTime();
}
public static void main(String[] args) {
// 示例输入
List<String> orders = new ArrayList<>();
orders.add("2020-01-01 12:00:00.001");
orders.add("2020-01-01 12:00:00.002");
orders.add("2020-01-01 12:00:01.000");
orders.add("2020-01-01 12:00:01.001");
// 调用函数并输出结果
int freeCount = countFreeOrders(orders);
System.out.println("Free orders count: " + freeCount); // 输出: 2
}
}
五、运行示例解析
示例输入
List<String> orders = new ArrayList<>();
orders.add("2020-01-01 12:00:00.001");
orders.add("2020-01-01 12:00:00.002");
orders.add("2020-01-01 12:00:01.000");
orders.add("2020-01-01 12:00:01.001");
这个orders列表包含了四个订单的时间戳,每个时间戳都精确到毫秒。
代码执行流程
-
初始化:
earliestOrders是一个HashMap,用于存储每个秒级时间点的最早订单。dateFormat是一个SimpleDateFormat实例,用于解析和格式化日期字符串。
-
遍历订单列表:
- 对于列表中的每个订单时间戳字符串,执行以下步骤:
a. 使用dateFormat.parse(order)将字符串解析为Date对象。
b. 调用truncateToSeconds(date)方法将Date对象截断到秒级。
c. 检查earliestOrders映射中是否已存在该秒级时间点的记录:- 如果不存在,将该秒级时间点和对应的
Date对象添加到映射中。 - 如果存在,比较当前订单与该秒级时间点记录的最早订单。由于我们的逻辑是只记录每个秒级时间点的第一个订单,所以这里的比较实际上是多余的(因为我们已经按列表顺序遍历,所以第一个遇到的订单自然是最早的),但为了代码的严谨性,我们还是保留了这一步骤(尽管在这个特定示例中它不会改变结果)。
- 如果不存在,将该秒级时间点和对应的
- 对于列表中的每个订单时间戳字符串,执行以下步骤:
-
计算免单数量:
- 遍历完成后,
earliestOrders映射中的键的数量即为免单顾客的数量。因为每个键代表一个不同的秒级时间点,而每个时间点我们只记录了一个最早的订单。
- 遍历完成后,
示例解析
- 第一个订单
"2020-01-01 12:00:00.001"被截断到秒后为2020-01-01 12:00:00,并作为该秒级时间点的最早订单被记录。 - 第二个订单
"2020-01-01 12:00:00.002"也被截断到秒后为2020-01-01 12:00:00,但由于它与第一个订单在同一秒内,所以不会被记录为新的免单。 - 第三个订单
"2020-01-01 12:00:01.000"被截断到秒后为2020-01-01 12:00:01,并作为该秒级时间点的最早订单被记录。 - 第四个订单
"2020-01-01 12:00:01.001"也被截断到秒后为2020-01-01 12:00:01,但由于它与第三个订单在同一秒内,所以不会被记录为新的免单。
结果
earliestOrders映射最终包含两个键:2020-01-01 12:00:00和2020-01-01 12:00:01,分别对应两个秒级时间点的最早订单。- 因此,免单顾客的数量为2。
输出
Free orders count: 2
这个输出验证了我们的逻辑是正确的,即只有每个秒级时间点的第一个订单被计为免单。
六、注意事项
-
时间精度:
- 题目中明确给出了时间精度到毫秒,因此在解析和比较时间时需要考虑到毫秒部分。
-
性能优化:
- 由于输入数据可能很大(最多50000行),因此需要对算法进行优化,以确保在合理的时间内得出结果。上述代码通过排序和一次遍历来实现统计,具有较好的性能表现。
-
边界条件:
- 需要注意处理边界条件,如输入数据为空、时间范围超出预期等。这些边界条件通常在题目描述中会给出明确的说明或限制。
综上所述,“免单统计”题目是一道考察时间处理和统计计算能力的编程题。通过合理的解析、排序和统计步骤,可以高效地得出结果。

3391

被折叠的 条评论
为什么被折叠?



