力扣第 47 场双周赛题解

第一次打力扣的比赛,有点不习惯。最后一题一开始进入误区了,而且调试改bug改了将近1个小时,改完后才发现真正解法,结果已经只剩5分钟,然后写完时比赛已经结束了5分钟了。本来代码习惯就一般,再加上急了所以代码写的pang乱。
前三道题确实简单,所以就不写题解了。

找到最近的有相同X或Y坐标的点

给你两个整数 xy ,表示你在一个笛卡尔坐标系下的 (x, y) 处。同时,在同一个坐标系下给你一个数组 points ,其中 points[i] = [ai, bi] 表示在 (ai, bi) 处有一个点。当一个点与你所在的位置有相同的 x 坐标或者相同的 y 坐标时,我们称这个点是 有效的 。

请返回距离你当前位置 曼哈顿距离 最近的 有效 点的下标(下标从 0 开始)。如果有多个最近的有效点,请返回下标 最小 的一个。如果没有有效点,请返回 -1

两个点 (x1, y1)(x2, y2) 之间的 曼哈顿距离 为 abs(x1 - x2) + abs(y1 - y2)

1 <= points.length <= 104
points[i].length == 2
1 <= x, y, ai, bi <= 104

class Solution {
public:
    int nearestValidPoint(int x, int y, vector<vector<int>>& points) {
        int n = points.size();
        int ans = -1;
        for(int i = 0;i < n;i ++){
            if(points[i][0] == x || points[i][1] == y){
                if(ans == -1)
                	ans = i;
                else{
                    if((points[i][0] - x) * (points[i][0] - x) + (points[i][1] - y) * (points[i][1] - y) < (points[ans][0] - x) * (points[ans][0] - x) + (points[ans][1] - y) * (points[ans][1] - y))
                        ans = i;
                }
            }
        }
        return ans;
    }
};

判断一个数字是否可以表示成三的幂的和

给你一个整数 n ,如果你可以将 n 表示成若干个不同的三的幂之和,请你返回 true ,否则请返回 false

对于一个整数 y ,如果存在整数 x 满足 y == 3x ,我们称这个整数 y 是三的幂。

class Solution {
public:
    bool checkPowersOfThree(int n) {
        int ma = 1;
    while(ma<=n)ma*=3;
    ma/=3;
    while(n>0){
        if(n>=ma)
            n-=ma;
        if(n>=ma) return false;
        ma/=3;
    }
    return true;
    }
};

所有子字符串美丽值之和

一个字符串的 美丽值 定义为:出现频率最高字符与出现频率最低字符的出现次数之差。

比方说,abaacc 的美丽值为 3 - 1 = 2
给你一个字符串 s ,请你返回它所有子字符串的 美丽值 之和。

1 <= s.length <= 500
s 只包含小写英文字母。

class Solution {
public:
    int beautySum(string s) {
        int len = s.length();
	    int num[505][30] = {0};
	    for(int i = 1;i <= len;i ++){
	        for(int j = 0;j < 26;j ++)
	            num[i][j] = num[i-1][j];
	        num[i][s[i-1] - 'a'] ++;
	    }
	    int ans = 0;
	    for(int i = 1;i < len;i ++){
	        for(int j= i + 1;j <= len;j ++){
	            int ma = 0,mi = 0x3f3f3f3f;
	            for(int k = 0;k < 26;k ++){
	                if(num[j][k] == num[i-1][k])
	                	continue;
	                mi = min(mi,num[j][k] - num[i-1][k]);
	                ma = max(ma,num[j][k] - num[i-1][k]);
	            }
	            ans += ma - mi;
	        }
	    }
	    return ans;
    }
};

统计点对的数目

给你一个无向图,无向图由整数 n ,表示图中节点的数目,和 edges 组成,其中 edges[i] = [ui, vi]表示 uivi之间有一条无向边。同时给你一个代表查询的整数数组 queries

j 个查询的答案是满足如下条件的点对 (a, b) 的数目:

  • a < b
  • cnt 是与 a 或者 b 相连的边的数目,且 cnt 严格大于 queries[j] 。

请你返回一个数组 answers ,其中 answers.length == queries.length 且 answers[j] 是第 j个查询的答案。

请注意,图中可能会有 重复边 。

2 <= n <= 2 * 104
1 <= edges.length <= 105
1 <= ui, vi <= n
ui != vi
1 <= queries.length <= 20
0 <= queries[j] < edges.length

题解

通过观察题目可以发现本题要求我们算出有多少个满足下面条件的点对(a,b):

  • 与点a和点b这两个点中至少一个点相连接的边的数量大于querises[i]

也就是说,假设有n个点,那么我们要得出(1,2),(1,3)……(1,n)(2,3),(2,4)……(n-1,n)中有多少对满足上面的那个条件。
本题的难点在于当要判断(a,b)时,ab之间本身可能存在连线,但是我们不可能真的用 O(n^2) 的办法进行暴力计算。

根据题目给的数据范围,我们可以考虑从进行入手思考办法。

首先我们可以通过边,计算出每个点的度将其存入数组d中,使得d[i]则为点i的度。同时,我们为了得到一个根据度进行排序的数组,我们可以再创建一个数组来专门进行排序,得到从大到小的度。(代码中因为一开始用的不是这个方法,导致我写了个结构体排,后来换方法的时候也懒得改了 ,正好也方便我后面用map来进行标记)
我们这时候根据排序得到的度,可以在 O(n) 的时间复杂度内得到所有度相加大于querises[i]的点对数。具体实现方法如下,应该比较好理解:

int sum = 0;
int last = n;
for(int j = 1;j <= n;j ++){
    while(sort_d[j] + sort_d[last] <= z && last > j)
    	last --;
    if(last <= j)
    	break;
    sum += last - j;
}

此时得出的值当然不是最后的答案,根据分析我们可以发现,我们这时算出的答案中还存在一直情况,即d[a]+d[b]>z但是ab之间可能存在边,单纯的将这种情况的度相加,会导致ab之间相连的边计算的两次,而当d[a]+d[b]-num[a][b]<=z时, (a,b)应该是不能满足条件的 ,所有我们应该将这种情况去除。
我们应该遍历边,找出所有的d[a] + d[b] > z 并且d[a] + d[b] - num[a][b] <= z的点对数,用上一步得出的总数减去这部分即为最后的答案。(注意因为可能会出现重边,所以相同的点对要标记,不要多算了)
这里的num即是点对(a,b)之间的边的数量,当然不能用数组实现,应该采用map进行映射。

具体代码:

class Solution {
public:

struct nod{
    int num;
    int i;
    bool operator <(const nod& b)const{
        if(num!=b.num)
        return num>b.num;
        else return i>b.i;
    }
    bool operator >(const nod &b)const{
        if(num!=b.num)
        return num<b.num;
        else return i<b.i;
    }
    
};
vector<int> countPairs(int n, vector<vector<int>>& edges, vector<int>& queries) {
    nod ak[20005]={0};
    int dd[20005]={0};
    int m = edges.size();
    int q = queries.size();
    vector<int> ans;
    map< nod ,int > ma;
           
    for(int i = 1;i <= n;i ++)
        ak[i].i = i;
        
    for(int i = 0;i < m;i ++){
        ak[edges[i][0]].num ++;
        ak[edges[i][1]].num ++;
        dd[edges[i][0]] ++;
        dd[edges[i][1]] ++;
        nod temp;
        temp.i = edges[i][0];
        temp.num = edges[i][1];
        if(temp.i > temp.num)
        	swap(temp.i,temp.num);
        ma[temp] = ma[temp] + 1;
    }
    
    sort(ak+1,ak+n+1);
    
    for(int i=0;i<q;i++){
        int z = queries[i];
        int tans = 0;
        map< nod ,bool > sc;
        for(int j = 0;j < m;j ++){
            if(dd[edges[j][0]] + dd[edges[j][1]] > z){
                nod temp;
                temp.i = edges[j][0];
                temp.num = edges[j][1];
                if(temp.i > temp.num)
                	swap(temp.i,temp.num);
                if(dd[edges[j][0]]+dd[edges[j][1]]-ma[temp]<=z
                	&&!sc[temp]){
                    tans--;
                    sc[temp] = true;
                }
            }
        }
        int last = n;
        for(int j = 1;j <= n;j ++){
            while(ak[j].num + ak[last].num <= z
            	&&last>j)
            	last--;
            if(last <= j)
            	break;
            tans += last - j;
        }
        ans.push_back(tans);
    }
    return ans;
}
};
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
题目描述: 给出两个非空链表来表示两个非负整数。其中,它们各自的位数是按照逆序的方式存储的,并且它们的每个节点只能存储一位数字。 如果,我们将这两个数相加起来,则会返回一个新的链表来表示它们的和。 您可以假设除了数字 0 之外,这两个数都不会以零开头。 示例: 输入:(2 -> 4 -> 3) + (5 -> 6 -> 4) 输出:7 -> 0 -> 8 原因:342 + 465 = 807 解题思路: 题目中要求我们按照逆序的方式存储两个非负整数,也就是说链表的头节点表示该数的个位,第二个节点表示该数的十位,以此类推。 因此,我们可以从两个链表的头节点开始,对应位相加,并记录进位,将结果添加到新的链表中。如果有进位,需要在下一位相加时加上进位。 需要注意的是,当两个链表的长度不一致时,可以将较短的链表的缺失位看作是 0。 最后,如果最高位有进位,还需要在新链表的最高位添加一个值为 1 的节点。 C 语言代码实现: /** * Definition for singly-linked list. * struct ListNode { * int val; * struct ListNode *next; * }; */ struct ListNode* addTwoNumbers(struct ListNode* l1, struct ListNode* l2){ struct ListNode *head = NULL, *tail = NULL; int carry = 0; while (l1 || l2) { int n1 = l1 ? l1->val : 0; int n2 = l2 ? l2->val : 0; int sum = n1 + n2 + carry; if (!head) { head = tail = malloc(sizeof(struct ListNode)); tail->val = sum % 10; tail->next = NULL; } else { tail->next = malloc(sizeof(struct ListNode)); tail = tail->next; tail->val = sum % 10; tail->next = NULL; } carry = sum / 10; if (l1) l1 = l1->next; if (l2) l2 = l2->next; } if (carry > 0) { tail->next = malloc(sizeof(struct ListNode)); tail = tail->next; tail->val = carry; tail->next = NULL; } return head; }
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值