本章包括的题目有:
238. 除了自身以外数组的乘积 - 力扣(LeetCode)
1.最大子数组和
思路解析:
定义 dp[i] 表示以数组中第 i 个元素结尾的连续子数组的最大和(此处 i 从 1 开始,对应 nums[i-1])。初始时,dp[0] = 0 表示没有元素时的和为 0。遍历数组,对于每个位置 i,以 nums[i-1] 结尾的最大子数组和有两种选择:将当前元素接在前面的子数组后面,即 dp[i-1] + nums[i-1];从当前元素重新开始一个子数组,即 nums[i-1]。两者取较大值作为 dp[i],同时用变量 m 记录遍历过程中出现的最大值。遍历结束后,m 即为整个数组的最大子数组和。
代码实现:
java
class Solution {
public int maxSubArray(int[] nums) {
int n = nums.length;
int[] dp = new int[n+1];
dp[0] = 0;
int m = Integer.MIN_VALUE;
for (int i = 1; i < n+1; i++) {
dp[i] = Math.max(dp[i-1]+nums[i-1],nums[i-1]);
if(dp[i]>m)m=dp[i];
}
return m;
}
}
时间复杂度O(n)
空间复杂度O(n)
2.合并区间
思路解析:
有一种简单的思路,就是先把每个区间按第一个元素排一个序,然后每次比较最小的两个区间,如果最小的两个区间有重合的,就把它们合并成一个区间。然后再次用这个区间作为最小的区间,和后面的区间比较;如果最小的两个区间没有重合的,就把最小的那个区间放到结果数组里,把第二小的区间去和后面的区间比较。这样遍历完成,就能得到结果。
代码实现:
java
import java.util.ArrayList;
import java.util.PriorityQueue;
class Solution {
public int[][] merge(int[][] intervals) {
PriorityQueue<int[]> pq = new PriorityQueue<>((a, b) -> a[0] - b[0]);
int n = intervals.length;
for (int i = 0; i < n; i++) {
pq.add(intervals[i]);
}
ArrayList<int[]> ret = new ArrayList<>();
int index = 0;
while(!pq.isEmpty()){
int[] arr1 = pq.poll();
if(pq.isEmpty()){
ret.add(arr1);
index ++;
break;
}
int[] arr2 = pq.poll();
if(arr1[1] >= arr2[0]) {
int[] arr3 = new int[2];
arr3[0] = arr1[0];
arr3[1] = Math.max(arr2[1],arr1[1]);
pq.add(arr3);
}else{
ret.add(arr1);
index ++;
pq.add(arr2);
}
}
int[][] ans = ret.toArray(new int[0][]);
return ans;
}
}
时间复杂度:O(n log n)
空间复杂度:O(n)
实现代码可以优化,因为每次将新的数组加入优先级队列的时候,都会产生额外的开销,可以考虑直接将优先级队列中的元素一个个取出来加添加到结果中,每次判定取出来的元素和前一个元素是否有重合部分,如果有的话,就更新前一个结果;没有的话就直接添加到结果数组中。
3.轮转数组
思路解析:
我们可以创建一个新的数组,来储存轮转之后的数组,然后将这个新的数组的值赋值给原来的数组返回就可以了。由于是轮转的原因,所以当k大于数组的长度时,可以对它的长度取模,因为转一圈相当于没转。
代码实现:
java
class Solution {
public void rotate(int[] nums, int k) {
if(k > nums.length) k = k % nums.length;
int[] nums2 = new int[nums.length];
for(int i = 0; i < nums.length; i++){
if(i>=k)nums2[i] = nums[i-k];
else nums2[i] = nums[nums.length-k+i];
}
System.arraycopy(nums2, 0, nums, 0, nums.length);
}
}
时间复杂度:O(n)
空间复杂度:O(n)
当然,我们也可以尝试空间复杂度更优的做法,我们先找到轮转的位置,然后对整个数组进行反转,再对轮转之前的位置进行反转,再对轮转及其之后的位置进行反转,就可以得到结果。

如图,我们假设k=3,进行反转之后就可以得到最后的结果,可以将空间复杂度优化到O(1)
4.除了自身以外数组的乘积
思路解析:
题目说不可以用除法,因此不能将所有的全部乘起来除当前数得到结果。我们计算除了自身以外数字的乘积,其实就是计算自己元素左边元素的累乘和右边元素的累乘之积。我们可以定义两个数组,一个数组 l 表示本元素及左边元素的累乘,一个数组 r 表示本元素及右边元素的累乘。因此结果就是结果数组的ret[i] = l[i - 1] * r [i + 1]。
代码实现:
java
class Solution {
public int[] productExceptSelf(int[] nums) {
long[] l = new long[nums.length];
long[] r = new long[nums.length];
for(int i = 0; i < nums.length; i++){
if(i != 0){
l[i] = nums[i] * l[i - 1];
r[nums.length - 1 - i] = nums[nums.length - 1 - i] * r[nums.length - i];
}
else {
l[i] += nums[i];
r[nums.length - 1 - i] += nums[nums.length - 1 - i];
}
}
int[] ret = new int[nums.length];
for(int i = 0; i < nums.length; i++){
if(i == 0){
ret[i] = (int)r[i + 1];
continue;
}
if(i == nums.length - 1){
ret[i] = (int)l[i - 1];
continue;
}
ret[i] = (int)(l[i - 1] * r [i + 1]);
}
return ret;
}
}
时间复杂度:O(n)
空间复杂度:O(n)
我们同样可以尝试优化我们的空间复杂度,思路如下,我们不需要引入额外的两个累乘数组,而是直接将本位置左边累乘的结果存到结果数组中,第二遍循环,逆着更新结果数组的值即可(定义一个常数变量,记录从右边开始累乘的值,依次乘到结果数组中去),这样就可以将空间复杂度优化到O(1)。
5.缺失的第一个正数
思路解析:
由于题目要求时间复杂度:O(n), 空间复杂度:O(1),因此我们只能对原数组进行操作,且不能进行排序操作。由题意可以联想到用哈希的方法去依次寻找有没有对应的值,但是这里不允许有额外的 n 的空间的开销,因此我们只能利用原数组进行哈希。思路为遍历数组,每遍历到一个位置,得到那个数,就把它填到本该属于它的地方(例如把数字1填到位置0),但是由于这样做会覆盖这个地方它原本的数,所以我们需要定义变量去储存这个原本的数,再把原本的数放到它本该属于的地方,以此类推,直到1.它本该属于的地方不在数组的范围之内,可以跳出循环2.它本来就属于这里。这样操作过后,每个数字至多被判断两遍,且只有常数级的开销。
代码实现:
java
class Solution {
public int firstMissingPositive(int[] nums) {
int pre = 0,cur = 0;
for (int i = 0; i < nums.length; i++){
if(nums[i] > nums.length || nums[i] < 1) continue;
if(nums[nums[i] - 1] == nums[i])continue;
else{
cur = nums[i];
while(true){
pre = cur;
if(pre - 1 < 0 || pre > nums.length || nums[pre - 1] == pre)break;
cur = nums[pre - 1];
nums[pre - 1] = pre;
}
}
}
for (int i = 0; i < nums.length; i++) {
if(nums[i] != i + 1) return i + 1;
}
return nums.length + 1;
}
}
上一章:
LeetCode热题100(Java)(4)子串-CSDN博客
下一章: