这题是一道backtrack的题目。
一开始我的思路用了set来表示每个空点能够填进去的数字,并对set做遍历,但后来发现这种解法虽然能过,但是复杂度太高了,最终我直接对每个点取‘0’到‘9’,看是否合适,其实感觉这种做法和对set不断erase的做法想法完全是一样的,只不过set的操作更复杂。可以得到一个结论是,当使用标准库和直接做的复杂度分析无明显差别时,如果直接做的算法更为直观,最好优先考虑比较直观的的算法。
另外一个很重要的点是:通过这道题,我发现,其实每次出错的地方往往都是我尝试着做改变、之前没有遇到过的地方。比如我今天写的这个算法:
class Solution { public: void solveSudoku(vector<vector<char>>& board) { solve(board, 0, 0); } private: int solve(vector<vector<char>>&board, int i, int j) { int find = 0; int x = i, y = j; int flag = 0; //找到下一个待填的位置 for (x = i; x < 9; x++) { //设置y的初始值 if (flag == 0) y = j; else { y = 0; } for (; y < 9; y++) { if (board[x][y] == '.') { find = 1; break; } } flag = 1; if (find == 1) break; } if (x == 9) return 1; //找出当前位置能够填入元素的集合,找到了就没关系了 for(char in='1';in<='9';in++) { if(isvalid(board,x,y,in)) { board[x][y]=in; if(solve(board,x,y)) return 1; board[x][y]='.'; } } return 0; } private: bool isvalid(vector<vector<char>>&board,int x,int y,char in) { for(int i=0;i<9;i++) { if(board[x][i]==in)return false; if(board[i][y]==in)return false; if(board[x/3*3+i/3][y/3*3+i%3]==in)return false; } return true; } }
出错的地方就在上面那个找下一个空('.')处,还连续犯了两个错。第一次是每次初始化都将y从j开始,其实应该是第一次从j开始,后面都从0开始。第二次错误是,未设置flag。
这给我一个启发,一方面以后检查出错的地方应重点检查我现场创新的地方,比如上面的“寻找下一个空位置”;另一方面,如果通过练习,对这些可能出错的地方形成固定的写法,那么可以大大降低出错的几率,即使出错了,debug的速度也可以得到很大的提高。