5400. 旅行终点站
给你一份旅游线路图,该线路图中的旅行线路用数组 paths 表示,其中 paths[i] = [cityAi, cityBi] 表示该线路将会从 cityAi 直接前往 cityBi 。请你找出这次旅行的终点站,即没有任何可以通往其他城市的线路的城市。
题目数据保证线路图会形成一条不存在循环的线路,因此只会有一个旅行终点站。
示例 1:
输入:paths = [[“London”,“New York”],[“New York”,“Lima”],[“Lima”,“Sao Paulo”]]
输出:“Sao Paulo”
解释:从 “London” 出发,最后抵达终点站 “Sao Paulo” 。本次旅行的路线是 “London” -> “New York” -> “Lima” -> “Sao Paulo” 。
示例 2:
输入:paths = [[“B”,“C”],[“D”,“B”],[“C”,“A”]]
输出:“A”
解释:所有可能的线路是:
“D” -> “B” -> “C” -> “A”.
“B” -> “C” -> “A”.
“C” -> “A”.
“A”.
显然,旅行终点站是 “A” 。
示例 3:
输入:paths = [[“A”,“Z”]]
输出:“Z”
提示:
1 <= paths.length <= 100
paths[i].length == 2
1 <= cityAi.length, cityBi.length <= 10
cityAi != cityBi
所有字符串均由大小写英文字母和空格字符组成。
思路
找只在第二个位置出现没有在第一个位置出现的元素
代码
class Solution {
public String destCity(List<List<String>> paths) {
HashSet<String> froms = new HashSet<>();
for (List<String> path: paths) {
froms.add(path.get(0));
}
for (List<String> path: paths) {
String to = path.get(1);
if (!froms.contains(to)) {
return to;
}
}
return "";
}
}
5401. 是否所有 1 都至少相隔 k 个元素
给你一个由若干 0 和 1 组成的数组 nums 以及整数 k。如果所有 1 都至少相隔 k 个元素,则返回 True ;否则,返回 False 。
示例 1:
输入:nums = [1,0,0,0,1,0,0,1], k = 2
输出:true
解释:每个 1 都至少相隔 2 个元素。
示例 2:
输入:nums = [1,0,0,1,0,1], k = 2
输出:false
解释:第二个 1 和第三个 1 之间只隔了 1 个元素。
示例 3:
输入:nums = [1,1,1,1,1], k = 0
输出:true
示例 4:
输入:nums = [0,1,0,1], k = 1
输出:true
提示:
1 <= nums.length <= 10^5
0 <= k <= nums.length
nums[i] 的值为 0 或 1
代码
class Solution {
public boolean kLengthApart(int[] nums, int k) {
int pre = -1, cur = -1, i = 0, n = nums.length;
for (i=0; i<n; ++i) {
if (nums[i] == 1) {
pre = cur;
cur = i;
if (pre != -1 && cur - pre <= k) {
return false;
}
}
}
return true;
}
}
5402. 绝对差不超过限制的最长连续子数组
给你一个整数数组 nums ,和一个表示限制的整数 limit,请你返回最长连续子数组的长度,该子数组中的任意两个元素之间的绝对差必须小于或者等于 limit 。
如果不存在满足条件的子数组,则返回 0 。
示例 1:
输入:nums = [8,2,4,7], limit = 4
输出:2
解释:所有子数组如下:
[8] 最大绝对差 |8-8| = 0 <= 4.
[8,2] 最大绝对差 |8-2| = 6 > 4.
[8,2,4] 最大绝对差 |8-2| = 6 > 4.
[8,2,4,7] 最大绝对差 |8-2| = 6 > 4.
[2] 最大绝对差 |2-2| = 0 <= 4.
[2,4] 最大绝对差 |2-4| = 2 <= 4.
[2,4,7] 最大绝对差 |2-7| = 5 > 4.
[4] 最大绝对差 |4-4| = 0 <= 4.
[4,7] 最大绝对差 |4-7| = 3 <= 4.
[7] 最大绝对差 |7-7| = 0 <= 4.
因此,满足题意的最长子数组的长度为 2 。
示例 2:
输入:nums = [10,1,2,4,7,2], limit = 5
输出:4
解释:满足题意的最长子数组是 [2,4,7,2],其最大绝对差 |2-7| = 5 <= 5 。
示例 3:
输入:nums = [4,2,2,2,4,4,2,2], limit = 0
输出:3
提示:
1 <= nums.length <= 10^5
1 <= nums[i] <= 10^9
0 <= limit <= 10^9
思路
二分答案。判断答案是否可行的函数就是在一个数组的给定长度的窗口上求最大最小值,使用滑动窗口实现。假设nums
的长度为n
,滑动窗口的时间复杂度为O(n)
,总的时间复杂度为O(nlogn)
.
代码
class Solution {
private class Value {
public int val, idx;
public Value(int _val, int _idx) {
val = _val;
idx = _idx;
}
}
private boolean slidingMaxMin(int[] nums, int w, int limit) {
if (w == 1) {
return true;
}
int i = 0, n = nums.length;
LinkedList<Value> dq_max = new LinkedList<>(), dq_min = new LinkedList<>();
dq_max.add(new Value(nums[0], 0));
dq_min.add(new Value(nums[0], 0));
for (i=1; i<n; ++i) {
// update dq_max
if (nums[i] >= dq_max.getFirst().val) {
dq_max.clear();
dq_max.add(new Value(nums[i], i));
} else {
while (!dq_max.isEmpty() && dq_max.getLast().val <= nums[i]) {
dq_max.removeLast();
}
dq_max.add(new Value(nums[i], i));
while (!dq_max.isEmpty() && dq_max.getFirst().idx <= i - w) {
dq_max.removeFirst();
}
}
// update dq_min
if (nums[i] <= dq_min.getFirst().val) {
dq_min.clear();
dq_min.add(new Value(nums[i], i));
} else {
while (!dq_min.isEmpty() && dq_min.getLast().val >= nums[i]) {
dq_min.removeLast();
}
dq_min.add(new Value(nums[i], i));
while (!dq_min.isEmpty() && dq_min.getFirst().idx <= i - w) {
dq_min.removeFirst();
}
}
// check max & min
if (i >= w-1 && dq_max.getFirst().val - dq_min.getFirst().val <= limit) {
return true;
}
}
return false;
}
public int longestSubarray(int[] nums, int limit) {
int n = nums.length, left = 1, right = n, mid = 0;
while (left <= right) {
mid = (right - left) / 2 + left;
if (mid == 1 || (mid != 0 && slidingMaxMin(nums, mid, limit))) {
left = mid + 1;
} else {
right = mid - 1;
}
}
return right;
}
}
5403. 有序矩阵中的第 k 个最小数组和
给你一个 m * n 的矩阵 mat,以及一个整数 k ,矩阵中的每一行都以非递减的顺序排列。
你可以从每一行中选出 1 个元素形成一个数组。返回所有可能数组中的第 k 个 最小 数组和。
示例 1:
输入:mat = [[1,3,11],[2,4,6]], k = 5
输出:7
解释:从每一行中选出一个元素,前 k 个和最小的数组分别是:
[1,2], [1,4], [3,2], [3,4], [1,6]。其中第 5 个的和是 7 。
示例 2:
输入:mat = [[1,3,11],[2,4,6]], k = 9
输出:17
示例 3:
输入:mat = [[1,10,10],[1,4,5],[2,3,6]], k = 7
输出:9
解释:从每一行中选出一个元素,前 k 个和最小的数组分别是:
[1,1,2], [1,1,3], [1,4,2], [1,4,3], [1,1,6], [1,5,2], [1,5,3]。其中第 7 个的和是 9 。
示例 4:
输入:mat = [[1,1,10],[2,2,9]], k = 7
输出:12
提示:
m == mat.length
n == mat.length[i]
1 <= m, n <= 40
1 <= k <= min(200, n ^ m)
1 <= mat[i][j] <= 5000
mat[i] 是一个非递减数组
思路
注意到每一行的选取是独立的,因此问题具有子结构:整个二维数组的前k小的数组和,在二维数组的前i行中的部分,也是前k小的数组和。因此,用一个最大堆维护前i行的最小的k个数组和,每次把前i行的k个数组和与第(i+1)行的前min(n, k)个数组分别相加,最小的k个加和就是前(i+1)行的最小的k个数组和。如此递推即可。时间复杂度O(mnklogk)
.
代码
class Solution {
public int kthSmallest(int[][] mat, int k) {
int m = mat.length, n = Math.min(mat[0].length, k), i = 0, j = 0;
PriorityQueue<Integer> pq = new PriorityQueue<>(new Comparator<Integer>() {
@Override
public int compare(Integer o1, Integer o2) {
return o2 - o1;
}
});
for (j=0; j<n; ++j) {
pq.add(mat[0][j]);
}
for (i=1; i<m; ++i) {
ArrayList<Integer> tmp = new ArrayList<>(pq);
pq.clear();
for (int num: tmp) {
for (j=0; j<n; ++j) {
int cur = num + mat[i][j];
if (pq.size() < k) {
pq.add(cur);
} else if (pq.size() == k && pq.peek() > cur) {
pq.poll();
pq.add(cur);
}
}
}
}
return pq.peek();
}
}