第195场周赛
判断路径是否相交
使用集合来存储已经走过的点的坐标,如果该点的坐标已经在集合中出现,那么说明有相交的路径,否则就没有。初始坐标为$(0,0)$。若方向为N,则横坐标y加1,方向为S,横坐标减1。若方向为E,则横坐标x加1,方向为W,横坐标减1。
1 | class Solution { |
2 | public boolean isPathCrossing(String path) { |
3 | int len = path.length(); |
4 | int row = 0; |
5 | int col = 0; |
6 | //也可以使用String来存储坐标 |
7 | Set<List<Integer>> set = new HashSet<List<Integer>>(); |
8 | List<Integer> list = new ArrayList<>(); |
9 | list.add(row); |
10 | list.add(col); |
11 | set.add(list); |
12 | for(int i=0; i<len; i++){ |
13 | char c = path.charAt(i); |
14 | if(c == 'N'){ |
15 | col++; |
16 | }else if(c == 'S'){ |
17 | col--; |
18 | }else if(c == 'E'){ |
19 | row++; |
20 | }else{ |
21 | row--; |
22 | } |
23 | List<Integer> list1 = new ArrayList<>(); |
24 | list1.add(row); |
25 | list1.add(col); |
26 | if(set.contains(list1)){ |
27 | return true; |
28 | }else{ |
29 | set.add(list1); |
30 | } |
31 | } |
32 | return false; |
33 | } |
34 | } |
复杂度分析:
- 时间复杂度:$O(n)$。
- 空间复杂度:$O(n)$。
##检查数组对是否可以被 k 整除
k的所有余数都在$[0,k-1]$之间,如果余数为$i$的个数和余数为$k-i$的个数相同,那么它们就能进行配对被k整除,所以只需要按余数分类并统计个数。对于余数为0的情况,需要看余数为0的个数是否为偶数,若为偶数,就可以自己和自己配对,若为奇数,那么最后就会剩余一个从而无法配对。对于余数出现负数的情况,就先加上k,再对k取余。
1 | class Solution { |
2 | public boolean canArrange(int[] arr, int k) { |
3 | int n = arr.length; |
4 | int[] mod = new int[k]; |
5 | for(int i=0; i<n; i++){ |
6 | int num = (arr[i]%k+k)%k; |
7 | mod[num]++; |
8 | } |
9 | for(int i=1; i<k/2; i++){ |
10 | if(mod[i] != mod[k-i]){ |
11 | return false; |
12 | } |
13 | } |
14 | return mod[0]%2==0; |
15 | } |
16 | } |
复杂度分析:
- 时间复杂度:$O(n)$。
- 空间复杂度:$O(n)$。
满足条件的子序列数目
方法一:二分查找
- 排序。
- 遍历左端点$i$,二分查找出小于等于$target-nums[i]$的最后一个下标$j$。
- 包含左端点$i$的满足条件的所有子序列个数为$2^{j-i}$个。
如果$j-i$的数很大的话,那么直接用Math.pow()
的话会很慢而且数会很大,即使是用double
也存储不下。因此我们使用一个长度为$nums.length$的数组来存储2的幂次方取模的结果。
1 | class Solution { |
2 | public int numSubseq(int[] nums, int target) { |
3 | Arrays.sort(nums); |
4 | if(nums[0]*2 > target){ |
5 | return 0; |
6 | } |
7 | int n = nums.length; |
8 | int res = 0; |
9 | int mode = (int) 1e9 + 7; |
10 | //快速幂,存储2的幂次方取模 |
11 | int[] pow = new int[n]; |
12 | pow[0] = 1; |
13 | for(int i=1;i<n;i++){ |
14 | pow[i] = pow[i-1] * 2; |
15 | pow[i] = pow[i] % mode; |
16 | } |
17 | for(int i=0; i<n; i++){ |
18 | int pos = binarySearch(nums, target-nums[i]); |
19 | if(pos>=i){ |
20 | res += pow[pos-i]; |
21 | res %= mode; |
22 | } |
23 | } |
24 | return res; |
25 | } |
26 | //二分查找 |
27 | public int binarySearch(int[] nums, int target) { |
28 | int low = 0, high = nums.length; |
29 | while (low < high) { |
30 | int mid = (high - low) / 2 + low; |
31 | if (mid == nums.length) { |
32 | return mid; |
33 | } |
34 | int num = nums[mid]; |
35 | if (num <= target) { |
36 | low = mid + 1; |
37 | } else { |
38 | high = mid; |
39 | } |
40 | } |
41 | return high-1; |
42 | } |
43 | } |
复杂度分析:
- 时间复杂度:$O(nlogn)$。
- 空间复杂度:$O(n)$。
方法二:双指针
- 排序
- 双指针,左指针
left
指向下标0
,右指针right
指向下标nums.length-1
。当$left<=right$时,如果$nums[left]+nums[right]<=target$时,说明满足条件,那么结果加上$2^{right-left}$,接着将左指针右移。否则就将右指针左移。
1 | class Solution { |
2 | public int numSubseq(int[] nums, int target) { |
3 | Arrays.sort(nums); |
4 | if (nums[0] * 2 > target) { |
5 | return 0; |
6 | } |
7 | int left = 0; |
8 | int right = nums.length - 1; |
9 | int res = 0; |
10 | int[] pow = new int[nums.length]; |
11 | pow[0] = 1; |
12 | int mode = (int) 1e9 + 7; |
13 | for (int i = 1; i < nums.length; i ++) { |
14 | pow[i] = pow[i-1] * 2; |
15 | pow[i] %= mode; |
16 | } |
17 | while (left <= right) { |
18 | if (nums[left] + nums[right] <= target) { |
19 | res += pow[right - left]; |
20 | res %= mode; |
21 | left++; |
22 | } |
23 | else { |
24 | right--; |
25 | } |
26 | } |
27 | return res; |
28 | } |
29 | } |
复杂度分析:
- 时间复杂度:$O(nlogn)$。
- 空间复杂度:$O(n)$。
满足不等式的最大值
由于当$i<j$时,$x_i < x_j$,所以$y_i + y_j + |x_i - x_j|$可以变为$y_i + y_j + x_j - x_i$即$(x_j + y_j) + (y_i - x_i)$。所以题目变为对于某一下标$j$时,找出$y_i - x_i$的最大值,同时满足$|x_i - x_j|<=k$。可以使用单调队列解决此问题,流程如下。
- 维护一个单调队列,队首为$y-x$最大的横坐标$x_{head}$。
- 对于点$j$,首先从队首依次删除不满足$x_j - x_{head}>k$的点,$x_{head}$为队首。
- 之后取出队首$x_{head}$,计算$x_j+y_j+y_{head}-x_{head}$,更新ans。
- 维护队列单调性,更新队列,从队尾开始删除$y_{tail}-x_{tail} < y_j - x_j$的元素,删除完毕后将点$j$插入队尾。
1 | class Solution { |
2 | public int findMaxValueOfEquation(int[][] points, int k) { |
3 | Deque<Integer> deque = new ArrayDeque<>(); |
4 | int ans = Integer.MIN_VALUE; |
5 | for(int i=0; i<points.length; i++){ |
6 | while(!deque.isEmpty() && (points[i][0]-points[deque.peekFirst()][0]>k)){ |
7 | deque.pollFirst(); |
8 | } |
9 | if(!deque.isEmpty()){ |
10 | int head = deque.peekFirst(); |
11 | ans = Math.max(ans, points[head][1]-points[head][0]+points[i][0]+points[i][1]); |
12 | } |
13 | while(!deque.isEmpty() && isLess(points, deque.peekLast(), i)){ |
14 | deque.removeLast(); |
15 | } |
16 | deque.offerLast(i); |
17 | } |
18 | return ans; |
19 | } |
20 | |
21 | public boolean isLess(int[][] points, int tail, int i){ |
22 | if(points[tail][1]-points[tail][0] < points[i][1]-points[i][0]){ |
23 | return true; |
24 | }else{ |
25 | return false; |
26 | } |
27 | } |
28 | } |
复杂度分析:
- 时间复杂度:$O(n)$。
- 空间复杂度:$O(n)$。