【Leetcode&C&并查集】959. Regions Cut By Slashes

13 篇文章 0 订阅

问题入口

https://leetcode.com/problems/regions-cut-by-slashes/

实现

这道题是对并查集的练习。并查集两个重要操作是Union和find。而合并什么是个问题。合并线段,将连在一起的线段合并为一个集合,这样就能得到线段数,但是线段数和区域数之间不是线性关系。例如下图(黑线用来划分网格,红线是题目给过的参数),线段数分别是4和5,但是区域数都是4。而合并区域的话,如果还把网格看成一个整体就没办法合并。

解决方案是把一个网格分成四个三角形,不论红线是“\”还是“/”,都无需顾虑。0号三角形始终可以和左边的区域合并,2号三角形始终可以和右边的区域合并,1号三角形始终和上边的区域合并,而3号三角形始终可以和下边的区域合并。很妙(但是我就想不出来……)。注意网格的位置也影响到了哪些区域可以合并,哪些区域不可以合并。

//找到父亲节点,并压缩路径
int find(int *parent, int p)
{
    while(p!=parent[p])
    {
        parent[p] = parent[parent[p]];
        p=parent[p];
    }
    return p;
}

void unionElement(int *parent, int *rank, int p, int q){
    int pRoot = find(parent,p);
    int qRoot = find(parent,q);

    if(rank[pRoot]>rank[qRoot])
        parent[qRoot]=pRoot;
    else if(rank[pRoot]<rank[qRoot])
        parent[pRoot]=qRoot;
    else
    {
        parent[pRoot]=qRoot;
        rank[qRoot]+=1;
    }

}


int regionsBySlashes(char ** grid, int gridSize){
    int *parent = malloc(sizeof(int)*gridSize*gridSize*4);
    int *rank = malloc(sizeof(int)*gridSize*gridSize*4);
    
    //初始化
    for(int i=0;i<gridSize*gridSize*4;i++)
    {
        parent[i]=i;
        rank[i]=1;        
    }

    //合并每个网格的区域,O(gridSize²)
    for(int i=0;i<gridSize;i++)
    {
        for(int j=0;j<gridSize;j++)
        {
            int u=gridSize*i*4+j*4;
            if(grid[i][j]=='/')
            {
                unionElement(parent,rank,u,u+1);
                unionElement(parent,rank,u+2,u+3);
            }
            else if(grid[i][j]=='\\')
            {
                unionElement(parent,rank,u,u+3);
                unionElement(parent,rank,u+1,u+2);
            }
            else{
                 unionElement(parent,rank,u,u+3);
                 unionElement(parent,rank,u,u+1);
                 unionElement(parent,rank,u,u+2);
            }

        }
    }

    //合并整个网格的区域,O(gridSize²)
    for(int i=0;i<gridSize;i++)
    {
        for(int j=0;j<gridSize;j++)
        {
            int u=gridSize*i*4+j*4;
            if(i==0)
                unionElement(parent,rank,u+3,u+4*gridSize+1);
            else if(i==gridSize-1)
                unionElement(parent,rank,u+1,u-4*gridSize+3);
            else
            {
                if(j==0 || j==gridSize-1)
                {
                    unionElement(parent,rank,u+1,u-4*gridSize+3);
                    unionElement(parent,rank,u+3,u+4*gridSize+1);
                }
                else{
                    unionElement(parent,rank,u,u-2);
                    unionElement(parent,rank,u+2,u+4);
                }
                
            }
            
            if(j==0)
                unionElement(parent,rank,u+2,u+4);
            else if(j==gridSize-1)
                unionElement(parent,rank,u,u-2);
            else
            {
                if(i==0 || i==gridSize-1){
                    unionElement(parent,rank,u,u-2);
                    unionElement(parent,rank,u+2,u+4);                   
                }
                else{
                   unionElement(parent,rank,u+1,u-4*gridSize+3);
                   unionElement(parent,rank,u+3,u+4*gridSize+1); 
                }
                
            }
                
            
        }    
    
    }

    //计数区域有多少个
    int *tmp = malloc(sizeof(int)*gridSize*gridSize*4);
    memset(tmp,0,sizeof(int)*gridSize*gridSize*4);
    for(int i=0;i<gridSize*gridSize*4;i++){
        tmp[find(parent,parent[i])]=1;
    }
    int count = 0;
    for(int i=0;i<gridSize*gridSize*4;i++)
    {
        if(tmp[i]==1)
            count++;
    }
    return count;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值