【leetcode】图---中等(1)332. 重新安排行程_欧拉通路_dfs(2)399. 除法求值_dfs,pre,有环图(3)684. 冗余连接_DEADLYSIGNAL_并查集

332、给定一个机票的字符串二维数组 [from, to],子数组中的两个成员分别表示飞机出发和降落的机场地点,对该行程进行重新规划排序。所有这些机票都属于一个从JFK(肯尼迪国际机场)出发的先生,所以该行程必须从 JFK 出发。

说明:

    如果存在多种有效的行程,你可以按字符自然排序返回最小的行程组合。例如,行程 ["JFK", "LGA"] 与 ["JFK", "LGB"] 相比就更小,排序更靠前
    所有的机场都用三个大写字母表示(机场代码)。
    假定所有机票至少存在一种合理的行程。

示例 1:

输入: [["MUC", "LHR"], ["JFK", "MUC"], ["SFO", "SJC"], ["LHR", "SFO"]]
输出: ["JFK", "MUC", "LHR", "SFO", "SJC"]

示例 2:

输入: [["JFK","SFO"],["JFK","ATL"],["SFO","ATL"],["ATL","JFK"],["ATL","SFO"]]
输出: ["JFK","ATL","JFK","SFO","ATL","SFO"]
解释: 另一种有效的行程是 ["JFK","SFO","ATL","JFK","ATL","SFO"]。但是它自然排序更大更靠后。

 dfs,带string的有向图,对每条边进行遍历,cnt记录已经遍历了几条边,当遍历所有边时结束dfs。

两个map分别映射str->int,int->str。图用set存边信息,为了使得边按照字典序排。

超时,80 / 80 个通过测试用例,不知道为啥没有返回给我数据...

const int MAX = 1e6;
typedef pair<string,int> E; // {点id,边id}

class Solution {
public:
    set<E> gra[MAX];        //   行程的图
    map<string, int> mp;    // 名称和id的映射,{"JFK", int}
    map<int, string> mp2;
    vector<bool> mark;      //对边进行标记,是否i访问
    vector<string> ans;
    int edge_num;     
    bool dfs(int s, int cnt){
        if(cnt == edge_num) return true;
        for(auto &i:gra[s]){
           if(!mark[i.second]){
                mark[i.second] = true;
                ans.push_back(i.first);
                if(dfs(mp[i.first], cnt + 1)) return true;
                ans.pop_back();
                mark[i.second] = false;
           }
        }
        return false;
    }
    vector<string> findItinerary(vector<vector<string>>& tickets) {
        int n = 0;    // 总地点数
        edge_num = tickets.size();
        mp.clear();
        mp2.clear();
        for(int i = 0; i < edge_num; i++){
            if(!mp.count(tickets[i][0])) {mp[tickets[i][0]] = n; mp2[n++] = tickets[i][0];}
            if(!mp.count(tickets[i][1])) {mp[tickets[i][1]] = n; mp2[n++] = tickets[i][1];}
            gra[mp[tickets[i][0]]].insert(E(tickets[i][1], i));
        }
        mark.resize(edge_num, false);
        ans.clear();
        ans.push_back("JFK");
        dfs(mp["JFK"], 0);
        return ans;
    }
};

修改,把存图的数组改为unordered_map,就不超时啦

const int MAX = 1e6;
typedef pair<string,int> E; // {点id,边id}
class Solution {
public:
    unordered_map<string, set<E>> gra;
    vector<bool> mark;      //对边进行标记,是否i访问
    vector<string> ans;
    int edge_num;     
    bool dfs(string s, int cnt){
        if(cnt == edge_num) return true;
        for(auto &i:gra[s]){
           if(!mark[i.second]){
                mark[i.second] = true;
                ans.push_back(i.first);
                if(dfs(i.first, cnt + 1)) return true;
                ans.pop_back();
                mark[i.second] = false;
           }
        }
        return false;
    }
    vector<string> findItinerary(vector<vector<string>>& tickets) {
        int n = 0;    // 总地点数
        edge_num = tickets.size();
        mark.resize(edge_num, false);
        for(int i = 0; i < edge_num; i++) gra[tickets[i][0]].insert(E(tickets[i][1], i));
        ans.clear();
        ans.push_back("JFK");
        dfs("JFK", 0);
        return ans;
    }
};

结果:

执行用时:56 ms, 在所有 C++ 提交中击败了59.34% 的用户

内存消耗:14.8 MB, 在所有 C++ 提交中击败了100.00% 的用户

 

补充知识:欧拉路径问题

  1. 欧拉回路:图G的一个回路,如果恰通过图G的每一条边,则该回路称为欧拉回路,具有欧拉回路的图称为欧拉图。欧拉图就是从图上的一点出发,经过所有边且只能经过一次,最终回到起点的路径
  2. 欧拉通路:即可以不回到起点,但是必须经过每一条边,且只能一次。也叫"一笔画"问题。
  3. 基图:基图是针对有向图的说法,是忽略有向图的方向得到的无向图。
  4. 欧拉图:存在欧拉回路的图。

定理一:无向图G为欧拉图,当且仅当G为连通图且所有顶点的度为偶数。

定理二:有向图G为欧拉图,当且仅当G的基图连通,且所有顶点的入度等于出度。

 

339、给出方程式 A / B = k, 其中 A 和 B 均为用字符串表示的变量, k 是一个浮点型数字。根据已知方程式求解问题,并返回计算结果。如果结果不存在,则返回 -1.0。

示例 :
给定 a / b = 2.0, b / c = 3.0
问题:  a / c = ?, b / a = ?, a / e = ?, a / a = ?, x / x = ? 
返回 [6.0, 0.5, -1.0, 1.0, -1.0 ]

输入为:  vector<pair<string, string>> equations, vector<double>& values, vector<pair<string, string>> queries(方程式,方程式结果,问题方程式), 其中 equations.size() == values.size(),即方程式的长度与方程式结果长度相等(程式与结果一一对应),并且结果值均为正数。以上为方程式的描述。 返回vector<double>类型。

基于上述例子,输入如下:

equations(方程式) = [ ["a", "b"], ["b", "c"] ],
values(方程式结果) = [2.0, 3.0],
queries(问题方程式) = [ ["a", "c"], ["b", "a"], ["a", "e"], ["a", "a"], ["x", "x"] ]. 

输入总是有效的。你可以假设除法运算中不会出现除数为0的情况,且不存在任何矛盾的结果。

unordered_map记录含string的图关系,dfs,有环图可以dfs时记录父亲节点pre。 

// a / b ==》 a->b的有向边权重为2,b->a的有向边权重为1/2.
class Solution {
public:
    unordered_map<string, unordered_map<string, double>> gra;
    double res;
    bool dfs(string a, string pre, string ends, double r){ // 注意因为图中有环,这里加了pre点,记录父亲节点,避免循环dfs。
        for(auto &i:gra[a]){
            if(i.first == ends) {res = r * i.second; return true;}
            if(i.first != pre && dfs(i.first, a, ends, r * i.second)) return true;
        }
        return false;
    }
    vector<double> calcEquation(vector<vector<string>>& equations, 
                                vector<double>& values, 
                                vector<vector<string>>& queries) {
        for(int i = 0; i < equations.size(); i++){
            gra[equations[i][0]].insert(make_pair(equations[i][1], values[i]));
            gra[equations[i][1]].insert(make_pair(equations[i][0], 1 / values[i]));
        }
        vector<double> ans;
        for(auto &vec:queries){
            double result = -1.0;
            if( gra.count(vec[0]) && gra.count(vec[1])){
                if(vec[0] == vec[1]) result = 1.0;
                else if(dfs(vec[0], "", vec[1], 1.0)) result = res;
            }
            ans.push_back(result);
        }
        return ans;
    }
};

结果:

执行用时:4 ms, 在所有 C++ 提交中击败了46.65% 的用户

内存消耗:7.5 MB, 在所有 C++ 提交中击败了100.00% 的用户

 

684、在本问题中, 树指的是一个连通且无环的无向图。

输入一个图,该图由一个有着N个节点 (节点值不重复1, 2, ..., N) 的树及一条附加的边构成。附加的边的两个顶点包含在1到N中间,这条附加的边不属于树中已存在的边。

结果图是一个以边组成的二维数组。每一个边的元素是一对[u, v] ,满足 u < v,表示连接顶点u 和v的无向图的边。

返回一条可以删去的边,使得结果图是一个有着N个节点的树。如果有多个答案,则返回二维数组中最后出现的边。答案边 [u, v] 应满足相同的格式 u < v。

示例 1:

输入: [[1,2], [1,3], [2,3]]
输出: [2,3]
解释: 给定的无向图为:
  1
 / \
2 - 3

示例 2:

输入: [[1,2], [2,3], [3,4], [1,4], [1,5]]
输出: [1,4]
解释: 给定的无向图为:
5 - 1 - 2
    |   |
    4 - 3

注意:

    输入的二维数组大小在 3 到 1000。
    二维数组中的整数在1到N之间,其中N是输入数组的大小。

注意 leetcode报错:

AddressSanitizer:DEADLYSIGNAL
=================================================================
==45==ERROR: AddressSanitizer: stack-overflow on address 0x7fffd4a8dff8 (pc 0x0000003912ba bp 0x7fffd4a8e020 sp 0x7fffd4a8e000 T0)
==45==ABORTING

意味着:死循环

并查集,遍历edges,每加入一边,使得两个不同并查集的点合并,若属于同集合,则必定有环,该边可以删除。

// 并查集
// 遍历edges,每加入一边,使得两个不同并查集的点合并,若属于同集合,则必定有环,该边可以删除。
const int MAX = 1e3+10;
class Solution {
public:
    vector<int> tree; 
    void init(int n){ // 并查集初始化,自成一树
        for(int i = 0; i <= n; i++)
            tree.push_back(i);
    }
    int findroot(int x){
        if(tree[x] == x) return x;
        else{
            int t = findroot(tree[x]);
            tree[x] = t;
            return t;
        }
    }
    vector<int> findRedundantConnection(vector<vector<int>>& edges) {
        int n = edges.size();
        init(n); 
        int id, x, y;
        for(int i = 0; i < n; i++){
            x = edges[i][0];
            y = edges[i][1];
            x = findroot(x);
            y = findroot(y);
            if(x != y) tree[x] = y;
            else id = i;
        }
        return edges[id];
    }
};

结果:

执行用时:12 ms, 在所有 C++ 提交中击败了71.37% 的用户

内存消耗:8.3 MB, 在所有 C++ 提交中击败了100.00% 的用户

 

 

 

 

 

 

 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值