LintCode 629: Minimum Spanning Tree (最小生成树经典题)

  1. Minimum Spanning Tree
    Given a list of Connections, which is the Connection class (the city name at both ends of the edge and a cost between them), find some edges, connect all the cities and spend the least amount.
    Return the connects if can connect all the cities, otherwise return empty list.

Example
Gievn the connections = [“Acity”,“Bcity”,1], [“Acity”,“Ccity”,2], [“Bcity”,“Ccity”,3]

Return [“Acity”,“Bcity”,1], [“Acity”,“Ccity”,2]

Notice
Return the connections sorted by the cost, or sorted city1 name if their cost is same, or sorted city2 if their city1 name is also same.

最⼩小⽣生成树的概念是在⼀一个⽆无向图中,找出⼀一些边,组成⼀一棵树,使得这些边的权值
加起来最⼩小。主要解法有Prim和Krusal等。

解法1:
Prim。我的方法是先将City按名称排序,先取第一个放入joint set,然后更新每个节点跟joint set的距离,再看那个节点离joint set 最近,将该节点放入joint set,同时更新每个节点跟joint set的距离,如此反复,直到所有节点都放入了joint set。
但我的solution不能pass,我看了一下fail的case,我的方案的output跟expected基本一样,只有一个distance不同 ,但distance的cost是一样的。这个可能是因为MST不唯一。
顺便提一下,什么样的图的MST唯一呢? 根据
https://zhidao.baidu.com/question/509521548.html

  1. 当带权连通图的任意一个环中所包含的权值均不相同,其MST是唯一的。
  2. 先正常求出最小生成树,再求次小生成树(具体可以枚举图上其他边加到树里,同时删去重复的边,找到权值和最小的删边方法),如果求出次小生成树的权值和与最小生成树不相等,则最小生成树唯一,否则不唯一。
/**
 * Definition for a Connection.
 * class Connection {
 * public:
 *   string city1, city2;
 *   int cost;
 *   Connection(string& city1, string& city2, int cost) {
 *       this->city1 = city1;
 *       this->city2 = city2;
 *       this->cost = cost;
 * }
 */
 
/**
 * Definition for a Connection.
 * class Connection {
 * public:
 *   string city1, city2;
 *   int cost;
 *   Connection(string& city1, string& city2, int cost) {
 *       this->city1 = city1;
 *       this->city2 = city2;
 *       this->cost = cost;
 * }
 */

bool operator < (const Connection& c1, const Connection& c2) {
    if (c1.cost != c2.cost)
      return c1.cost < c2.cost;

    if (c1.city1 != c2.city1)
      return c1.city1 < c2.city1;

   return c1.city2 < c2.city2;
}


class Solution {
public:
    /**
     * @param connections given a list of connections include two cities and cost
     * @return a list of connections from results
     */
    vector<Connection> lowestCost(vector<Connection>& connections) {
        // MST - Prime algorithm
        vector<Connection> result;
        if (connections.size() == 0) return result;

        sort(connections.begin(), connections.end());
	 set<string> nameSet;
        for (auto c : connections) {
            nameSet.insert(c.city1);
            nameSet.insert(c.city2);
        }
        
        int len = nameSet.size();
        vector<string> nameVec(nameSet.begin(), nameSet.end());
        
        map<string, int> nameMap; //city_name, city_index
        int index = 0;
        for (auto s : nameSet) {
            nameMap[s] = index++;
        }
        
        string dummy = "INVALID";
        vector<vector<Connection>> graph(len, vector<Connection>(len, Connection(dummy, dummy, INT_MAX))); //need to do this because Connection does not provide default construction

        for (int i = 0; i < len; ++i) {
            for (int j = 0; j < len; ++j) {
                graph[i][j] = Connection(nameVec[i], nameVec[j], INT_MAX);
            }
        }
        

        for (auto c : connections) {
            if ((c < graph[nameMap[c.city1]][nameMap[c.city2]]) &&
                (c < graph[nameMap[c.city2]][nameMap[c.city1]])) {
                graph[nameMap[c.city1]][nameMap[c.city2]] = c;
                graph[nameMap[c.city2]][nameMap[c.city1]] = c;
            } 
        }
        vector<bool> visited(len, false);
        visited[0] = true;
        
        map<int, Connection *> dist; //city_index, distance to mst
        for (int i = 0; i < len; ++i) {
                dist[i] = &graph[0][i];
        }

        for (int i = 1; i < len; ++i) {
            int minDist = INT_MAX;
            for (int j = 0; j < len; ++j) {
                if (!visited[j] && dist[j]->cost < minDist) {
                    minDist = dist[j]->cost;
                    index = j;
                }
            }
            
            if (minDist == INT_MAX) return vector<Connection>();
            visited[index] = true;
            result.push_back(*dist[index]);

            for (int j = 0; j < len; ++j) {
                if (!visited[j]) {
                  if (dist[j]->cost > graph[index][j].cost) dist[j] = &graph[index][j];
                }
            }
        }
        sort(result.begin(), result.end());
        return result;
    }
};

解法2:Kruskal + union-find
Kruska 算法:⾸首先按照每条边的边权排序。然后依次按下面步骤将各条边加入MST:

  1. 判断该边两个端点是否都已经在MST中。若都在,说明连这条边就会形成环,故不能选该边;否则至少有一端点不在MST中,所以把这条边加⼊入MST中。
  2. 如何判断两个端点是否在一个结合中呢?并查集。
    注意:最后result里面的connection数应为所有城市数-1。因为2个城市一个connection,而且又没有环。
/**
 * Definition for a Connection.
 * class Connection {
 * public:
 *   string city1, city2;
 *   int cost;
 *   Connection(string& city1, string& city2, int cost) {
 *       this->city1 = city1;
 *       this->city2 = city2;
 *       this->cost = cost;
 * }
 */

bool operator < (const Connection & a, const Connection & b) {
    if (a.cost == b.cost) {
        if (a.city1 == b.city1) {
            return a.city2 < b.city2;
        } else {
            return a.city1 < b.city1;
        }
    } else {
        return a.cost < b.cost;
    }
}

class Solution {
public:
    /**
     * @param connections given a list of connections include two cities and cost
     * @return a list of connections from results
     */
    vector<Connection> lowestCost(vector<Connection>& connections) {
        sort(connections.begin(), connections.end());
        unordered_map<string, int> hashmap; //cityname, index
        int index = 0;
        
        for (auto connection : connections) {
            if (hashmap.find(connection.city1) == hashmap.end()) {
                hashmap[connection.city1] = index++;
            }
            if (hashmap.find(connection.city2) == hashmap.end()) {
                hashmap[connection.city2] = index++;
            }
        }
        
        int numCities = index; 
        father.resize(numCities);
        for (int i = 0; i < numCities; ++i) {
            father[i] = i;
        }
        vector<Connection> results;
        
        for (auto connection : connections) {
    
            int cityIndex1 = hashmap[connection.city1];
            int cityIndex2 = hashmap[connection.city2];

            int root1 = find(cityIndex1);
            int root2 = find(cityIndex2);
            if (root1 != root2) {
                father[root1] = root2;
                results.push_back(connection);
            }
        }
        
        if (results.size() != numCities - 1) return {};
        return results;
    }
    
private:
    vector<int> father;
    int find(int num) {
        if (father[num] == num) return num;
        father[num] = find(father[num]);
        return father[num];
    }
};

代码同步在
https://github.com/luqian2017/Algorithm

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值