【LeetCode】Sama的个人记录_11

 

 

【Q560】(md) 和为K的子数组
 
给定一个整数数组和一个整数 k,你需要找到该数组中和为 k 的连续的子数组的个数。
 
示例 1 :
 
  输入:nums = [1,1,1], k = 2
  输出: 2 , [1,1] 与 [1,1]为两种不同的情况。

class Solution {
     
	/*
	* 要求是连续的子数组,且数组无序,排序后再双指针的常规思路行不通
	* 正确的思路是【前缀和】
	* 举个例sums = {1, 3, 4, 6, -6}, k = 7
	* 使用暴力法会重复相同的加法运算,所以我们使用一个前缀和数组——值为从下标0到某位置的和
	* {1, 4,8,14,8}
	* 使用前缀和的目的就是将【多个数的加法】转化为【两个数的减法】(关键思想★)
	* 我们遍历出所有相减的情况情况就可以(不要忘记初始化一个0)
	*/
    public int subarraySum(int[] nums, int k) {
     
    	int res = 0;
    	int len = nums.length;
    	int[] preSum = new int[len + 1];
    	preSum[0] = 0;
    	for(int i = 0 ; i < len ; i++) {
     
    		preSum[i + 1] = preSum[i] +nums[i];
    	}
    	for(int L = 0 ; L < len ; L++) {
     
    		for(int R = L ; R < len ; R++) {
     
    			if(preSum[R + 1] - preSum[L] == k) {
     
    				res++;
    			}
    		}
    	}
    	return res;
    }
}
class Solution {
     
	/*
	* 【前缀和】和【哈希优化】
	* 前缀和是根据顺序(从下标0到某处)算出来的,但算出来后,我们只需要组合出所有的两数相减的情况即可,不需要再去关心顺序
	* 因此,我们每有必要用数组取存前缀和,使用哈希表即可
	* 代码思路:
	* preSum为当前的前缀和(在每次遍历一个数字时与其累加)
	* 获得此时的preSum后,在Map中搜寻是否含有preSum-k的键值(依据是 a - b = k 移项 a -k = b)
	*
	* 另外,由于0和负数的存在,某个前缀和的数值可能出现两次(意味着两处的前缀和相同)
	* 因此这就是key(前缀和)后的value的意义:此前缀和出现的次数 —— 计数的count加的是这个value,而非1
	*/
    public int subarraySum(int[] nums, int k) {
     
    	Map<Integer, Integer> map = new HashMap<Integer, Integer>();
    	map.put(0, 1);
    	int count = 0;
    	int preSum = 0;
    	for(int n : nums) {
     
    		preSum += n;
    		if (map.containsKey(preSum - k)) {
     
                count += map.get(preSum - k);
            }
            map.put(preSum, map.getOrDefault(preSum, 0) + 1);
    	}
    	return count;
    }
}

 

 

【Q36】(md) 有效的数独
 
判断一个 9x9 的数独是否有效。只需要根据以下规则,验证已经填入的数字是否有效即可。
 
数字 1-9 在每一行只能出现一次。 数字 1-9 在每一列只能出现一次。 数字 1-9 在每一个以粗实线分隔的 3x3 宫内只能出现一次。
 
数独部分空格内已填入了数字,空白格用 ‘.’ 表示。

/*
* 只能【暴力法】
* 行遍历和列遍历当然简答,关键是九宫格遍历;其实也不难:
* for(int r = 0 ; r < 3 ; r++) 
       for(int c = 0 ; c < 3 ; c++) 
    		for(int i = 3*r ; i < 3*r+3 ; i++) 
    			 for(int j = 3*c ; j < 3*c+3 ; j++) 
*/
class Solution {
     
	/*
	* 上面的思路是分三次进行遍历(行、列、九宫格);然而,这可以巧妙的一次完成
	* 巧妙之处一:用数组来判断一个数字是否重复
	* 		因为只能是1~9的数字字符,因此声明一个大小为9的数组,每个位置下标恰好代表数字
	* 		当遍历到一个数字时,直接查看该下标对应的是否为true(是true,则重复了;是false,则未重复,该位置置为true)
	* 巧妙之处二:blockIndex = i / 3 * 3 + j / 3
	* 		这个式子使得在正常的遍历二维数组时,也能对应到每个九宫格上去
	* 		比如(0,0)(0,1)(0,2)(1,0)(1,1)(1,2)(2,0)(2,1)(2,2)的blockIndex都是0
	* 		比如(3,3)(3,4)(3,5)(4,3)(4,4)(4,5)(5,3)(5,4)(5,5)的blockIndex都是4
	*/
    public boolean isValidSudoku(char[][] board) {
     
    	// 底下这几个数组之所以是二维的,是因为有9的行,9个列,9个九宫格,用一个数字来标识每一行(列、九宫格)
        // 记录某行,某位数字是否已经被摆放
        boolean[][] row = new boolean[9][9];
        // 记录某列,某位数字是否已经被摆放
        boolean[][] col = new boolean[9][9];
        // 记录某 3x3 宫格内,某位数字是否已经被摆放
        boolean[][] block = new boolean[9][9];

        for (int i = 0; i < 9; i++) {
     
            for (int j = 0; j < 9; j++) {
     
                if (board[i][j] != '.') {
     
                    int num = board[i][j] - '1';	// 这里也巧妙的很!通过ascii码的差值,巧妙实现了从char类型的数字到int的转换
                    int blockIndex = i / 3 * 3 + j / 3;
                    if (row[i][num] || col[j][num] || block[blockIndex][num]) {
     
                        return false;
                    } else {
     
                        row[i][num] = true;
                        col[j][num] = true;
                        block[blockIndex][num] = true;
                    }
                }
            }
        }
        return true;
    }
}

 

 

【Q152】(md) 乘积最大子数组
 
给你一个整数数组 nums ,请你找出数组中乘积最大的连续子数组(该子数组中至少包含一个数字),并返回该子数组所对应的乘积。
 

示例 1:

    输入: [2,3,-2,4]
    输出: 6
    解释: 子数组 [2,3] 有最大乘积 6。
 
示例 2:

    输入: [-2,0,-1]
    输出: 0
    解释: 结果不能为 2, 因为 [-2,-1] 不是子数组。

class Solution {
     
	/*
	 * 【动态规划】
	 * 	特别之处在于,因为负数可能会让最小的变成最大的,因此动态规划有两行:
	 * 	一行记录以此数字结尾的最大值,一行记录以此数字结尾的最小值
	 *  每次遍历一个数字,就填入max和min(注意为了获取max和min,其实是在三个可能的数中进行选择)
	 */
    public int maxProduct(int[] nums) {
     
    	int len = nums.length;
    	if(len == 0)	return 0;
    	   	
    	int[] maxArr = new int[len];
    	int[] minArr = new int[len];
    	int theMax = nums[0];
    	maxArr[0] = nums[0];
    	minArr[0] = nums[0];
    	for(int i = 1 ; i < len ; i++) {
     
    		maxArr[i] = getMax(maxArr[i-1] * nums[i], minArr[i-1] * nums[i], nums[i]);
    		minArr[i] = getMin(maxArr[i-1] * nums[i], minArr[i-1] * nums[i], nums[i]);	// 注意在三个可能的数字中选
    		theMax = Math.max(maxArr[i], theMax);
    	}
    	return theMax;
    }
    private int getMax(int x, int y, int z) {
     
    	int temp = Math.max(x, y);
    	return Math.max(temp, z);
    }
    private int getMin(int x, int y, int z) {
     
    	int temp = Math.min(x, y);
    	return Math.min(temp, z);
    }
}

 
 

 

 

 

 

 

 

 

 

 

Qs from https://leetcode-cn.com
♦ loli suki
♥ end

你可能感兴趣的:(Leetcode,算法,java,leetcode,动态规划)