lintcode 樹上最長距離
lintcode 樹上最長距離
描述
給出由n個結點,n-1條邊組成的一棵樹。求這棵樹中距離最遠的兩個結點之間的距離。
給出三個大小為n-1的陣列starts,ends,lens,表示第i條邊是從starts[i]連向ends[i],長度為lens[i]的無向邊。
返回的是樹上任意兩個結點的最遠距離,而不是樹的深度,注意給定的邊是無向邊。
題目保證給出的邊一定能構成一棵樹。
1≤n≤1∗10^5
1≤lens[i]≤1∗10^3
樣例
給出n=5,starts=[0,0,2,2],ends=[1,2,3,4],lens=[1,2,5,6],返回11。
解釋:
(3→2→4)這條路徑長度為11
給出n=5,starts=[0,0,2,2],ends=[1,2,3,4],lens=[5,2,5,6],返回13。
解釋:
(1→0→2→4)這條路徑長度為13
,當然(4→2→0→1)也是一樣的。
思路
最一開始想到的暴力解法,直接維護一個二維陣列,求出所有點之間的距離,找出最大值。但是發現n最大是10^5,時間複雜度只能是O(n),也就是說,在有限的遍歷中就能找出那個最大值。因此需要知道一個樹中的性質。
從a點找出一個最遠點b,從b再出發,找出最遠點c(很容易知道bc的距離是大於等於ab的,c點就是a點時取等號,a在bc這條路上的時候取大於號)那麼bc就是最大距離。證明如下:
假設存在一條邊的距離大於bc,那麼首先,這條邊的起點和終點絕對不是a或b或c,設為mn。
假設m與a是相通的,那麼m,n,a之間不可能構成環,所以只有2種情況。此時a->m->n或者m->n->a的距離就大於m->n,不正確。也有可能是m->a->n 或者n->a->m這條路線,如果是這樣的話。因為an和am這兩條路的大小都小於ab(ab為a引出去的最長距離),所以ab,ac,am,an的大小關係就是
ab > an > ac 並且 ab > am > ac 並且 am+an > ab+ac,但是此時 b引出去的最長距離就不是b->a->c(此處有個問題,b引出的最長距離未必經過a,可以是另外一條支路,也就是abc公用了一個根,此時把根理解為a,一樣得出結論)就是b->a->n或者m,又不正確。
因此證明了bc這條邊就是最長的距離。
本人表達能力和邏輯能力欠佳,所以可以參考這位大佬的
https://blog.csdn.net/macuilxochitl/article/details/19157579
程式碼
我寫的程式碼維護了一個n^2大小的矩陣,導致了記憶體超出了限制。。
因為已經糾結這題很久了,就沒有再去想解決的方法。
這是我的程式碼
class Solution { public: /** * @param n: The number of nodes * @param starts: One point of the edge * @param ends: Another point of the edge * @param lens: The length of the edge * @return: Return the length of longest path on the tree. */ void dfs(vector<vector<int>> cost,const int tar, int & _max, int & END) { int n = cost.size(),top = -1, j, s[10007]; vector<int> visited(n, 0); visited[tar] = 1; s[++top] = tar; int v; while (top != -1) { v = s[top]; for (j = 0; j < n; j++) { if (visited[j] == 0 && cost[v][j] != 0) { visited[j] = 1; s[++top] = j; if (cost[tar][j] == 0) { cost[j][tar] = cost[tar][j] = cost[tar][v] + cost[v][j]; if (_max < cost[tar][j]) { _max = cost[tar][j]; END = j; } } break; } } if (j == n) top--; } } int longestPath(int n, vector<int> &starts, vector<int> &ends, vector<int> &lens) { // Write your code here vector<vector<int>> cost; cost.resize(n); for (int i = 0; i < n; i++) cost[i].resize(n); for (int i = 0; i < n-1; i++) { int start = starts[i], end = ends[i], len = lens[i]; cost[start][end] = len; cost[end][start] = len; } int _max = 0, END = 0; dfs(cost, 0,_max, END); _max = 0; dfs(cost, END,_max, END); return _max; } };
這個是九章演算法種提供的答案。
原網址:
https://www.jiuzhang.com/solution/longest-path-on-the-tree/#tag-other
/**
* 本參考程式來自九章演算法,由 @P同學 提供。版權所有,轉發請註明出處。
* - 九章演算法致力於幫助更多中國人找到好的工作,教師團隊均來自矽谷和國內的一線大公司在職工程師。
* - 現有的面試培訓課程包括:九章演算法班,系統設計班,演算法強化班,Java入門與基礎演算法班,Android 專案實戰班,
* - Big Data 專案實戰班,演算法面試高頻題班, 動態規劃專題班
* - 更多詳情請見官方網站:http://www.jiuzhang.com/?source=code
*/
public class Solution {
/**
* @param n: The number of nodes
* @param starts: One point of the edge
* @param ends: Another point of the edge
* @param lens: The length of the edge
* @return: Return the length of longest path on the tree.
*/
class TreeNode {
int val;
Map<TreeNode, Integer> neighbors;
public TreeNode(int val) {
this.val = val;
neighbors = new HashMap<>();
}
}
class Pair implements Comparable<Pair> {
TreeNode node;
int val;
public Pair(TreeNode node, int val) {
this.node = node;
this.val = val;
}
@Override
public int compareTo(Pair other) {
return this.val - other.val;
}
}
public int longestPath(int n, int[] starts, int[] ends, int[] lens) {
if (n <= 1) {
return 0;
}
Map<Integer, TreeNode> map = new HashMap<>();
for (int i = 0; i < starts.length; i++) {
if (!map.containsKey(starts[i])) {
TreeNode newNode = new TreeNode(starts[i]);
map.put(starts[i], newNode);
}
if (!map.containsKey(ends[i])) {
TreeNode newNode = new TreeNode(ends[i]);
map.put(ends[i], newNode);
}
TreeNode node1 = map.get(starts[i]);
TreeNode node2 = map.get(ends[i]);
node1.neighbors.put(node2, lens[i]);
node2.neighbors.put(node1, lens[i]);
}
TreeNode root = map.get(0);
Pair p = bfsHelper(n, root);
return bfsHelper(n, p.node).val;
}
private Pair bfsHelper(int n, TreeNode node) {
Pair res = new Pair(null, -1);
boolean[] visited = new boolean[n];
visited[node.val] = true;
PriorityQueue<Pair> pq = new PriorityQueue<>();
pq.offer(new Pair(node, 0));
while (!pq.isEmpty()) {
Pair curt = pq.poll();
res = curt;
for (Map.Entry<TreeNode, Integer> entry : curt.node.neighbors.entrySet()) {
if (visited[entry.getKey().val]) {
continue;
}
visited[entry.getKey().val] = true;
pq.offer(new Pair(entry.getKey(), curt.val + entry.getValue()));
}
}
return res;
}
}
如果有大神能指正我的程式碼的錯誤。非常感謝!