Q:
Determine if a Sudoku is valid, according to: Sudoku Puzzles - The Rules.
The Sudoku board could be partially filled, where empty cells are filled with the character '.'.
Note:A valid Sudoku board (partially filled) is not necessarily solvable. Only the filled cells need to be validated.
A:
這個題目并不是要找數(shù)獨unfilled區(qū)域的solution曲横,而是要check每個filled cells是否valid桃移。
public boolean isValidSudoku(char[][] board) {
for(int i = 0; i<9; i++){
HashSet<Character> rows = new HashSet<Character>(); //注釋1
HashSet<Character> columns = new HashSet<Character>(); //注釋4
HashSet<Character> cube = new HashSet<Character>();
for (int j = 0; j < 9;j++){
if(board[i][j]!='.' && !rows.add(board[i][j])) //注釋2
return false;
if(board[j][i]!='.' && !columns.add(board[j][i]))
return false;
int RowIndex = 3*(i/3); //注釋3
int ColIndex = 3*(i%3);
if(board[RowIndex + j/3][ColIndex + j%3]!='.'
&& !cube.add(board[RowIndex + j/3][ColIndex + j%3]))
return false;
}
}
return true;
}
注釋1:
為什么類型里要用Character字柠,不可以寫成char无拗?
正如Integer之于int,char是基本數(shù)據(jù)類型(primitive type)概疆,Character是包裝類型(Wrapper Classes独旷,也就是類,實例化出來的叫對象)芯义,其中還涉及到自動封箱(Autoboxing),自動拆解(extracts):
example 1:
int t = 10; Integer t1 = t; //自動封箱
Integer t = new Integer(10); int t1 = t ; //自動解封
example 2:
Interger = obj1; int num1 = 69; obj1 = num1; //自動封箱
Integer obj2 = new Integer (69); int num2; num2 = obj2; //自動解封
初次之外很重要的一點妻柒,Character作為char的包裝類扛拨,作為一個類,它提供了很多方法举塔。比如當我們使用HashSet.add()這個方法時绑警,設(shè)計到object.equals()等方法,所以包裝類的意義就體現(xiàn)出來了央渣。
還有個情況计盒,就是在數(shù)據(jù)類型不統(tǒng)一的時候,在操作數(shù)據(jù)的時候容易引起錯誤痹屹,報出“java.lang.ClassCastException”章郁,比如Integer類型沖突了String,通過泛型志衍,也可以進行一個限制暖庄,參考(http://www.cnblogs.com/lwbqqyumidi/p/3837629.html)。
注釋2:
先來看一下 java.util.HashSet.add() HashSet API方法:
public boolean add (E e)
Adds the specified element to this set if it is not already present. More formally, adds the specified element e to this set if this set contains no element e2 such that (e==null ? e2==null : e.equals(e2)). If this set already contains the element, the call leaves the set unchanged and returns false.
Specified by:
add in the interface Collection<E>
add in interface Set<E>
Overrides:
add in class AbstractCollection<E>
Returns:
true if this set did not already contain the specified element
由此可知楼肪,!rows.add(board[i][j])
表達的意思是當要加入新的數(shù)字時培廓,如果不成功,說明matrix里已經(jīng)存在了春叫,那么這時出現(xiàn)了重復的值肩钠,要返回false,但暂殖!之后价匠,返回true。如果那個cell的值也不是'.'呛每,說明那個cell是有數(shù)字的踩窖。邏輯與(&&)操作之后,true && true
晨横,則判斷條件成立洋腮,return false
。
HashSet vs HashMap:
HashSet 實現(xiàn)了Set接口手形。不允許重復的值啥供。 .add()
HashMap實現(xiàn)了Map接口。不允許重復的鍵库糠。.put()
Map接口有兩個基本的實現(xiàn)伙狐,HashMap和TreeMap。TreeMap保存了對象的排列次序,而HashMap則不能鳞骤。HashMap允許鍵和值為null窒百。HashMap是非synchronized的。
HashMap <Key, Value>豫尽,而我們這里參數(shù)進來其實不需要鍵值篙梢,使用Character封裝類,匹配parameter char[][] board
就好美旧。
HashSet類維護了一個HashMap引用作為自己的成員變量:
public HashSet() { map = new HashMap<E,Object>(); }
HashSet源碼分析:http://blog.chinaunix.net/uid-26864892-id-3167656.html
注釋3:
@jaqenhgar 解釋了這里運算背后的邏輯:
0,0
,0,1
,0,2
; < --- 3 Horizontal Steps followed by 1 Vertical step to next level.
1,0
,1,1
,1,2
; < --- 3 Horizontal Steps followed by 1 Vertical step to next level.
2,0
,2,1
,2,2
; < --- 3 Horizontal Steps.
And so on...But, thej
iterates from0 to 9
.
But we need to stop after 3 horizontal steps, and go down 1 step vertical.
Use
%
for horizontal traversal. Because%
increments by 1 for each j :0%3 = 0 , 1%3 = 1, 2%3 = 2
, and resets back. So this covers horizontal traversal for each block by 3 steps.
Use/
for vertical traversal. Because/
increments by 1 after every 3 j:0/3 = 0; 1/3 = 0; 2/3 =0; 3/3 = 1
.
So far, for a given block, you can traverse the whole block using just j.
But because j is just 0 to 9, it will stay only first block. But to increment block, use i. To move horizontally to next block, use%
again :ColIndex = 3 * (i%3)
(Multiply by 3 so that the next block is after 3 columns. Ie0,0
is start of first block, second block is0,3
(not0,1
);
Similarly, to move to next block vertically, use/
and multiply by 3 as explained above. Hope this helps.