【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