LeetCode之路:463. Island Perimeter

一、引言

这道题的题目信息请点击这里查看Island Perimeter

其实我一看到这道题,我就想到了我之前做过的一个五子棋的项目。关于这个项目我还写了一篇博客,可以点击这里查看一个使用纯Win32 SDK和C语言实现的五子棋游戏

这里让我们想想,五子棋的判胜逻辑。

五子棋五子棋,顾名思义,就是五个同色的棋子连接成了一条线,则判定胜利。那么五子棋的判胜逻辑应该怎么完成呢?

这里进行简要的总结:其实主要的核心思想就是以一个棋子为观察原点,我们以这个棋子出发,观察八个方向(包括四个斜方向)是否存在同色棋子,然后递归计数即可。

回到这道题,其实我们以一个 island 为中心,观察它附近是否存在 island,如果存在,则周长 -1 即可。

二、我的代码

这里我再来总结下思路:
1. 遍历整个 grid,找到其中所有的 island,也就是值为 1 的点
2. 以这个点为中心,检测它附近是否有 island,也就是检测它附近的四个方向的点(上下左右)是否值为 1。这里我们假想一下,其实很简单,只要有一个相邻的 island,周长就会减少 1,最后我们将每个 island 的周长计数加起来即可。
3. 将每个 island 的周长计数加起来即可。

代码如下:

class Solution {
public:
    struct point {
        unsigned x;
        unsigned y;
    };

    int validSideCount(const point &pt, vector<vector <int>> &grid) {
        int validSideCount = 0;
        if (pt.x == 0 || grid[pt.x - 1][pt.y] == 0) {
            ++validSideCount;
        }
        if (pt.x == grid.size() - 1 || grid[pt.x + 1][pt.y] == 0) {
            ++validSideCount;
        }
        if (pt.y == 0 || grid[pt.x][pt.y - 1] == 0) {
            ++validSideCount;
        }
        if (pt.y == grid[0].size() - 1 || grid[pt.x][pt.y + 1] == 0) {
            ++validSideCount;
        }
        return validSideCount;
    }

    int islandPerimeter(vector<vector<int>> &grid) {
        int islandPerimeter = 0;
        for (int row = 0; row < grid.size(); ++row) {
            for (int col = 0; col < grid[row].size(); ++col) {
                if (grid[row][col] == 1) {
                    point pt = { row, col };
                    islandPerimeter += validSideCount(pt, grid);
                }
            }
        }
        return islandPerimeter;
    }
};

这里,我声明了一个 point 类,方便记录当前 island 的坐标。validSideCount 函数的作用就是用来检测 island 附近是否存在值为 1 的点,计数一个 island 的周长,要获得所有 island 的周长,只需要将所有的 island 的周长计数加起来即可。

这里需要注意的是,validSideCount 函数中这四个判定,需要检测是否碰到了边(坐标不能超出 vector 的最大范围值),如果碰到了边,则不能进行递减或者递增操作了,这样会出现数组访问溢出错误。

三、同样,赏析一段别人的代码

直接上代码吧,写完自己的,总是要看看别人的代码为什么比自己的代码优美不是?

int islandPerimeter(vector<vector<int>>& grid) {
        int count=0, repeat=0;
        for(int i=0;i<grid.size();i++)
        {
            for(int j=0; j<grid[i].size();j++)
                {
                    if(grid[i][j]==1)
                    {
                        count ++;
                        if(i!=0 && grid[i-1][j] == 1) repeat++;
                        if(j!=0 && grid[i][j-1] == 1) repeat++;
                    }
                }
        }
        return 4*count-repeat*2;
    }

这段代码要点如下:
1. 利用了两个变量进行周长计数:

其中,count 用来计数 island 的个数, repeat 用来计数两个 island 相邻(两个 island 相邻必然导致两条周长的减少)的个数。

那么 count * 4 - repeat * 2必然就是最终的周长,其中 repeat 的计算只计算了当前 island 的上方和左方的记录,因为下方和右方的损失也已经包括在相对方里面去了,这里就不用再多判断了,只需要乘以2即可。

  1. 我发现外国人写代码,很喜欢将很短小的代码块放到一行书写,比如这里的两个判断语句:
if(i!=0 && grid[i-1][j] == 1) repeat++;
if(j!=0 && grid[i][j-1] == 1) repeat++;

要是我来写,准得写成这样:

if (i != 0 && grid[i - 1][j == 1) {
    repeat++;
}
if (j != 0 && grid[i)[j - 1] == 1) {
    repeat++;
}

其实孰优孰劣一目了然,前者真的是一目了然,而我看似在认真的遵循着编码习惯,实则扰乱了眼球,让代码阅读者一眼看不到重点。

三、总结

总结就这么多,从这道题,又看到了一点点当初写五子棋游戏的影子;同时也看到了不一样的代码风格。

让我们的代码更加 elegant 吧!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值