优先队列题目:拼车

题目

标题和出处

标题:拼车

出处:1094. 拼车

难度

4 级

题目描述

要求

有一辆有 capacity \texttt{capacity} capacity 个空座位的车。车只能向东行驶(不能掉头向西行驶)。

给定整数 capacity \texttt{capacity} capacity 和数组 trips \texttt{trips} trips,其中 trips = [numPassengers i ,   from i ,   to i ] \texttt{trips} = \texttt{[numPassengers}_\texttt{i}\texttt{, from}_\texttt{i}\texttt{, to}_\texttt{i}\texttt{]} trips=[numPassengersi, fromi, toi] 表示第 i \texttt{i} i 个行程有 numPassengers i \texttt{numPassengers}_\texttt{i} numPassengersi 个乘客,乘客的上车地点和下车地点分别是 from i \texttt{from}_\texttt{i} fromi to i \texttt{to}_\texttt{i} toi。地点是车的初始位置向东的千米数。

如果可以对所有给定行程接送乘客,返回 true \texttt{true} true,否则返回 false \texttt{false} false

示例

示例 1:

输入: trips   =   [[2,1,5],[3,3,7]],   capacity   =   4 \texttt{trips = [[2,1,5],[3,3,7]], capacity = 4} trips = [[2,1,5],[3,3,7]], capacity = 4
输出: false \texttt{false} false

示例 2:

输入: trips   =   [[2,1,5],[3,3,7]],   capacity   =   5 \texttt{trips = [[2,1,5],[3,3,7]], capacity = 5} trips = [[2,1,5],[3,3,7]], capacity = 5
输出: true \texttt{true} true

示例 3:

输入: trips   =   [[2,1,5],[3,5,7]],   capacity   =   3 \texttt{trips = [[2,1,5],[3,5,7]], capacity = 3} trips = [[2,1,5],[3,5,7]], capacity = 3
输出: true \texttt{true} true

示例 4:

输入: trips   =   [[3,2,7],[3,7,9],[8,3,9]],   capacity   =   11 \texttt{trips = [[3,2,7],[3,7,9],[8,3,9]], capacity = 11} trips = [[3,2,7],[3,7,9],[8,3,9]], capacity = 11
输出: true \texttt{true} true

数据范围

  • 1 ≤ trips.length ≤ 1000 \texttt{1} \le \texttt{trips.length} \le \texttt{1000} 1trips.length1000
  • trips[i].length = 3 \texttt{trips[i].length} = \texttt{3} trips[i].length=3
  • 1 ≤ numPassengers i ≤ 100 \texttt{1} \le \texttt{numPassengers}_\texttt{i} \le \texttt{100} 1numPassengersi100
  • 0 ≤ from i ≤ to i ≤ 1000 \texttt{0} \le \texttt{from}_\texttt{i} \le \texttt{to}_\texttt{i} \le \texttt{1000} 0fromitoi1000
  • 1 ≤ capacity ≤ 10 5 \texttt{1} \le \texttt{capacity} \le \texttt{10}^\texttt{5} 1capacity105

解法

思路和算法

对于行程 [ numPassengers , from , to ] [\textit{numPassengers}, \textit{from}, \textit{to}] [numPassengers,from,to],其中包含两个事件,分别是在地点 from \textit{from} from numPassengers \textit{numPassengers} numPassengers 个乘客上车和在地点 to \textit{to} to numPassengers \textit{numPassengers} numPassengers 个乘客下车。每个事件包含两个信息,分别是地点和乘客数量变化,其中乘客数量变化为正表示上车,乘客数量变化为负表示下车。上述两个事件可以分别表示成 [ from , numPassengers ] [\textit{from}, \textit{numPassengers}] [from,numPassengers] [ to , − numPassengers ] [\textit{to}, -\textit{numPassengers}] [to,numPassengers]

为了判断是否可以对所有给定行程接送乘客,可以将所有事件按照特定顺序排列,然后按照顺序遍历所有事件,模拟全部行程。事件的排列顺序如下:

  1. 如果两个事件的地点不同,则地点小的事件发生在地点大的事件之前;

  2. 如果两个事件的地点相同,则下车事件发生在上车事件之前,即乘客数量变化为负的事件发生在乘客数量变化为正的事件之前。

根据上述排列规则,可以使用优先队列或者排序的方法指定事件顺序,此处提供的解法为优先队列。

首先创建符合上述排列规则的优先队列,然后遍历数组 trips \textit{trips} trips,对于每个行程创建两个事件并加入优先队列。当所有行程对应的事件都加入优先队列之后,即可按照从优先队列中取出行程的顺序模拟全部行程,同时维护车上乘客数量,判断是否可以对所有给定行程接送乘客。具体做法如下。

  1. 初始化乘客数量 passengers = 0 \textit{passengers} = 0 passengers=0

  2. 每次从优先队列中取出一个事件,将该事件对应的乘客数量变化更新到 passengers \textit{passengers} passengers,然后判断 passengers \textit{passengers} passengers 是否大于 capacity \textit{capacity} capacity

    • 如果 passengers > capacity \textit{passengers} > \textit{capacity} passengers>capacity,说明当前需要在车上的乘客数量超过容量,无法满足所有乘客都在车上,因此无法对所有给定行程接送乘客,返回 false \text{false} false

    • 如果 passengers ≤ capacity \textit{passengers} \le \textit{capacity} passengerscapacity,则重复上述操作,直到队列为空或者出现 passengers > capacity \textit{passengers} > \textit{capacity} passengers>capacity

  3. 如果当队列为空时仍未出现 passengers > capacity \textit{passengers} > \textit{capacity} passengers>capacity,则任何时刻车上的乘客数量都没有超过容量,因此可以对所有给定行程接送乘客,返回 true \text{true} true

代码

class Solution {
    public boolean carPooling(int[][] trips, int capacity) {
        PriorityQueue<int[]> pq = new PriorityQueue<int[]>(new Comparator<int[]>() {
            public int compare(int[] change1, int[] change2) {
                if (change1[0] != change2[0]) {
                    return change1[0] - change2[0];
                } else {
                    return change1[1] - change2[1];
                }
            }
        });
        for (int[] trip : trips) {
            int numPassengers = trip[0], from = trip[1], to = trip[2];
            int[] changeFrom = {from, numPassengers};
            int[] changeTo = {to, -numPassengers};
            pq.offer(changeFrom);
            pq.offer(changeTo);
        }
        int passengers = 0;
        while (!pq.isEmpty()) {
            int[] change = pq.poll();
            passengers += change[1];
            if (passengers > capacity) {
                return false;
            }
        }
        return true;
    }
}

复杂度分析

  • 时间复杂度: O ( n log ⁡ n ) O(n \log n) O(nlogn),其中 n n n 是数组 trips \textit{trips} trips 的长度。由于每个行程对应两个事件,因此共有 2 n 2n 2n 个事件,将 2 n 2n 2n 个事件加入优先队列需要 O ( n log ⁡ n ) O(n \log n) O(nlogn) 的时间,每次从优先队列中取出元素需要 O ( log ⁡ n ) O(\log n) O(logn) 的时间,因此总时间复杂度是 O ( n log ⁡ n ) O(n \log n) O(nlogn)

  • 空间复杂度: O ( n ) O(n) O(n),其中 n n n 是数组 trips \textit{trips} trips 的长度。空间复杂度主要取决于优先队列空间,由于每个行程对应两个事件,因此共有 2 n 2n 2n 个事件,优先队列内元素个数不会超过 2 n 2n 2n

结语

这道题的解法不唯一。除了优先队列以外,这道题还有以下两种解法:

  • 根据行程创建事件之后,将所有的事件排序,然后遍历排序后的事件;

  • 由于地点的千米数不超过 1000 1000 1000,因此可以创建数组记录每个地点的乘客数量情况,数组长度根据最大地点的千米数决定。

读者可以自行尝试。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

伟大的车尔尼

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值