Leetcode刷题 2021.02.19
Leetcode1577 数的平方等于两数乘积的方法数
给你两个整数数组 nums1 和 nums2 ,请你返回根据以下规则形成的三元组的数目(类型 1 和类型 2 ):
类型 1:三元组 (i, j, k) ,如果 nums1[i]2 == nums2[j] * nums2[k] 其中 0 <= i < nums1.length 且 0 <= j < k < nums2.length
类型 2:三元组 (i, j, k) ,如果 nums2[i]2 == nums1[j] * nums1[k] 其中 0 <= i < nums2.length 且 0 <= j < k < nums1.length
题解里好多都是hash表解法,写了一会儿发现比较复杂。然后想想好像就是三数之和,于是用排序做了。代码的话基本就是三数之和那一套,分析的时间复杂度比较低。但是实际击败双百,应该是hash表的查找速度比较慢吧。
class Solution {
int res = 0;
public int numTriplets(int[] nums1, int[] nums2) {
Arrays.sort(nums1);
Arrays.sort(nums2);
helper(nums1, nums2);
helper(nums2, nums1);
return res;
}
private void helper(int[] nums1, int[] nums2){
for(int k = 0; k < nums1.length; k++){
//先求下平方
long target = (long)nums1[k] * nums1[k];
int i = 0, j = nums2.length - 1;
while (i < j){
//双指针从头和从尾找
long temp = (long)nums2[i] * nums2[j];
//如果大于就减小右指针
if (temp > target){
j--;
//小于增大左指针
}else if (temp < target){
i++;
}else{
//如果是相等的情况,要特殊处理
if (nums2[i] == nums2[j]){
int count = j - i;
res += (1 + count) * count / 2;
break;
}
//否则找一下联系相同的个数
int n1 = 1, n2 = 1;
while (nums2[i] == nums2[i + 1]){
i++;
n1++;
}
while (nums2[j] == nums2[j - 1]){
j--;
n2++;
}
res += n1 * n2;
i++;
j--;
}
}
}
}
}
Leetcode1626 无矛盾的最佳球队
假设你是球队的经理。对于即将到来的锦标赛,你想组合一支总体得分最高的球队。球队的得分是球队中所有球员的分数 总和 。
然而,球队中的矛盾会限制球员的发挥,所以必须选出一支 没有矛盾 的球队。如果一名年龄较小球员的分数 严格大于 一名年龄较大的球员,则存在矛盾。同龄球员之间不会发生矛盾。
给你两个列表 scores 和 ages,其中每组 scores[i] 和 ages[i] 表示第 i 名球员的分数和年龄。请你返回 所有可能的无矛盾球队中得分最高那支的分数 。
先进行排序,然后在选择。因为是否要选择某一个人是不确定的,因此自然想到动态规划。实际上类似于最长上升子序列的题。
class Solution {
public int bestTeamScore(int[] scores, int[] ages) {
int n = scores.length;
int[][] help = new int[n][2];
for(int i = 0; i < n; i++){
help[i][0] = ages[i];
help[i][1] = scores[i];
}
//先用一个辅助数组进行排序,首先对年龄排序,然后对分数排序,这样就不用考虑是否会有矛盾的情况了
Arrays.sort(help, (x, y) -> (x[0] == y[0] ? x[1] - y[1] : x[0] - y[0]));
int[] dp = new int[n];
dp[0] = help[0][1];
int max = 0;
//然后dp,基本类似于最长上升子序列
for(int i = 0; i < n; i++){
dp[i] = help[i][1];
for(int j = 0; j < i; j++){
if (help[i][1] >= help[j][1]){
dp[i] = Math.max(dp[i], dp[j] + help[i][1]);
}
}
max = Math.max(max, dp[i]);
}
return max;
}
}
Leetcode1670 设计前中后队列
请你设计一个队列,支持在前,中,后三个位置的 push 和 pop 操作。
请你完成 FrontMiddleBack 类:
FrontMiddleBack() 初始化队列。
void pushFront(int val) 将 val 添加到队列的 最前面 。
void pushMiddle(int val) 将 val 添加到队列的 正中间 。
void pushBack(int val) 将 val 添加到队里的 最后面 。
int popFront() 将 最前面 的元素从队列中删除并返回值,如果删除之前队列为空,那么返回 -1 。
int popMiddle() 将 正中间 的元素从队列中删除并返回值,如果删除之前队列为空,那么返回 -1 。
int popBack() 将 最后面 的元素从队列中删除并返回值,如果删除之前队列为空,那么返回 -1 。
请注意当有 两个 中间位置的时候,选择靠前面的位置进行操作。比方说:
将 6 添加到 [1, 2, 3, 4, 5] 的中间位置,结果数组为 [1, 2, 6, 3, 4, 5] 。
从 [1, 2, 3, 4, 5, 6] 的中间位置弹出元素,返回 3 ,数组变为 [1, 2, 4, 5, 6] 。
看到这题想到了数据流中的中位数那道题,想法也差不多,用两个双向队列进行模拟。保证某一个队列只比另一个队列最多多一个元素就行了。
class FrontMiddleBackQueue {
LinkedList<Integer> front;
LinkedList<Integer> back;
int first;
int second;
public FrontMiddleBackQueue() {
front = new LinkedList<>();
back = new LinkedList<>();
}
public void pushFront(int val) {
front.addFirst(val);
first++;
balance();
}
public void pushMiddle(int val) {
if (first == second){
back.addFirst(val);
second++;
}else{
front.addLast(val);
first++;
}
}
public void pushBack(int val) {
back.addLast(val);
second++;
balance();
}
public int popFront() {
if (!front.isEmpty()){
int temp = front.removeFirst();
first--;
balance();
return temp;
}else if (!back.isEmpty()){
int temp = back.removeFirst();
second--;
balance();
return temp;
}
return -1;
}
public int popMiddle() {
if (first == 0 && second == 0) return -1;
if (first == second){
int temp = front.removeLast();
first--;
balance();
return temp;
}else{
int temp = back.removeFirst();
second--;
balance();
return temp;
}
}
public int popBack() {
if (!back.isEmpty()){
int temp = back.removeLast();
second--;
balance();
return temp;
}
return -1;
}
//balance用来调整队列里的元素数量。
private void balance(){
if (first > second){
back.addFirst(front.removeLast());
first--;
second++;
}
if (second > first + 1){
front.addLast(back.removeFirst());
second--;
first++;
}
}
}