題意
給N個單詞表示N個點,和N-1個單詞對,表示可以走的路徑,求字典序最小的總路徑。
首先說下這麼暴力DFS能過。暴力的我都不敢寫= =
class Solution { public: vector<string> findItinerary(vector<vector<string> >& tickets) { map<string, vector<string> > mp; for (int i = 0; i < tickets.size(); i++) { string from = tickets[i][0]; string to = tickets[i][1]; if (mp.find(from) == mp.end()) { vector<string> v; v.push_back(to); mp[from] = v; } else { mp[from].push_back(to); } } for (map<string, vector<string> >::iterator iter = mp.begin(); iter != mp.end(); iter++) { sort(iter->second.begin(), iter->second.end()); } vector<string> res; string cur = "JFK"; res.push_back(cur); dfs(cur, mp, res, tickets.size()); return res; } bool dfs(string cur, map<string, vector<string> > &mp, vector<string> &res, int n) { if (res.size() == n + 1) return true; if (mp.find(cur) == mp.end()) return false; if (mp[cur].size() == 0) return false; for (int i = 0; i < mp[cur].size(); i++) { string nxt = mp[cur][i]; res.push_back(nxt); mp[cur].erase(mp[cur].begin() + i); if (dfs(nxt, mp, res, n)) return true; mp[cur].insert(mp[cur].begin() + i, nxt); res.pop_back(); } return false; } };
然後說正解。
如果把每一個字符串當做一個點,每一個字符串對就是一條有向邊。那麼這麼題目就是要求輸出最小字典序的歐拉路徑。
以下參考 https://www.cnblogs.com/TEoS/p/11376707.html
什麼是歐拉路徑?歐拉路徑就是一條能夠不重不漏地經過圖上的每一條邊的路徑,即小學奧數中的一筆畫問題。而若這條路徑的起點和終點相同,則將這條路徑稱爲歐拉回路。
如何判斷一個圖是否有歐拉路徑呢?顯然,與一筆畫問題相同,一個圖有歐拉路徑需要以下幾個條件:
- 首先,這是一個連通圖
- 若是無向圖,則這個圖的度數爲奇數的點的個數必須是0或2;若是有向圖,則要麼所有點的入度和出度相等,要麼有且只有兩個點的入度分別比出度大1和少1
上面這兩個條件很好證明。查找歐拉路徑前,必須先保證該圖滿足以上兩個條件,否則直接判誤即可。
查找歐拉路徑的算法有Fluery算法和Hierholzer算法。下面介紹一下Hierholzer算法。
算法流程:
- 對於無向圖,判斷度數爲奇數的點的個數,若爲0,則設任意一點爲起點,若爲2,則從這2個點中任取一個作爲起點;對於有向圖,判斷入度和出度不同的點的個數,若爲0,則設任意一點爲起點,若爲2,則設入度比出度小1的點爲起點,另一點爲終點。具體起點的選擇要視題目要求而定。
- 從起點開始進行遞歸:對於當前節點x,掃描與x相連的所有邊,當掃描到一條(x,y)時,刪除該邊,並遞歸y。掃描完所有邊後,將x加入答案隊列。
- 倒序輸出答案隊列。(因爲這裏是倒序輸出,我們可以用棧來存儲答案,當然用雙端隊列也可以)
我畫圖理解一下這個算法,一個歐拉路徑其實都是這個樣子的
就是從起點到終點的路徑上畫幾個圈。
舉兩個具體的例子
path = []
A --> B --> C 因爲C沒有再相連的邊 所以把C加入路徑 path=[C]
--> D --> B 因爲B沒有再相連的邊 所以把B加入路徑 path=[C, B]
D path=[C, B, D]
B path=[C, B, D, B]
A path=[C, B, D, B, A]
path = []
A --> B --> C --> B --> D 因爲D沒有再相連的邊 所以把D加入路徑 path=[D]
B path=[D, B]
C path=[D, B, C]
B path=[D, B, C, B]
A path=[D, B, C, B, A]
所以無論先遍歷的那一條邊都能得出正確的歐拉路徑,既然題目要求字典序,那麼每次選擇最小字符串先處理即可。
代碼
class Solution { public: vector<string> findItinerary(vector<vector<string> >& tickets) { map<string, priority_queue<string,vector<string>,greater<string> > > mp; for (int i = 0; i < tickets.size(); i++) { string from = tickets[i][0]; string to = tickets[i][1]; if (mp.find(from) == mp.end()) { priority_queue<string,vector<string>,greater<string> > q; q.push(to); mp[from] = q; } else { mp[from].push(to); } } vector<string> res; string cur = "JFK"; dfs(cur, mp, res); reverse(res.begin(), res.end()); return res; } void dfs(string cur, map<string, priority_queue<string,vector<string>,greater<string> > > &mp, vector<string> &res) { while(mp[cur].size()) { string nxt = mp[cur].top(); mp[cur].pop(); dfs(nxt, mp, res); } res.push_back(cur); } };