「这是我参与2022首次更文挑战的第20天,活动详情查看:2022首次更文挑战」。
导读
肥友们为了更好的去帮助新同学适应算法和面试题,最近我们开始进行专项突击一步一步来。上一期我们完成了动态规划二十一天现在我们进行下一项对各类算法进行二十八天的一个小总结。还在等什么快来一起肥学进行二十八天挑战吧!!
算法特训二十八天
给你一个正整数数组 arr ,请你计算所有可能的奇数长度子数组的和。
子数组 定义为原数组中的一个连续子序列。
请你返回 arr 中 所有奇数长度子数组的和 。
示例 1:
输入:arr = [1,4,2,5,3]
输出:58
解释:所有奇数长度子数组和它们的和为:
[1] = 1
[4] = 4
[2] = 2
[5] = 5
[3] = 3
[1,4,2] = 7
[4,2,5] = 11
[2,5,3] = 10
[1,4,2,5,3] = 15
我们将所有值求和得到 1 + 4 + 2 + 5 + 3 + 7 + 11 + 10 + 15 = 58
示例 2:
输入:arr = [1,2]
输出:3
解释:总共只有 2 个长度为奇数的子数组,[1] 和 [2]。它们的和为 3 。
示例 3:
输入:arr = [10,11,12]
输出:66
解题思路:
对于每个子数组需要使用 O(n)O(n) 的时间计算子数组的和。如果能将计算每个子数组的和的时间复杂度从 O(n)O(n) 降低到 O(1)O(1),则能将总时间复杂度从 O(n^3)O(n 3 ) 降低到 O(n^2)O(n 2 )。
为了在 O(1)O(1) 的时间内得到每个子数组的和,可以使用前缀和。创建长度为 n + 1n+1 的前缀和数组 \textit{prefixSums}prefixSums,其中 \textit{prefixSums}[0] = 0prefixSums[0]=0,当 1 \le i \le n1≤i≤n 时,\textit{prefixSums}[i]prefixSums[i] 表示数组 \textit{arr}arr 从下标 00 到下标 i - 1i−1 的元素和。
得到前缀和数组 \textit{prefixSums}prefixSums 之后,对于 0 \le \textit{start} \le \textit{end} < n0≤start≤end<n,数组 \textit{arr}arr 的下标范围 [\textit{start}, \textit{end}][start,end] 的子数组的和为 \textit{prefixSums}[\textit{end} + 1] - \textit{prefixSums}[\textit{start}]prefixSums[end+1]−prefixSums[start],可以在 O(1)O(1) 的时间内得到每个子数组的和。
class Solution {
public int sumOddLengthSubarrays(int[] arr) {
int n = arr.length;
int[] prefixSums = new int[n + 1];
for (int i = 0; i < n; i++) {
prefixSums[i + 1] = prefixSums[i] + arr[i];
}
int sum = 0;
for (int start = 0; start < n; start++) {
for (int length = 1; start + length <= n; length += 2) {
int end = start + length - 1;
sum += prefixSums[end + 1] - prefixSums[start];
}
}
return sum;
}
}
给定一个数组 nums,编写一个函数将所有 0 移动到数组的末尾,同时保持非零元素的相对顺序。
请注意 ,必须在不复制数组的情况下原地对数组进行操作。
示例 1:
输入: nums = [0,1,0,3,12]
输出: [1,3,12,0,0]
示例 2:
输入: nums = [0]
输出: [0]
解题思路:
使用双指针,左指针指向当前已经处理好的序列的尾部,右指针指向待处理序列的头部。
右指针不断向右移动,每次右指针指向非零数,则将左右指针对应的数交换,同时左指针右移。
注意到以下性质:
左指针左边均为非零数;
右指针左边直到左指针处均为零。
因此每次交换,都是将左指针的零与右指针的非零数交换,且非零数的相对顺序并未改变。
class Solution {
public void moveZeroes(int[] nums) {
int n = nums.length, left = 0, right = 0;
while (right < n) {
if (nums[right] != 0) {
swap(nums, left, right);
left++;
}
right++;
}
}
public void swap(int[] nums, int left, int right) {
int temp = nums[left];
nums[left] = nums[right];
nums[right] = temp;
}
}
面试题
多线程会产生哪些并发问题 ?
安全性问题:在单线程系统上正常运行的代码,在多线程环境中可能会出现意料之外的结果。
活跃性问题:不正确的加锁、解锁方式可能会导致死锁 or 活锁问题。
性能问题:多线程并发即多个线程切换运行,线程切换会有一定的消耗并且不正确的加锁
Mybatis 如何将对象转换成 SQL?
SQL 绑定是在加载 Mybatis 配置文件,然后扫描到哪个 mapper 子节点,再加载
mapper 映射文件,扫描里面的 SQL 节点,然后封装成对象(MappedStatement,在这个
对象的 SqlSource 封装着 sql 语句)。所有的配置信息保存在 Configuration 类,最后动
态代理执行的时候,取出来封装 sql 的对象,执行 sql。