53最大子数组和
题目:给你一个整数数组 nums ,请你找出一个具有最大和的连续子数组(子数组最少包含一个元素),返回其最大和。子数组是数组中的一个连续部分。
此题是贪心算法。局部最优:当前"连续和"为负数的时候立刻放弃,从下一个元素重新计算"连续和",因为负数加上下一个元素 "连续和"只会越来越小。
全局最优:选取最大"连续和"
局部最优的情况下,并记录最大的"连续和",可以推出全局最优。
从代码角度上来讲:遍历 nums,从头开始用 count 累积,如果 count 一旦加上 nums[i]变为负数,那么就应该从 nums[i+1]开始从 0 累积 count 了,因为已经变为负数的 count,只会拖累总和。
区间终止位置不用调整么? 如何才能得到最大"连续和"呢?区间的终止位置,其实就是如果 count 取到最大值了,及时记录下来了。
java
class Solution {
public int maxSubArray(int[] nums) {
int result=Integer.MIN_VALUE;
int count=0;
for(int i=0;i<nums.length;i++){
count=count+nums[i];
result=count>result?count:result;
if(count<0){
count=0;
}
}
return result;
}
}
56合并区间
以数组 intervals 表示若干个区间的集合,其中单个区间为 intervals[i] = [starti, endi] 。请你合并所有重叠的区间,并返回一个不重叠的区间数组,该数组需恰好覆盖输入中的所有区间。
示例 1:输入:intervals = [[1,3],[2,6],[8,10],[15,18]],输出:[[1,6],[8,10],[15,18]]
解释:区间 [1,3] 和 [2,6] 重叠, 将它们合并为 [1,6]
先排序,让所有的相邻区间尽可能的重叠在一起,按左边界,或者右边界排序都可以,处理逻辑稍有不同。按照左边界从小到大排序之后,如果 intervals[i][0] <= intervals[i - 1][1] 即intervals[i]的左边界 <= intervals[i - 1]的右边界,则一定有重叠。(本题相邻区间也算重贴,所以是<=)
如何去模拟合并区间呢?其实就是用合并区间后左边界和右边界,作为一个新的区间,加入到result数组里就可以了。如果没有合并就把原区间加入到result数组。
java
class Solution {
public int[][] merge(int[][] intervals) {
List<int[]> result=new LinkedList<>();
Arrays.sort(intervals,(x,y)->Integer.compare(x[0],y[0]));
int start=intervals[0][0];
int right=intervals[0][1];
for(int i=1;i<intervals.length;i++){
if(intervals[i][0]>right){
result.add(new int[]{start,right});
start=intervals[i][0];
right=intervals[i][1];
}else{
right=Math.max(right,intervals[i][1]);
}
}
result.add(new int[]{start,right});
return result.toArray(new int[result.size()][]);
}
}
189轮转数组
题目:给定一个整数数组 nums,将数组中的元素向右轮转 k 个位置,其中 k 是非负数。
示例 1:输入: nums = [1,2,3,4,5,6,7], k = 3,输出: [5,6,7,1,2,3,4]
解释:向右轮转 1 步: [7,1,2,3,4,5,6]
向右轮转 2 步: [6,7,1,2,3,4,5]
向右轮转 3 步: [5,6,7,1,2,3,4]
直接做开辟新空间:
java
class Solution {
public void rotate(int[] nums, int k) {
int[] result=new int[nums.length];
for(int i=0;i<nums.length;i++){
result[(i+k)% nums.length]=nums[i];
}
System.arraycopy(result,0,nums,0,nums.length);
}
}
语法补充:System.arraycopy(newArr, 0, nums, 0, n);的解释
newArr 是源数组。nums 是目标数组。n 是一个整数,表示要复制的元素个数。
srcPos = 0:从源数组 newArr 的索引 0(第一个元素)开始复制。
destPos = 0:从目标数组 nums 的索引 0(第一个位置)开始粘贴。
length = n:总共复制 n 个元素。
整体作用:将 newArr 的前 n 个元素复制到 nums 的前 n 个位置,相当于用 newArr 的前 n 个元素覆盖 nums 的前 n 个元素。
使用数组翻转:
原理如下,以示例一为例:

java
class Solution {
public void rotate(int[] nums, int k) {
int n=nums.length;
k=k%n;
reverse(nums,0,n-1);
reverse(nums,0,k-1);
reverse(nums,k,n-1);
}
private void reverse(int[] nums,int i,int j){
while(i<j){
int temp=nums[i];
nums[i]=nums[j];
i++;
nums[j]=temp;
j--;
}
}
}
238除了自身以外数组的乘积
题目:给你一个整数数组 nums,返回数组 answer ,其中 answer[i] 等于 nums 中除了 nums[i] 之外其余各元素的乘积 。题目数据保证数组 nums之中任意元素的全部前缀元素和后缀的乘积都在 32 位整数范围内。请不要使用除法,且在 O(n) 时间复杂度内完成此题。
示例 1: 输入: nums = [1,2,3,4],输出: [24,12,8,6]
使用双指针,左边就是nums[i]左边所有元素的乘积,右边就是nums[i]右边所有元素的乘积。用一个数组来同时操作两端。
java
class Solution {
public int[] productExceptSelf(int[] nums) {
int n=nums.length;
int[] result=new int[n];
Arrays.fill(result,1);
int leftnum=1;
int rightnum=1;
for(int i=0,j=n-1;i<n;i++,j--){
result[i]=result[i]*leftnum;
result[j]=result[j]*rightnum;
leftnum=leftnum*nums[i];
rightnum=rightnum*nums[j];
}
return result;
}
}
41缺失的第一个正数
题目:给你一个未排序的整数数组 nums ,请你找出其中没有出现的最小的正整数。请你实现时间复杂度为 O(n) 并且只使用常数级别额外空间的解决方案。
示例 1:输入:nums = [1,2,0],输出:3
解释:范围 [1,2] 中的数字都在数组中。
示例 2:输入:nums = [3,4,-1,1],输出:2
解释:1 在数组中,但 2 没有。
原地哈希方法。它的核心思想是:利用数组的下标来标记数字是否出现过。
对于一个长度为 n 的数组,缺失的最小正整数一定在 1 到 n+1 之间。为什么?
如果数组包含了 1,2,3,...,n 这全部 n 个正整数,那么答案就是 n+1。否则,至少有一个 1~n 之间的数缺失,答案就是那个最小的缺失数。因此我们只需要关心数组中的数字是否在 [1, n] 范围内,对于小于等于 0 或大于 n 的数字可以忽略。
我们可以把数组想象成有 n 个座位,编号从 0 到 n-1。每个座位应该坐一个学号,学号 k 应该坐在座位 k-1 上(因为下标从 0 开始)。比如学号 1 应该坐在座位 0,学号 2 坐在座位 1,依此类推。
目标:通过交换,把所有在 [1, n] 范围内的数字放到它们"应该"在的位置上。这样,如果某个座位上的学号与座位编号不匹配,就说明那个学号缺失了。
nums 中可能有重复元素。假设 nums=[1,1,2]。从 nums[1] 开始。这个座位上的学生坐在正确的座位上。继续遍历,nums[2]=1,这是 1 号学生的影分身。由于 1 号学生的真身已经坐在正确的座位上,我们可以在第二次遍历中知道「数组中有 1」这个信息,所以可以忽略 nums[2],向后遍历。
条件 nums[nums[i] - 1] != nums[i] 是为了避免死循环:如果目标位置已经是对的数字,说明当前数字其实已经有一个副本在正确位置(例如数组中有重复数字),那么交换没有意义,直接跳过。实际上,如果当前数字和它目标位置的数字相等,就不需要交换。
java
class Solution {
public int firstMissingPositive(int[] nums) {
int n=nums.length;
for(int i=0;i<n;i++){
while(1<=nums[i] && nums[i]<=n && nums[nums[i]-1]!=nums[i]){
int j=nums[i]-1;
int temp=nums[i];
nums[i]=nums[j];
nums[j]=temp;
}
}
for(int i=0;i<n;i++){
if(nums[i]!=i+1){
return i+1;
}
}
return n+1;
}
}