普通数组
[1- 最大数组和](#1- 最大数组和)
[2- 合并区间](#2- 合并区间)
[3- 轮转数组](#3- 轮转数组)
[4- 除了自身以外数组的乘积](#4- 除了自身以外数组的乘积)
[5- 缺失的第一个正数](#5- 缺失的第一个正数)
1- 最大数组和
给你一个整数数组 nums ,请你找出一个具有最大和的连续子数组(子数组最少包含一个元素),返回其最大和。
子数组是数组中的一个连续部分。
var maxSubArray = function(nums) {
// 贪心算法
// Kadane算法
// currentSum 记录当前的累加和
let currentSum = nums[0]
// maxSum 记录历史最高分(初始化为 nums[0])。
let maxSum = nums[0]
for(let i = 1; i < nums.length; i++){
if (currentSum > 0) {
// 如果前面的累加和是正数,对我有正面贡献,我就加上它
currentSum += nums[i];
} else {
// 如果前面的累加和是负数或0,对我没好处,我直接从当前数字重新开始
currentSum = nums[i];
}
// 更新最高分
maxSum = Math.max(currentSum,maxSum)
}
return maxSum
};
2- 合并区间
以数组 intervals 表示若干个区间的集合,其中单个区间为 intervals[i] = [starti, endi] 。请你合并所有重叠的区间,并返回 一个不重叠的区间数组,该数组需恰好覆盖输入中的所有区间 。
var merge = function(intervals) {
// 合并区间:先升序排序,再判断是否需要合并
if(intervals.length === 0) return []
// 按子区间左端点升序排序
intervals.sort((a,b)=>a[0]-b[0])
// 记录合并后的区间
let result = []
// 取排序后的第一个区间作为合并中的区间
let prev = intervals[0]
// 遍历后续区间,是否重叠需要合并
for(let i =1;i < intervals.length; i++){
// 依次遍历后面的区间
let curr = intervals[i]
// 有重叠:curr的左端点 <= prev的右端点
if(curr[0] <= prev[1]){
// 更新prev的右端点为两个区间中的右端点的较大值
prev[1] = Math.max(prev[1],curr[1])
}else{
// 无重叠,将当前prev存入结果数组
result.push(prev)
// 取curr作为当前prev,进行下一轮比较
prev = curr
}
}
// 最后一个合并好的区间也要存入
result.push(prev)
return result
};
var merge = function(intervals) {
// 和上一个解法逻辑相同
// 但不用另外使用一个prev来维护正在合并中的区间
// 使用result中的最后一个区间作为prev
// 每次比较不是直接在result中添加新的curr就是维护更新旧的prev的右端点
let result = []
intervals.sort((a,b)=>a[0]-b[0])
for(let curr of intervals){
//
if(result.length === 0 || curr[0] > result[result.length - 1][1]){
result.push(curr)
}else{
result[result.length - 1][1] = Math.max(curr[1],result[result.length - 1][1])
}
}
return result
};
3- 轮转数组
给定一个整数数组 nums,将数组中的元素向右轮转 k个位置,其中 k是非负数。
var rotate = function(nums, k) {
// 三步反转法
// 轮转后的结果其实就是把数组分成两部分,然后分别处理
// 1.反转整个数组元素
// 2.反转前k个数组元素
// 3.反转剩余数组元素
// k可能会大于数组长度
k = k%nums.length
// 反转函数
const reverse = (start,end) => {
while(start < end){
[nums[start], nums[end]] = [nums[end], nums[start]]
start++
end--
}
}
// 三步翻转法
reverse(0,nums.length-1)
reverse(0,k-1)
reverse(k,nums.length-1)
// 向右移动 k 位,本质上是把末尾的 k 个元素挪到最前面
// 局部反转将每一部分的顺序修正回正向
};
4- 除了自身以外数组的乘积
给你一个整数数组 nums,返回 数组 answer ,其中 answer[i] 等于 nums 中除了 nums[i] 之外其余各元素的乘积 。
题目数据 保证 数组 nums之中任意元素的全部前缀元素和后缀的乘积都在 32 位 整数范围内。
请 不要使用除法, 且在 O(n) 时间复杂度内完成此题。
var productExceptSelf = function(nums) {
// 前缀积 × 后缀积:(i 左边所有数的乘积) × (i 右边所有数的乘积)
const n = nums.length
// answer维护元素左边的前缀积
let answer = new Array(n)
// 第一个元素左边有没有元素,初始化第一个元素的前缀积为1
answer[0] = 1
// 计算每一个元素的前缀积
for(let i = 1; i < n; i++){
// 当前元素的前缀积 = 前一个元素的前缀积*前一个元素值
answer[i] = answer[i-1] * nums[i-1]
}
// 使用right维护后缀积,逻辑和前缀积一样
let right = 1
for(let i = n-1; i >= 0; i--){
// 此时answer[i]已经是i的前缀积了
// right是i的后缀积
answer[i] = answer[i] * right
// 更新后缀积,为下一个i-1元素准备
right *= nums[i]
}
return answer
};
5- 缺失的第一个正数
给你一个未排序的整数数组 nums ,请你找出其中没有出现的最小的正整数。
请你实现时间复杂度为 O(n) 并且只使用常数级别额外空间的解决方案。
var firstMissingPositive = function(nums) {
// 萝卜回家
// 数组中每一个数字x 呆在数组索引值为x-1的位置
// 判断nums[i] = nums[nums[i] - 1],如果不匹配,进行交换
// 最后返回第一个不在自己索引值位置的正整数i+1(nums[i] != i+1)
const n = nums.length
// 原地交换,让数字 x 放到索引 x-1 的位置上
for(let i = 0; i < n; i++){
// 在合理范围 (1 到 n)
while(nums[i] > 0 && nums[i] <= n && nums[i] !== nums[nums[i] - 1]){
// 在 JavaScript 中,如果你使用 [a, b] = [b, a] 进行变量交换,且前一行代码没有以分号结尾,JS 引擎会把前一行和这一行连起来解析,导致报错。
const targetIndex = nums[i] -1;
// 交换位置
[nums[i], nums[targetIndex]] = [nums[targetIndex], nums[i]]
}
}
// 查找第一个不在自己位置上的正整数
for(let i = 0; i < n; i++){
if(nums[i] !== i +1){
return i+1
}
}
// 如果全部都在位置上,返回 n + 1
return n+1
}