1、难度简单:强行让你接触哈希表

要求:时间复杂度小于 O(n2) 的算法
方法一:暴力枚举:时间复杂度O(N2),空间复杂度O(1)

class Solution {
    public int[] twoSum(int[] nums, int target) {
        int n = nums.length;
        for (int i = 0; i < n; ++i) {
            for (int j = i + 1; j < n; ++j) {
                if (nums[i] + nums[j] == target) {
                    return new int[]{i, j};
                }
            }
        }
        return new int[0];
    }
}

上述代码原理:枚举数组中的每一个数 x,寻找数组中是否存在 target - x。
当我们使用遍历整个数组的方式寻找 target - x 时,需要注意到每一个位于 x 之前的元素都已经和 x 匹配过,因此不需要再进行匹配。而每一个元素不能被使用两次,所以我们只需要在 x 后面的元素中寻找 target - x

方法二:哈希表:时间复杂度:O(N),空间复杂度O(N)

class Solution {
    public int[] twoSum(int[] nums, int target) {
        Map<Integer, Integer> hashtable = new HashMap<Integer, Integer>(); //创建哈希表,此时里面是空的
        for (int i = 0; i < nums.length; ++i) { //遍历nums数组
            if (hashtable.containsKey(target - nums[i])) { //若哈希表中存在数据target - nums[i]那就return结果数组
                return new int[]{hashtable.get(target - nums[i]), i}; 
            }
            hashtable.put(nums[i], i); //没找到答案,那就把当前遍历到的值插入到哈希表中
        }
        return new int[0]; //如果nums里的元素无法满足我们的要求,那就返回0个元素的数组
    }
}

方法一缺陷:寻找 target - x 的时间复杂度过高。
使用哈希表,可以将寻找 target - x 的时间复杂度降低到从 O(N)O(N) 降低到 O(1)O(1)。
这样我们创建一个哈希表,对于每一个 x,我们首先查询哈希表中是否存在 target - x,然后将 x 插入到哈希表中,即可保证不会让 x 和自己匹配。

36、难度中等:活用哈希表原理

数独:数独是一类经典的推理游戏,在数独当中可以经常看到有很多的数字,独表示的就是唯一不重复, 最常见的数独游戏就是九宫数独,九宫数独要求行不重复、列不重复、(小九)宫(格)不重复。
注意题给数组board的内容格式:
在这里插入图片描述

使用哈希表一次遍历:时间复杂度:O(1)、空间复杂度:O(1)

class Solution {
    public boolean isValidSudoku(char[][] board) {
        int[][] rows = new int[9][9]; //单元格的行数最大为9,元素最大为9。但代码中为 0 ~ 8
        int[][] columns = new int[9][9]; //单元格的列数最大为9
        int[][][] subboxes = new int[3][3][9]; //该数组表示小九宫格,行列最大为3
        //题目传来的数组board的格式为二维数组,每一个元素是一个一维数组,记录数独的每一行。该一维数组的每一项是单元格的数值。
        for (int i = 0; i < 9; i++) { //遍历一维数组(也就是数独的每一行)
            for (int j = 0; j < 9; j++) { //遍历每一行的每一个元素
                char c = board[i][j]; //存储元素内容
                if (c != '.') { //如果不是字符.
                    //index是每个单元格的数值
                    //将char类型的数字转换成int型的数字:char变量 - '0'即可
                    //再 -1 是因为数组rows、columns等范围都是0~8,表示1~9的数值范围。
                    int index = c - '0' - 1; 
                    //更新单元格出现次数
                    rows[i][index]++;
                    columns[j][index]++;
                    subboxes[i / 3][j / 3][index]++;
                    //如果不符合数独条件,那就返回false
                    if (rows[i][index] > 1 || columns[j][index] > 1 || subboxes[i / 3][j / 3][index] > 1) {
                        return false;
                    }
                }
            }
        }
        //若遍历时一次都未出现不符合的条件,那就返回true
        return true;
    }
}

图片部分是官方描述,文字部分是添加的理解:
在这里插入图片描述

数独由3 * 3的小九宫格组成,每个小九宫格又由3 * 3的小单元格组成。所以小九宫格的行数为02(0、1、2共三行),列数为02。所以单元格的行下表整除3就能得到它所在小九宫格的行数,同理得列数。
在这里插入图片描述

二维数组rows、columns中的每一项 rows[ i ] [index] 和 columns[ j ] [index] 对应的数值:表示数独的第 i 行第 j 列的单元格所在的行、列中,数字 index+1 出现的次数。其中 0 ≤ index < 9,对应的数字 index+1 满足 1 ≤ index + 1 ≤ 9。
三维数组subboxes中的每一项 subboxes [ i/3 ] [ j/3 ] [ index ] 对应的数值:表示数独的第 i 行第 j 列的单元格所在的小九宫格中,数字index +1 出现的次数。
在这里插入图片描述

题目传入的数组char[ ] [ ] board 中每一项元素可能为数字值,也可能为字符. 所以我们需要判断当前board [ i ] [ j ] 是否为数字(也就是是否填入了数字n)若填入了,那就把 rows[ i ] [n-1] 、columns[ j ] [n-1] 、subboxes [ i/3 ] [ j/3 ] [ n-1 ] 各加1,也就是说明当前元素出现了一次。添加完后(也就是更新完后)若计数大于1那就不符合数独条件,返回false。(这一块和哈希表原理相同,三个数组都是空的,遍历传来的数组然后把数据加载到伪哈希表三个数组中)
在这里插入图片描述

更多推荐