1. 程式人生 > >lintcode 樹上最長距離

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

,當然(4→2→3)也是一樣的。
給出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;
    }
}

如果有大神能指正我的程式碼的錯誤。非常感謝!