【Leetcode Sheet】Weekly Practice 14

Leetcode Test

2003 每棵子树内缺失的最小基因值(10.31)

有一棵根节点为 0家族树 ,总共包含 n 个节点,节点编号为 0n - 1 。给你一个下标从 0 开始的整数数组 parents ,其中 parents[i] 是节点 i 的父节点。由于节点 0 ,所以 parents[0] == -1

总共有 105 个基因值,每个基因值都用 闭区间 [1, 105] 中的一个整数表示。给你一个下标从 0 开始的整数数组 nums ,其中 nums[i] 是节点 i 的基因值,且基因值 互不相同

请你返回一个数组 ans ,长度为 n ,其中 ans[i] 是以节点 i 为根的子树内 缺失最小 基因值。

节点 x 为根的 子树 包含节点 x 和它所有的 后代 节点。

提示:

  • n == parents.length == nums.length
  • 2 <= n <= 105
  • 对于 i != 0 ,满足 0 <= parents[i] <= n - 1
  • parents[0] == -1
  • parents 表示一棵合法的树。
  • 1 <= nums[i] <= 105
  • nums[i] 互不相同。

【DFS】

class Solution {
public:
    vector<int> smallestMissingValueSubtree(vector<int>& parents, vector<int>& nums) {
        int n=parents.size();
        vector<int> ans(n,1);
        auto it=find(nums.begin(),nums.end(),1);
        if(it==nums.end()){
            //不存在基因值为1的点
            return ans;
        }

        //建树
        vector<vector<int>> g(n);
        for(int i=1;i<n;i++){
            g[parents[i]].push_back(i);
        }
        
        unordered_set<int> vis;
        function<void(int)> dfs=[&](int x)->void{
            //标记基因值
            vis.insert(nums[x]);
            
            for(int son: g[x]){
                if(!vis.count(nums[son])){
                    dfs(son);
                }
            }
        };

        //缺失的最小基因值
        int mex=2;
        int node=it-nums.begin();
        while(node>=0){
            dfs(node);
            while(vis.count(mex)){
                //node子树包含这个基因值
                mex++;
            }
            //缺失的最小基因值
            ans[node]=mex;
            //上行
            node=parents[node];
        }
        return ans;
    }
};

2127 参加会议的最多员工数(11.1)

一个公司准备组织一场会议,邀请名单上有 n 位员工。公司准备了一张 圆形 的桌子,可以坐下 任意数目 的员工。

员工编号为 0n - 1 。每位员工都有一位 喜欢 的员工,每位员工 当且仅当 他被安排在喜欢员工的旁边,他才会参加会议。每位员工喜欢的员工 不会 是他自己。

给你一个下标从 0 开始的整数数组 favorite ,其中 favorite[i] 表示第 i 位员工喜欢的员工。请你返回参加会议的 最多员工数目

提示:

  • n == favorite.length
  • 2 <= n <= 105
  • 0 <= favorite[i] <= n - 1
  • favorite[i] != i

【拓扑】

int maximumInvitations(int* favorite, int favoriteSize){
    int n=favoriteSize;

    //构建入度
    int indeg[n];
    memset(indeg,0,sizeof(indeg));
    for(int i=0;i<n;i++){
        indeg[favorite[i]]++;
    }

    //used:是否访问过
    //f[i] 表示到节点 i 为止的最长「游走」路径经过的节点个数
    int used[n],f[n];
    memset(used,0,sizeof(used));
    for (int i=0;i<n;i++) {
        f[i]=1;
    }

    //q:统计没有人被喜欢的人次?
    int q[n];
    int head=0,tail=0;
    for(int i=0;i<n;i++){
        if(!indeg[i]){
            //如果这个人没人喜欢,把这个人push到q里面
            q[tail++]=i;
        }
    }

    //dp,求解「基环内向树」上的最长的「双向游走」路径
    while(head!=tail){
        int u=q[head++];    //u是第head个没人喜欢的人
        used[u]=1;          //访问过u了
        int v=favorite[u];  //v是u喜欢的人
        //状态转移
        f[v]=fmax(f[v],f[u]+1);
        indeg[v]--;         //v的入度减去这个u
        if(!indeg[v]){      //如果v也变成没人喜欢的点了
            q[tail++]=v;    //把v点push到q里面
        }
    }

    //ring最大环的大小,total所有环长度为2的最长链
    int ring=0,total=0;
    for(int i=0;i<n;i++){
        //如果没有访问过i
        if(!used[i]){
            //j是i喜欢的人
            int j=favorite[i];
            //环的大小为2,即i也是j喜欢的人
            if(favorite[j]==i){
                total+=f[i]+f[j];   //统计i和j处的最长链
                used[i]=used[j]=1;  //访问过i和j了
            }
            //环的大小至少为3
            else{
                int u=i,cnt=0;      //u是i,增加计数变量
                while(1){
                    cnt++;          //cnt自增
                    u=favorite[u];  //更新u为u喜欢的对象
                    used[u]=1;      //访问过上一个u喜欢的对象了
                    if(u==i){       //如果回到最开始的起点了
                        break;      //退出循环
                    }
                }
                ring=fmax(ring,cnt);    //更新最大环的大小
            }
        }
    }
    //等价于求出图的最大环的长度,以及所有长度为 2 的环加上其最长链,返回二者的最大值
    return fmax(ring,total);
}

2103 环和杆(11.2)

总计有 n 个环,环的颜色可以是红、绿、蓝中的一种。这些环分别穿在 10 根编号为 09 的杆上。

给你一个长度为 2n 的字符串 rings ,表示这 n 个环在杆上的分布。rings 中每两个字符形成一个 颜色位置对 ,用于描述每个环:

  • i 对中的 第一个 字符表示第 i 个环的 颜色'R''G''B')。
  • i 对中的 第二个 字符表示第 i 个环的 位置,也就是位于哪根杆上('0''9')。

例如,"R3G2B1" 表示:共有 n == 3 个环,红色的环在编号为 3 的杆上,绿色的环在编号为 2 的杆上,蓝色的环在编号为 1 的杆上。

找出所有集齐 全部三种颜色 环的杆,并返回这种杆的数量。

提示:

  • rings.length == 2 * n
  • 1 <= n <= 100
  • i偶数 ,则 rings[i] 的值可以取 'R''G''B'(下标从 0 开始计数)
  • i奇数 ,则 rings[i] 的值可以取 '0''9' 中的一个数字(下标从 0 开始计数)

【模拟】

int countPoints(char * rings){
    int len=strlen(rings),cnt=0;
    int *r=malloc(sizeof(int)*10);
    int *g=malloc(sizeof(int)*10);
    int *b=malloc(sizeof(int)*10);
    for(int i=0;i<10;i++){
        r[i]=0;
        g[i]=0;
        b[i]=0;
    }
    int i=len-1;
    while(i>0){
        int j=i-1;//show color
        if(rings[j]=='R'){
            r[rings[i]-'0']=1;
        }
        else if(rings[j]=='G'){
            g[rings[i]-'0']=1;
        }
        else{
            b[rings[i]-'0']=1;
        }
        i-=2;
    }
    for(int i=0;i<10;i++){
        if(r[i]==1 && g[i]==1 && b[i]==1){
            cnt++;
        }
    }
    return cnt;
}

117 填充每个节点的下一个右侧节点指针Ⅱ(11.3)

给定一个二叉树:

struct Node {
  int val;
  Node *left;
  Node *right;
  Node *next;
}

填充它的每个 next 指针,让这个指针指向其下一个右侧节点。如果找不到下一个右侧节点,则将 next 指针设置为 NULL

初始状态下,所有 next 指针都被设置为 NULL

提示:

  • 树中的节点数在范围 [0, 6000]
  • -100 <= Node.val <= 100

进阶:

  • 你只能使用常量级额外空间。
  • 使用递归解题也符合要求,本题中递归程序的隐式栈空间不计入额外空间复杂度。

【层序遍历】

/**
 * Definition for a Node.
 * struct Node {
 *     int val;
 *     struct Node *left;
 *     struct Node *right;
 *     struct Node *next;
 * };
 */

struct Node* connect(struct Node* root) {
	if(!root) return NULL;
    struct Node *q[10001];
    int left=0,right=0;
    q[right++]=root;
    while(left<right){
        int n=right-left;
        struct Node *last=NULL;
        for(int i=1;i<=n;i++){
            struct Node *f=q[left++];
            if(f->left){
                q[right++]=f->left;
            }
            if(f->right){
                q[right++]=f->right;
            }
            if(i!=1){
                last->next=f;
            }
            last=f;
        }
    }
    return root;
}

421 数组中两个数的最大异或值(11.4)

给你一个整数数组 nums ,返回 nums[i] XOR nums[j] 的最大运算结果,其中 0 ≤ i ≤ j < n

提示:

  • 1 <= nums.length <= 2 * 105
  • 0 <= nums[i] <= 231 - 1

【hash】421. 数组中两个数的最大异或值 - 力扣(LeetCode)

class Solution {
    static constexpr int HIGH_BIT=30;
public:
    int findMaximumXOR(vector<int>& nums) {
        int x=0;
        for(int k=HIGH_BIT;k>=0;k--){
            unordered_set<int> seen;
            for(int num:nums){
                seen.insert(num>>k);
                //保留从最高位开始到第k个二进制位为止的部分
            }
            int x_next=2*x+1;
            bool found=0;

            //枚举
            for(int num:nums){
                if(seen.count(x_next ^ (num>>k))){
                    found=1;
                    break;
                }
            }

            if(found){
                x=x_next;
            }
            else{
                x=x_next-1;
            }
        }
        return x;
    }
};

187 重复的DNA序列(11.5)

DNA序列 由一系列核苷酸组成,缩写为 'A', 'C', 'G''T'.。

  • 例如,"ACGAATTCCG" 是一个 DNA序列

在研究 DNA 时,识别 DNA 中的重复序列非常有用。

给定一个表示 DNA序列 的字符串 s ,返回所有在 DNA 分子中出现不止一次的 长度为 10 的序列(子字符串)。你可以按 任意顺序 返回答案。

提示:

  • 0 <= s.length <= 105
  • s[i]``==``'A''C''G' or 'T'

【hash】把每个长度为10的substring都push到hash表里面,看计数>1的substring个数

class Solution {
    const int L=10;
public:
    vector<string> findRepeatedDnaSequences(string s) {
        vector<string> ans;
        unordered_map<string,int> cnt;
        int n=s.length();
        for(int i=0;i<=n-L;i++){
            string sub=s.substr(i,L);
            if(++cnt[sub]==2){
                ans.push_back(sub);
            }
        }
        return ans;
    }
};

【hash + 滑动窗口 + 位运算】同官解

/*
A:00
C:01
G:10
T:11
*/
class Solution {
    const int L = 10;
    //定义substring的长度
    unordered_map<char, int> bin = {{'A', 0}, {'C', 1}, {'G', 2}, {'T', 3}};
    //映射字母和对应编号
    
public:
    vector<string> findRepeatedDnaSequences(string s) {
        vector<string> ans;
        int n = s.length();
        if (n <= L) {
            return ans;
            //如果string就不够10个字符,则自动返回
        }
        
        int x = 0;
        //当前窗口对应的整数
        for (int i = 0; i < L - 1; ++i) {
            x = (x << 2) | bin[s[i]];
            //计算第一个窗口
        }
        
        unordered_map<int, int> cnt;
        for (int i = 0; i <= n - L; ++i) {
            x = ((x << 2) | bin[s[i + L - 1]]) & ((1 << (L * 2)) - 1);
            //x<<2,左移2位,以开辟一个新的字符空间
            //一个新字符进入,x=x|bin[t],bin[t]为计算字符t的二进制
            //高2位离开x,即低20位保留,高位置0,x=x&((1 << (L * 2)) - 1)
            
            if (++cnt[x] == 2) {
                ans.push_back(s.substr(i, L));
            }
        }
        return ans;
    }
};

318 最大单词长度乘积(11.6)

给你一个字符串数组 words ,找出并返回 length(words[i]) * length(words[j]) 的最大值,并且这两个单词不含有公共字母。如果不存在这样的两个单词,返回 0

提示:

  • 2 <= words.length <= 1000
  • 1 <= words[i].length <= 1000
  • words[i] 仅包含小写字母

【str库函数】(偷懒)

int maxProduct(char ** words, int wordsSize){
    int res = 0;
    for (int i = 0; i < wordsSize; i++) {
        for (int j = i; j < wordsSize; j++) {
            if (strpbrk(words[i], words[j]) == NULL) {
                res = fmax(res, strlen(words[i]) * strlen(words[j]));
            }
        }
    }
    return res;
}

cpp可用位运算(不会orz)

你可能感兴趣的:(Leetcode,Series,leetcode,算法,数据结构)