仅当集合为空时才弹出值的直觉

Posted

技术标签:

【中文标题】仅当集合为空时才弹出值的直觉【英文标题】:Intuition behind popping the values only when the set is empty 【发布时间】:2018-02-20 00:28:36 【问题描述】:

我正在解决 Leetcode 上的一个问题:https://leetcode.com/problems/reconstruct-itinerary/description/。问题是:

Given a list of airline tickets represented by pairs of departure and 
arrival airports [from, to], reconstruct the itinerary in order. All of 
the tickets belong to a man who departs from JFK. Thus, the itinerary 
must begin with JFK.

例如,如果tickets = [["MUC", "LHR"], ["JFK", "MUC"], ["SFO", "SJC"], ["LHR", "SFO"]],则输出应为:["JFK", "MUC", "LHR", "SFO", "SJC"]

我编写了以下代码,这(可以理解)在输入 [["JFK","KUL"],["JFK","NRT"],["NRT","JFK"]] 上中断,因为根据我的代码,节点“NRT”仍未被访问:

class Solution 
public:
    vector<string> findItinerary(vector<pair<string, string>> tickets) 
        if(tickets.empty()) return vector<string>();

        vector<string> result;
        unordered_map<string, multiset<string>> itinerary;

        for(auto& each : tickets)
            itinerary[each.first].insert(each.second);

        stack<string> myStack;
        myStack.push("JFK");
        while(!myStack.empty()) 
            string topVal=myStack.top();
            result.push_back(topVal);
            myStack.pop();
            if(!itinerary[topVal].empty()) 
                myStack.push(*itinerary[topVal].begin());
                itinerary[topVal].erase(itinerary[topVal].begin());
            
        

        return result;
    
;

为了克服这个问题,其中一个被赞成的解决方案提出了这个小改动:

class Solution 
public:
    vector<string> findItinerary(vector<pair<string, string>> tickets) 
        if(tickets.empty()) return vector<string>();

        vector<string> result;
        unordered_map<string, multiset<string>> itinerary;

        for(auto& each : tickets)
            itinerary[each.first].insert(each.second);

        stack<string> myStack;
        myStack.push("JFK");
        while(!myStack.empty()) 
            string topVal=myStack.top();
            if(itinerary[topVal].empty())    //--->this if condition
                result.push_back(topVal);
                myStack.pop();
            
            else 
                myStack.push(*itinerary[topVal].begin());
                itinerary[topVal].erase(itinerary[topVal].begin());
            
        

        reverse(result.begin(), result.end());
        return result;
    
;

现在,我使用示例 [["JFK","KUL"],["JFK","NRT"],["NRT","JFK"]] 编写此代码,并查看它如何以相反的方式将值插入到 result 向量中;但我无法理解 if 条件背后的直觉

如何只在集合为空时才从栈中弹出,确保这个测试用例得到照顾?

【问题讨论】:

【参考方案1】:

问题本质上是在有向图中找到一个Eulerian path,其中每个 [from, to] 对都代表一条边。

赞成的答案使用称为Hierholzer's algorithm 的算法(Hierholzer 的算法最初用于查找欧拉循环,但很容易将其修改为欧拉路径)。一般情况下

继续跟踪未使用的边缘并移除它们,直到我们被卡住。 一旦卡住,我们会回溯到当前路径中最近的具有未使用边的顶点,并重复该过程,直到所有边都被使用。

强调的部分是您的解决方案与赞成的解决方案之间的区别。

附:虽然算法很简单,但正确性的证明并不是那么简单。有兴趣的可以在网上搜索一下。

【讨论】:

【参考方案2】:

访问to城市后,它被删除,这实际上意味着to城市作为中间from城市已经被访问过,下次访问相应的from城市时不需要考虑,否则它将是一个没有停止条件的永无止境的循环。

因此,if 语句是检查 from 城市的所有 to 城市是否已经访问过的地方。它几乎就像一个访问过的数组,跟踪到目前为止访问过的所有城市。

【讨论】:

以上是关于仅当集合为空时才弹出值的直觉的主要内容,如果未能解决你的问题,请参考以下文章

MSSQL - 仅当所有值都不为空时才插入值

JPA:仅当结果集不为空时才缓存查询

仅当对象在一行上不为空时才设置属性[重复]

MongoDb 仅当数组不为空时才在数组中添加字段

仅当字段为空时才更新来自 select 语句的查询

仅当枢轴属性为空时才通过 manyToMany 计算相关模型 - Laravel