Codewars 打怪日記 5星級kyu 數獨遊戲 我是否完成了陣列 Did I Finish my Sudoku? 看小菜和大神迴圈的巧妙運用
史蒂夫·喬布斯說過,每個人都應該學習給電腦編寫程式的技術,因為這一過程能夠教你如何去思考!學習程式設計的渠道有很多種,比如你可以利用一些互動平臺或者書籍去學習程式設計,無論是哪種,只要找到適合自己的就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,關於工作和麵試。