LeetCode第5场双周赛

第一次玩这个,听说比周赛简单点,这次一共四道题,发现自己图论是真不会。
后面两题,第三题是裸的最小生成树,第四题是裸的拓扑排序。

  1. 最大唯一数 显示英文描述
    用户通过次数 302
    用户尝试次数 313
    通过次数 307
    提交次数 525
    题目难度 Easy
    给你一个整数数组 A,请找出并返回在该数组中仅出现一次的最大整数。

如果不存在这个只出现一次的整数,则返回 -1。

示例 1:

输入:[5,7,3,9,4,9,8,3,1]
输出:8
解释: 
数组中最大的整数是 9,但它在数组中重复出现了。而第二大的整数是 8,它只出现了一次,所以答案是 8。

示例 2:

输入:[9,9,8,8]
输出:-1
解释: 
数组中不存在仅出现一次的整数。

提示:

1 <= A.length <= 2000
0 <= A[i] <= 1000

分析:
这题有两种思路比较常见,我已开始用排序做…结果错了好几次,后面干脆用hash表读一下算了。
解法一:
排序后遍历

class Solution {
public:
    int largestUniqueNumber(vector<int>& A) {
        sort(A.begin(), A.end());
        if(A.size() == 1) {
            return A[0];
        }
        for(int i = A.size()-1; i >= 0; i--) {
            if(i < A.size() -1 && A[i] == A[i+1]) {
                continue;
            }
            if(i == 0 || A[i] != A[i-1]) {
                return A[i];
            }
        }
        return -1;
    }
};

解法二:
hash

class Solution {
public:
    int largestUniqueNumber(vector<int>& A) {
        unordered_map<int, int> map;
        for(int i = 0; i < A.size(); i++) {
            map[A[i]]++;
        }
        for(int i = 1000; i >=0; i--) {
            if(map[i] == 1) {
                return i;
            }
        }
        return -1;
    }
};
  1. 阿姆斯特朗数 显示英文描述
    用户通过次数 301
    用户尝试次数 304
    通过次数 306
    提交次数 388
    题目难度 Easy
    假设存在一个 k 位数 N,其每一位上的数字的 k 次幂的总和也是 N,那么这个数是阿姆斯特朗数。

给你一个正整数 N,让你来判定他是否是阿姆斯特朗数,是则返回 true,不是则返回 false。

示例 1:

输入:153
输出:true
示例: 
153 是一个 3 位数,且 153 = 1^3 + 5^3 + 3^3。

示例 2:

输入:123
输出:false
解释: 
123 是一个 3 位数,且 123 != 1^3 + 2^3 + 3^3 = 36。

提示:

1 <= N <= 10^8

class Solution {
public:
    bool isArmstrong(int N) {
        int num = 0;
        int temp1 = N;
        while(N) {
            N = N / 10;
            num++;
        }
        N = temp1;
        int temp = 0, sum = 0;
        while(N) {
            temp = N % 10;
            sum += pow(temp, num);
            N = N / 10;
        }
        return sum == temp1;
    }
};
  1. 最低成本联通所有城市 显示英文描述
    用户通过次数 79
    用户尝试次数 148
    通过次数 87
    提交次数 535
    题目难度 Medium
    想象一下你是个城市基建规划者,地图上有 N 座城市,它们按以 1 到 N 的次序编号。

给你一些可连接的选项 conections,其中每个选项 conections[i] = [city1, city2, cost] 表示将城市 city1 和城市 city2 连接所要的成本。(连接是双向的,也就是说城市 city1 和城市 city2 相连也同样意味着城市 city2 和城市 city1 相连)。

返回使得每对城市间都存在将它们连接在一起的连通路径(可能长度为 1 的)最小成本。该最小成本应该是所用全部连接代价的综合。如果根据已知条件无法完成该项任务,则请你返回 -1。

示例 1:

alt

输入:N = 3, conections = [[1,2,5],[1,3,6],[2,3,1]]
输出:6
解释:
选出任意 2 条边都可以连接所有城市,我们从中选取成本最小的 2 条。

示例 2:

alt

输入:N = 4, conections = [[1,2,3],[3,4,4]]
输出:-1
解释: 
即使连通所有的边,也无法连接所有城市。

提示:

1 <= N <= 10000
1 <= conections.length <= 10000
1 <= conections[i][0], conections[i][1] <= N
0 <= conections[i][2] <= 10^5
conections[i][0] != conections[i][1]

分析:
明确一下方向,求最小生成树一共有两种算法:prim算法和Kruskal算法。
prim算法适合稠密图,时间复杂度为O(n^2),n为结点数
Kruskal算法适合稀疏图,时间复杂度为O(eloge),e为边数
上面的题目属于稀疏图,选择Kruskal算法。

//并查集可以通过查询两个结点所在集合的根结点是否相同来判断他们是否在同一个集合
//而只要把测试边的两个端点所在集合合并,就能达到将边加入最小生成树的效果
class UnionSet{
public:
    vector<int> father;
    UnionSet(int n) {
        father.resize(n+1);
        for(int i = 1; i <= n; i++) {
            father[i] = i;
        }
    }
    
    int find(int x) {
        while(father[x] != x) {
            x = father[x];
        }
        return x;
    }
    
    void insert(int x, int y) {
        father[find(x)] = y;
    }
    
    // int count() {
    //     int cnt = 0;
    //     for(int i = 1; i <= father.size(); i++) {
    //         if(father[i] == i) {
    //             cnt++;
    //         }
    //     }
    //     return cnt;
    // }
};
class Solution {
public:
    //const static int maxv = 10002;
    static bool cmp(vector<int>& e1, vector<int>& e2) {
        return e1[2] < e2[2];
    }
    int minimumCost(int N, vector<vector<int>>& conections) {
        UnionSet un = UnionSet(N);
        //对所有边按边权从小到大排序
        sort(conections.begin(), conections.end(), cmp);
        int sum = 0;
        int cnt = 0;
        for(int i = 0; i < conections.size(); i++) {
        //按边权从小到大测试所有边,如果当前测试边所连接的两个顶点不在同一个连通块中,则把这条测试边加入到当前最小生成树中;否则,将边舍弃
            if(un.find(conections[i][0]) != un.find(conections[i][1])) {
                un.insert(conections[i][0], conections[i][1]);
                cnt++;
                sum += conections[i][2];
            }
        }
        //直到最小生成树中的边数等于总顶点数减1或是测试完所有边时结束
        if(cnt == N - 1) {
            return sum;
        } else {
            return -1;
        }
    }
};
  1. 平行课程 显示英文描述
    用户通过次数 101
    用户尝试次数 127
    通过次数 104
    提交次数 221
    题目难度 Hard
    已知有 N 门课程,它们以 1 到 N 进行编号。

给你一份课程关系表 relations[i] = [X, Y],用以表示课程 X 和课程 Y 之间的先修关系:课程 X 必须在课程 Y 之前修完。

假设在一个学期里,你可以学习任何数量的课程,但前提是你已经学习了将要学习的这些课程的所有先修课程。

请你返回学完全部课程所需的最少学期数。

如果没有办法做到学完全部这些课程的话,就返回 -1。

示例 1:

alt

输入:N = 3, relations = [[1,3],[2,3]]
输出:2
解释:
在第一个学期学习课程 1 和 2,在第二个学期学习课程 3。

示例 2:

alt

输入:N = 3, relations = [[1,2],[2,3],[3,1]]
输出:-1
解释:
没有课程可以学习,因为它们相互依赖。

提示:

1 <= N <= 5000
1 <= relations.length <= 5000
relations[i][0] != relations[i][1]
输入中没有重复的关系

思路:
求解拓扑序列:

  1. 定义一个队列q,并把所有入度为0的结点加入队列
  2. 取队首结点,输出。然后删去所有从它出发的边,并令这些边到达的顶点的入度减1,如果某个顶点的入度减为0,则将其加入队列。
  3. 反复进行2操作,直到队列为空。如果队列为空时入队的结点数目恰好为n,说明拓扑排序成功,图G为有向无环图;
    否则,拓扑排序失败,图G中有环。
class Solution {
private:
    int const static maxv = 5001;
public:
    int minimumSemesters(int N, vector<vector<int>>& relations) {
        int num = 0;
        queue<int> q;
        int inD[maxv];
        vector<int> edge[maxv];
        memset(inD, 0, sizeof(inD));
        for(int i = 0; i < relations.size(); i++) {
            inD[relations[i][1]]++;
            edge[relations[i][0]].push_back(relations[i][1]);
        }
        int b[maxv];
        int res = 0;
        memset(b, 0, sizeof(b));
        for(int i = 1; i <= N; i++) {
            if(inD[i] == 0) {
                q.push(i);
                b[i] = 1;
                res = 1;
                num++;
            }
        }
        while(!q.empty()) {
            int u = q.front();
            q.pop();
            for(int i = 0; i < edge[u].size(); i++) {
                int v = edge[u][i];
                inD[v]--;
                if(inD[v] == 0) {
                    q.push(v);
                    b[v] = b[u] + 1;
                    res = b[u] + 1;
                    num++;
                }
            }
        }
        if(num == N) {
            return res;
        } else {
            return -1;
        }
    }
};
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值