leetcode-36- 有效的数独(valid sudoku)-java

题目及测试用例

package pid036;
/*有效的数独

判断一个 9x9 的数独是否有效。只需要根据以下规则,验证已经填入的数字是否有效即可。

    数字 1-9 在每一行只能出现一次。
    数字 1-9 在每一列只能出现一次。
    数字 1-9 在每一个以粗实线分隔的 3x3 宫内只能出现一次。

上图是一个部分填充的有效的数独。

数独部分空格内已填入了数字,空白格用 '.' 表示。

示例 1:

输入:
[
  ["5","3",".",".","7",".",".",".","."],
  ["6",".",".","1","9","5",".",".","."],
  [".","9","8",".",".",".",".","6","."],
  ["8",".",".",".","6",".",".",".","3"],
  ["4",".",".","8",".","3",".",".","1"],
  ["7",".",".",".","2",".",".",".","6"],
  [".","6",".",".",".",".","2","8","."],
  [".",".",".","4","1","9",".",".","5"],
  [".",".",".",".","8",".",".","7","9"]
]
输出: true

示例 2:

输入:
[
  ["8","3",".",".","7",".",".",".","."],
  ["6",".",".","1","9","5",".",".","."],
  [".","9","8",".",".",".",".","6","."],
  ["8",".",".",".","6",".",".",".","3"],
  ["4",".",".","8",".","3",".",".","1"],
  ["7",".",".",".","2",".",".",".","6"],
  [".","6",".",".",".",".","2","8","."],
  [".",".",".","4","1","9",".",".","5"],
  [".",".",".",".","8",".",".","7","9"]
]
输出: false
解释: 除了第一行的第一个数字从 5 改为 8 以外,空格内其他数字均与 示例1 相同。
     但由于位于左上角的 3x3 宫内有两个 8 存在, 因此这个数独是无效的。

说明:

    一个有效的数独(部分已被填充)不一定是可解的。
    只需要根据以上规则,验证已经填入的数字是否有效即可。
    给定数独序列只包含数字 1-9 和字符 '.' 。
    给定数独永远是 9x9 形式的。

*/
public class main {
	
	public static void main(String[] args) {
		char[][] testTable1 ={
			  {'5','3','.','.','7','.','.','.','.'},
			  {'6','.','.','1','9','5','.','.','.'},
			  {'.','9','8','.','.','.','.','6','.'},
			  {'8','.','.','.','6','.','.','.','3'},
			  {'4','.','.','8','.','3','.','.','1'},
			  {'7','.','.','.','2','.','.','.','6'},
			  {'.','6','.','.','.','.','2','8','.'},
			  {'.','.','.','4','1','9','.','.','5'},
			  {'.','.','.','.','8','.','.','7','9'}
			};
		char[][] testTable2 ={
			  {'8','3','.','.','7','.','.','.','.'},
			  {'6','.','.','1','9','5','.','.','.'},
			  {'.','9','8','.','.','.','.','6','.'},
			  {'8','.','.','.','6','.','.','.','3'},
			  {'4','.','.','8','.','3','.','.','1'},
			  {'7','.','.','.','2','.','.','.','6'},
			  {'.','6','.','.','.','.','2','8','.'},
			  {'.','.','.','4','1','9','.','.','5'},
			  {'.','.','.','.','8','.','.','7','9'}
			};
		
			test(testTable1);
			test(testTable2);
		
	}
		 
	private static void test(char[][] ito) {
		Solution solution = new Solution();
		boolean rtn;
		long begin = System.currentTimeMillis();
		
		rtn = solution.isValidSudoku(ito);//执行程序
		long end = System.currentTimeMillis();		
		
	    System.out.print(rtn);
		
		System.out.println();
		System.out.println("耗时:" + (end - begin) + "ms");
		System.out.println("-------------------");
	}

}

解法1(成功,30ms,速度中等)
速度o(n),空间o(n)
具体在某一行,某一列,某个九宫格查找是否1-9重复的办法是
加入一个hashmap,以1-9为key,加入hashmap,到了新的地方就查找是否containskey
另外在99 对33进行循环的方法是k从0-9循环,3*3具体的左上角的index通过k模乘得到

package pid036;

import java.util.HashMap;

public class Solution {
public boolean isValidSudoku(char[][] board) {
        
        int length=board.length;
        int length2=board[0].length;
        if(length!=9||length2!=9){
        	return false;
        }
        HashMap map=new HashMap();
        //横着的row
        for(int i=0;i

解法2(成功,6ms,很快)
使用一个boolean数组代替hashmap,节约空间和时间,每次将num-1 位变为true,如果已经为true,则重复,返回false
每次遍历后,清空数组,等待再次使用。

九宫格,先算出左上角顶点,然后for循环i 3次,j 3次。
如何算出顶点,第一个for循环indexi 3次(每次+3),indexj也一样。
或者k从0循环9次,indexi =(k%3)*3;indexj=k-begini/3;
或者从i,j倒推第几个九宫格, box_index = (row / 3) * 3 + columns / 3(直接设置9个数组,遍历81个元素,直接放入对应数组。)

	public boolean isValidSudoku(char[][] board) {
        boolean[] hasNum=new boolean[9];
        //先对行
        for(int j=0;j<9;j++){
        	for(int i=0;i<9;i++){
        		//如果插入结果为false,说明重复,返回false
        		if(!addIntoArray(i, j, hasNum, board)){
        			return false;
        		}        		
        	}
        	clearArray(hasNum);
        }
        //对列
        for(int i=0;i<9;i++){
        	for(int j=0;j<9;j++){
        		//如果插入结果为false,说明重复,返回false
        		if(!addIntoArray(i, j, hasNum, board)){
        			return false;
        		}        		
        	}
        	clearArray(hasNum);
        }
        //对九宫格,indexi为九宫格左上角的row,indexj为九宫格左上角的col,两者每次加3
        for(int indexi=0;indexi<9;indexi+=3){
        	for(int indexj=0;indexj<9;indexj+=3){
            	for(int i=indexi;i

解法3(别人的,应该成功,貌似很快,但实测30ms)
经典的位运算
n为1*2的a次方,化为二进制
比如
1 10
2 100
3 1000
4 10000
首先比如第二行 signs[0][1] 初始值为0
当0与不同的n进行或运算,这个值对应n的那个1的那位也变成了1
比如0|100=100 100|10=110
但是如果这个数与一个重复的数进行与运算,则
比如110&1000=0 110&100=100 则此时不为0,证明发生了重复
很高明的手法,本来用hashmap或hashset要用o(n)的空间,但用位运算只要一个int就可以了

首先也是循环每个元素节点,然后通过

int a = board[i][j] -'0';

转换成int类型,答案中写的是减1。始终不明白,后来在去掉不减的时候debug发现得到的a并不是原数,而是原数的Ascii码。岁而明白这是char转int的方法。

int n = 1 << a;  

这个就是正常的按位左移运算了,主要用来判断是否有相同的元素存在。

if ((signs[0][i] & n) != 0 
                		|| (signs[1][j] & n) != 0 
                		|| (signs[2][cubeIndex] & n) != 0){
                    return false;
                }

这段我认为是最难理解的地方,我们可以把它拆分来看,先看每行,其他先不关注。代码中使用了&,并且上边是按元素左位移的,所以只有当元素相等的情况下值才不回为0,&为相对应位都是1,则结果为1,否则为0。每一列、每一区块的判断逻辑都是如此。

signs[0][i] |= n;
                signs[1][j] |= n;
                signs[2][cubeIndex] |= n;

这段就是把循环的元素都合并到一起,这样才能保证在上边进行重复判断的时候所有的元素都会判断上。下面附上这种方案的完整解决方案:

public boolean isValidSudoku(char[][] board) {
        int[][] signs = new int[3][9];
        for (int i = 0; i < 9; i++) {
            for (int j = 0; j < 9; j++) {
            	
                if (board[i][j] == '.'){
                    continue;
                }
                int a = board[i][j] -'0';   //char 转  int方法
                int n = 1 << a;             
                int cubeIndex = i / 3 * 3 + j / 3;
                if ((signs[0][i] & n) != 0 
                		|| (signs[1][j] & n) != 0 
                		|| (signs[2][cubeIndex] & n) != 0){
                    return false;
                }
                
                signs[0][i] |= n;
                signs[1][j] |= n;
                signs[2][cubeIndex] |= n;
            }
        }
        return true;
    }

你可能感兴趣的:(数据结构-数组,leetcode-初级,leetcode)