您的位置:首页 > 编程语言

Codewars 打怪日记 5星级kyu 数独游戏 我是否完成了数组 Did I Finish my Sudoku? 看小菜和大神循环的巧妙运用

2016-06-27 19:52 696 查看
史蒂夫·乔布斯说过,每个人都应该学习给电脑编写程序的技术,因为这一过程能够教你如何去思考!学习编程的渠道有很多种,比如你可以利用一些互动平台或者书籍去学习编程,无论是哪种,只要找到适合自己的就OK。编程极富有创造性,你可以创造出许多新奇有趣的想法。很多时候,开发者在相同的问题上花费了大量时间,而忽略了创造性。所以很多网站都发起编程挑战赛,找到千千万的实现方法,codewars就是这样一个在线编程社区,而且有奖励系统激励程序员们像打游戏升级一样地做习题,做的题目难度大,自己段位就升高。随着段位的升高,会获得相应的特权。

只说不练假把式,编程更重要的实践,codewars提供的题目都有实际背景,不管初学者还是小牛,都可以去codewars挑战自己。

进入正题,编写一个函数,输入是一个数组,判断该数组是否是完成了的数独 ,如果是 返回'Finished!'
否则返回'Try again!'; 数独游戏 ,我们从小都玩过,这里的规则是 输入一个9行9列的数组,每行每列都包含1-9个数字,数字不能重复,不能有完全相同的两行或者两列,除此之外,每个区域(小9宫格也要满足这个要求)。如图是一个完成的数独。



我的思路是:先判断每一行是否包含1-9,再判断每一列是否包含 1 -9 ,如果所有行和所有列都包含1-9 ,那么肯定不会有相同的行和列(如果输入的是9*9数组)。然后再去判断 小区域 ,区域是三行三列一个循环,所以在这里,对循环的访问嵌套了四层,时间复杂度达到o(n^4)。好吧,来看看小菜的代码吧。

var isValid = function(arrR){
arrR.sort();
for (var n = 1 ;n<= 9; ++n)
{
if (arrR[n-1] != n)
{
arrR.length = 0;
return  0;
}
}
arrR.length = 0;
return 1;

}
function doneOrNot(board){
//your code here
var rows = board.length;
var columns = board.length ;
if (rows  != columns  ||  rows != 9 ) {return "Try again!";};
var i,j;
var arrR = [];
///////////////先检查行
for(i = 0;i < rows ; ++ i)
{
for (j =0;j < columns ; ++ j)
{
arrR.push(board[i][j]) ;
}
if ( !isValid(arrR) ) { return "Try again!";} ;
}
arrR.length = 0;
//////////////在检查列,如果行列都满足,则满足
for(j = 0;j < columns ; ++j)
{
for (i =0;i< rows ; ++ i)
{
arrR.push(board[i][j]) ;
}
if ( !isValid(arrR) ) { return "Try again!";} ;
}
//////////////判断每一块是否满足
arrR.length = 0;
<span style="color:#ff0000;"> for (var count = 0; count < rows ; )
{
for(var countC = 0; countC < columns ; )
{
for ( i =count ;i < count + 3;++  i)
{
for( j = countC ; j <countC + 3; ++j)
{
arrR.push(board[i][j])  ;
}
}
if ( !isValid(arrR) ) {  return "Try again!";} ;
countC += 3;
}
count  += 3;
}
<span style="font-family: monospace; white-space: pre; background-color: rgb(240, 240, 240);">/*为了判断每一块是否满足,我可谓是煞费苦心,但是最后搞出来四层嵌套循环也是很无奈 ,思路就是最外面两层,代表行和列,每次增加3,这样这样控制访问到9个小块,*/</span></span>
<span style="color:#ff0000;"><span style="font-family: monospace; white-space: pre; background-color: rgb(240, 240, 240);">/* 然后每个小块的填充又用了两层循环,第一层三行,第二层三列,这样组成3*3的数组,然后调用isValid()判断是否有效  */</span></span>
<span style="font-family: Arial, Helvetica, sans-serif;">     return "Finished!"; </span>
}
doneOrNot()函数思路就是

判断行的时候把每一行放到数组arrR里,然后调用isValid()判断每行是否符合要求 ; 然后判断列,控制循环把每一列放到数组里,。。。 然后就是块,再把块放到数组里,判断。 isValid()的函数就是用来判断一组数组是否包含1-9并且不重复的, 但是方法很笨,首先数组排序,i从1到9循环,看下标为i-1的数组的值是否为i,如果不是则返回0,

都通过了则返回真。

function doneOrNot(rows){

var columns = []
,    blocks = [];

for (var i = 0; i < 9; i++) {
columns[i] = [];

for (var j = 0; j < 9; j++) {
var k = Math.floor(i / 3) + Math.floor(j / 3) * 3;
blocks[k] = blocks[k] || [];

blocks[k].push(rows[i][j]);
columns[i].push(rows[j][i]);
}
}

var is_valid_row = (row) => row.slice(0).sort((a, b) => a - b).join('') == '123456789';

var is_valid = rows.every(is_valid_row)
&& columns.every(is_valid_row)
&& blocks.every(is_valid_row);

return is_valid ? 'Finished!' : 'Try again!';
}
看看高分代码吧!首先也是把 判断一个数数组是否满足要求单独写一个函数,但是他的实现方法不用循环,很简洁,还用了es6的新的箭头函数。row.slice(0)复制数组,大概是为了不污染输入数组,row.slice(0).sort((a,b) => a-b) 排序,row.slice(0).sort((a,b) => a-b) .join('') == "123456789' 排序之后分割成字符串 ,看字符串是否等于1-9。 简单的代码,用原声的js方法代替了我的循环,效率肯定比我的循环快。

其次,判断行,rows.every(is_valid_row) ,代替了我的两个for循环,对于Array类来说,二维数组其实还是按照一维的访问,只是每一个元素是一个数组,所以这里every的元素就是每一行。

判断列和判断块。块和列分别申请两个二维数组,block的每一行是一个块,columns的每一行是一列。仅仅两个for循环,搞定块和列,然后同样适用every对每一行检测。代码非常简洁,重要的是并不晦涩难懂,所以很值得学习。

最重要的感悟就是多用js原生对象的内置方法。可以减少很多不必要的循环。

ps : 如果比对数独游戏感兴趣点击这里这里 。欢迎交流。关于前端,关于codewars,关于工作和面试。



/*为了判断每一块是否满足,我可谓是煞费苦心,但是最后搞出来四层嵌套循环也是很无奈 ,
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息