国产探花免费观看_亚洲丰满少妇自慰呻吟_97日韩有码在线_资源在线日韩欧美_一区二区精品毛片,辰东完美世界有声小说,欢乐颂第一季,yy玄幻小说排行榜完本

首頁 > 編程 > JavaScript > 正文

JavaScript遍歷求解數獨問題的主要思路小結

2019-11-20 09:43:28
字體:
來源:轉載
供稿:網友

數獨規則
數獨游戲,經典的為9×9=81個單元格組成的九宮格,同時也形成了3×3=9個小九宮格,要求在81個小單元格中填入數字1~9,并且數字在每行每列及每個小九宮格中都不能重復。

數獨技巧

  • 直觀法
  • 候選數法
  • 相關二十格:一個數字只與其所在行列及小九宮格的二十格相關

我的思路

  • 精心設計了有效性判定函數,最多一次遍歷81個小單元格就能做出方案的有效性判定。
  • 同理設計了相關20格判定,一次0~9的循環就完成有效性判定。
  • 用數組模擬堆棧,為搜索提供回溯信息。
  • 利用對象具有map性質,來輔助判斷方案的有效性,大大簡化了算法。

方案設計與實現
只用了一個二維數組存儲數獨方案,一個一維數組作堆棧,一個布爾變量作回溯標識。

1.變量定義:

var problem = [        //這是書上提到的難度10.7的題  [8,0,0,0,0,0,0,0,0],  [0,0,3,6,0,0,0,0,0],  [0,7,0,0,9,0,2,0,0],  [0,5,0,0,0,7,0,0,0],  [0,0,0,0,4,5,7,0,0],  [0,0,0,1,0,0,0,3,0],  [0,0,1,0,0,0,0,6,8],  [0,0,8,5,0,0,0,1,0],  [0,9,0,0,0,0,4,0,0]]var stack = [],flag = false;

2.方案有效性判定:
充分利用了javascript對象的哈希特性,為了方便調試,判定有效時函數的返回值為0,無效時分三種情況,行沖突、列沖突、小九宮格沖突,分別返回1,2,3。前期判定用了它,后來增加了相關二十格判定,在找答案時這個函數就用不上了。

function checkValid(sudo){  let subSudo = {}            //輔助變量,用來判定小九宮格是否沖突  for(let i = 0; i<9; i++){    let row = {}, col = {}       //輔助變量,用來判定行、列是否沖突    for(let j = 0; j<9; j++){      let cur1 = sudo[i][j], cur2 = sudo[j][i]      //一次內循環同時完成行列的判定      if(row[cur1])          //當前元素已經在行中出現,優化掉零的判斷,key為0時值為0,不需要額外判斷        return 1;          //返回錯誤代碼      else        row[cur1] = cur1      //當前元素未在行中出現,存入輔助變量中        if(col[cur2])          //列的判定與行類似,優化掉零的判斷,key為0時值為0,不需要額外判斷        return 2;      else        col[cur2] = cur2;      let key = Math.floor(i/3)+'-'+Math.floor(j/3)    //為不同的小九宮格生成不同的key      if(subSudo[key]){         //小九宮格中已經有元素,優化掉零的判斷,key為0時值為0,不需要額外判斷        if(subSudo[key][cur1])    //對某一個小九宮格的判定與行類似          return 3        else          subSudo[key][cur1] = cur1      }else{              //這是某小九宮格中的第一個元素        subSudo[key] = {}       //為小九宮格新建一個輔助變量,并將第一個元素存入其中        subSudo[key][cur1] = cur1      }             }  }  return 0;                //程序能運行到這,說明方案有效}
3.相關二十格判定
原理同整體判定,亮點在小九宮格的定位上。
function check20Grid(sudo,i,j){          let row = {}, col = {}, subSudo = {}        //輔助變量  for(let k = 0; k < 9; k++){    let cur1 = sudo[i][k], cur2 = sudo[k][j]    if(cur1){                    //當前元素已經在行中出現,優化掉零的判斷,key為0時值為0,不需要額外判斷      if(row[cur1])        return 1;                //返回錯誤代碼      else        row[cur1] = cur1            //當前元素未在行中出現,存入輔助變量中    }    if(cur2){                    //列的判定與行類似,優化掉零的判斷,key為0時值為0,不需要額外判斷      if(col[cur2])        return 2;      else        col[cur2] = cur2;    }    //轉化循環變量到小九宮格的坐標    let key = sudo[Math.floor(i/3)*3 + Math.floor(k/3)][Math.floor(j/3)*3+Math.floor(k%3)]    if(subSudo[key])                //九宮格判定與行類似,優化掉零的判斷,key為0時值為0,不需要額外判斷      return 3    else      subSudo[key] = key  }  return 0;}

4.遍歷求解
利用元素狀態初值為零的元素即為待定的特性,并加上堆棧的輔助,沒有再開辟額外的存儲空間。

function findAnswer(){  for(let i = 0; i<9; i++){    for(let j = 0; j<9; ){      if(problem[i][j] === 0 || flag){       //當前位置為待定元素的首次處理或回溯到當前位置,兩種情況看似不同,其實處理相同,自加1即可        flag = false;        let k = problem[i][j] + 1;        //搜索向下一個合法值邁進        while(k<10){               //循環找到下一個合法值          problem[i][j] = k;          //填值          if(check20Grid(problem,i,j) == 0){  //判定合法,相關二十格判定            stack.push([i,j++])        //存儲回溯點,并步進            break;          }          k++;        }        if(k>9){                 //當前位置找不到合法值,回溯          problem[i][j] = 0;          //回溯前歸零          let rt = stack.pop();         //堆棧中取回溯信息          if(!rt)                //無解判斷,返回0            return 0;            i=rt[0]                //穿越          j=rt[1]          flag = true;        }      }else{                    //當前位置數字為題目給定        j++;      }    }  }  return 1;                      //成功找到一組解}

發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
主站蜘蛛池模板: 天镇县| 宁国市| 宜宾县| 阿荣旗| 松江区| 乐至县| 尖扎县| 广元市| 四平市| 富源县| 图们市| 定兴县| 聂拉木县| 神池县| 黄山市| 佛冈县| 鹰潭市| 民丰县| 始兴县| 鹤庆县| 高阳县| 鹤庆县| 南京市| 治多县| 靖安县| 宝应县| 福鼎市| 孟村| 宝清县| 彩票| 长葛市| 龙游县| 宜丰县| 永川市| 南汇区| 扎赉特旗| 射阳县| 三原县| 勐海县| 苍溪县| 霍林郭勒市|