每日一题 移除最多的同行或同列石头 LeetCode947

69 篇文章 0 订阅
44 篇文章 0 订阅

这是一个图论问题,首先按照题意构建图。
石头作为顶点。
如果两个石头位于同一行或同一列,则认为这两个石头之间有一条边,即这两个石头位于同一个连通分支之内。由题中删除石头的规则可知,实际上就是不断删除连通分支之内的顶点。而每个连通分支最后都会剩下一个顶点。所以,可以移除的最大的石头数 = 石头总数 - 连通分支数。

所以题目就转化为两步,首先要构建图,然后再计算图中的连通分支数。
计算连通分支数一般有两类方法,一是使用DFS或BFS之类的搜索方法,二是使用并查集的方法。这里考虑并查集的方法

并查集

并查集回答的是图的连通性问题,可以快速得出图中的连通分支数,但不回答同属于一个连通分支的顶点是如何连接的问题,本题只需要计算图中的连通分支的个数,所以比较适合用并查集。

并查集的底层是一维的,所以要把二维的坐标映射到一维。因为x的范围是[0, 10000],所以将y坐标整体加上10000,就可以将y映射到另一个集合。

又因为,只要x,y坐标有一个相同的顶点都属于相同的集合,所以可以将每个顶点的x,y都合并起来,这样就可以简便地构成了连通分支,省去了建图的步骤。同时,构建并查集的时候应当维护当前连通分支的数量。如果有新的顶点就加一,如果合并了一个顶点就减一。
代码如下,并查集基于集合的大小(size)进行了优化(另一种方法是基于树高(rank)进行优化):

class Solution {
public:
    Solution() {
        connectedComponentCount = 0;
    }

    int findf(int x) {
        if (!f.count(x)) {
            f[x] = x;
            setSize[x] = 1;
            connectedComponentCount++;
        }
        return f[x] == x ? x : f[x] = findf(f[x]);
    }

    void unionSet(int x, int y) {
        int fx = findf(x), fy = findf(y);
        if (fx == fy) return;
        if (setSize[fx] < setSize[fy]) swap(fx, fy);
        f[fy] = fx;
        setSize[fx] += setSize[fy];
        connectedComponentCount--;
    }

    int removeStones(vector<vector<int>>& stones) {
        int n = stones.size();
        for (int i = 0; i < n; ++i) {
            // 把每个顶点的x和y坐标看作是同级别的两个数,将一个顶点的x和y坐标合并,这样和该顶点具有相同x坐标和y坐标的点都会被合并到同一个集合中
            // 合并到不同集合中的顶点一定是具有
            unionSet(stones[i][0], stones[i][1] + 10000);
        } 
        return n - connectedComponentCount;
    }

    private:
        unordered_map<int, int> f, setSize; // 使用哈希表为底层数据结构实现父亲数组, setSize是每个联通分支的顶点数
        int connectedComponentCount;

};
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值