1. 程式人生 > >Codewars 打怪日記 5星級kyu 數獨遊戲 我是否完成了陣列 Did I Finish my Sudoku? 看小菜和大神迴圈的巧妙運用

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,關於工作和麵試。

/*為了判斷每一塊是否滿足,我可謂是煞費苦心,但是最後搞出來四層巢狀迴圈也是很無奈 ,