华为OD机试真题---免单统计

华为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

所有输入保证合法。

输出

输出一个整数,表示有多少顾客可以获取免单。

三、解题思路

  1. 时间解析

    • 首先,需要将输入的时间字符串解析为可比较的时间对象。可以使用Python中的datetime模块来完成这一任务。
  2. 时间排序

    • 将所有顾客的下单时间按照时间顺序进行排序。这可以通过对时间对象进行排序来实现。
  3. 统计免单顾客

    • 遍历排序后的时间列表,对于每个时间,检查它是否与前一个时间(在同一秒内)不同。如果不同,则说明该顾客是该秒内的第一个下单者,可以获取免单。
    • 使用一个字典或哈希表来记录每一秒内已经出现过的时间(精确到秒),以便快速判断当前时间是否是该秒内的第一个时间。
  4. 输出结果

    • 统计出可以获取免单的顾客数量,并输出结果。

以下实现只供参考,并不是标准-----------------------------------------------------------------

四、示例代码

以下是一个可能的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列表包含了四个订单的时间戳,每个时间戳都精确到毫秒。

代码执行流程
  1. 初始化

    • earliestOrders是一个HashMap,用于存储每个秒级时间点的最早订单。
    • dateFormat是一个SimpleDateFormat实例,用于解析和格式化日期字符串。
  2. 遍历订单列表

    • 对于列表中的每个订单时间戳字符串,执行以下步骤:
      a. 使用dateFormat.parse(order)将字符串解析为Date对象。
      b. 调用truncateToSeconds(date)方法将Date对象截断到秒级。
      c. 检查earliestOrders映射中是否已存在该秒级时间点的记录:
      • 如果不存在,将该秒级时间点和对应的Date对象添加到映射中。
      • 如果存在,比较当前订单与该秒级时间点记录的最早订单。由于我们的逻辑是只记录每个秒级时间点的第一个订单,所以这里的比较实际上是多余的(因为我们已经按列表顺序遍历,所以第一个遇到的订单自然是最早的),但为了代码的严谨性,我们还是保留了这一步骤(尽管在这个特定示例中它不会改变结果)。
  3. 计算免单数量

    • 遍历完成后,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:002020-01-01 12:00:01,分别对应两个秒级时间点的最早订单。
  • 因此,免单顾客的数量为2。
输出
Free orders count: 2

这个输出验证了我们的逻辑是正确的,即只有每个秒级时间点的第一个订单被计为免单。

六、注意事项

  1. 时间精度

    • 题目中明确给出了时间精度到毫秒,因此在解析和比较时间时需要考虑到毫秒部分。
  2. 性能优化

    • 由于输入数据可能很大(最多50000行),因此需要对算法进行优化,以确保在合理的时间内得出结果。上述代码通过排序和一次遍历来实现统计,具有较好的性能表现。
  3. 边界条件

    • 需要注意处理边界条件,如输入数据为空、时间范围超出预期等。这些边界条件通常在题目描述中会给出明确的说明或限制。

综上所述,“免单统计”题目是一道考察时间处理和统计计算能力的编程题。通过合理的解析、排序和统计步骤,可以高效地得出结果。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值