文章目录
- 引言
- 新作
- 最大子数组和
- 个人实现
- 参考实现
- 合并区间
- 个人实现
- 短板补充——自定义排序标准
- 参考实现
- 转轮数组
- 最终实现
- 除自身以外数组的乘积
- 个人实现
- 总结
引言
- 以前刷题的方式方法有问题,花太多时间了,应该先过一遍,然后再针对特定的题目好好刷,现在是广度上不来,还是有所欠缺的,以后的刷题方式改为:
- 五分钟没思路,直接看题解,然后看完题解在写代码,还是写不出来,去看代码!
新作
最大子数组和
-
今天做腾讯的面试,遇到了这道题,我不会做,然后直接使用暴力搜索做的,然后其他方法没有想到!
-
题目链接
注意事项
- 元素的个数是10的五次方
- 元素可能为负数
- 时间复杂度是O(n)
- 进一步可以尝试使用分治去解决
个人实现
- 这里想的是能不能保存一下,到达i是,前n项中,最小的序列,然后再遍历一下不同的序列!
- 难受,这里想起来了,使用动态规划,实现,一下子调出来了,可惜了了!
class Solution {
public int maxSubArray(int[] nums) {
int len = nums.length;
int[] minSeq = new int[nums.length];
int[] sumValue = new int[nums.length];
sumValue[0] = nums[0];
// 这里是计算对应前n项和
minSeq[0] = Math.min(nums[0],0);
for(int i = 1;i < len;i ++){
sumValue[i] = nums[i] + sumValue[ i -1];
minSeq[i] = Math.min(minSeq[i - 1],sumValue[i]);
}
System.out.println(Arrays.toString(minSeq));
// 再次遍历计算最值
int res = nums[0];
for(int i = 0;i < len;i ++){
if(i >= 1)
res = Math.max(sumValue[i] - minSeq[i - 1],res);
}
return res;
}
}
参考实现
-
这里本质上是采用了区间DP,同时使用滚动数组进行优化,先说一下动态数组表示的数据。
- f[i]表示以节点i为结尾的所有序列的最大值
- 最终的状态应该是这样,按照序列的长度的进行划分
- 序列的长度为1,仅仅包含节点i
- 序列的长度大于等于二,但是都包含对应f[i - 1]个元素,所以应该是f[i - 1] + nums[i]
- 最终应该选择最大值所以f[i] = Math.max(nums[i],f[i - 1] + nums[i]);
-
然后使用滚动数组进行优化,从前往后进行遍历,i从零变成n
- 然后就是last = Math.max(nums[i],last + nums[i])
-
最后每一次在比较一下最大值的
class Solution {
public int maxSubArray(int[] nums) {
if(nums.length == 1) return nums[0];
int res = Integer.MIN_VALUE,last = 0;
for(int i = 0;i < nums.length;i ++){
last = Math.max(nums[i],last + nums[i]);
res = Math.max(last,res);
}
return res;
}
}
合并区间
- 题目连接
个人实现
- 这道题单纯使用的方法是模拟,先按照区间的起点进行排序,然后逐个元素进行比较,进行合并区间,这个时候主要关注的是第二个区间大小关系!
class Solution {
public int[][] merge(int[][] nums) {
List<int[]> list = new ArrayList<>();
List<int[]> numsList = new ArrayList<>();
for(int[] row : nums) numsList.add(row);
numsList.sort(Comparator.comparingInt(a -> a[0]));
// 定义左右边界
int l = numsList.get(0)[0],r = numsList.get(0)[1];
for(int i = 1;i < nums.length;i ++){
if(numsList.get(i)[0] <= r && numsList.get(i)[0] >= l) r = Math.max(r,numsList.get(i)[1]);
else{
list.add(new int[]{l,r});
l = numsList.get(i)[0];
r = numsList.get(i)[1];
}
}
list.add(new int[]{l,r});
int[][] res = new int[list.size()][2];
for(int i = 0;i <list.size();i ++){
res[i][0] = list.get(i)[0];
res[i][1] = list.get(i)[1];
}
return res;
}
}
在完成这个题目的过程中,遇到了一些问题,不知道关于二维数组的排序怎么实现,如何自定义对于一个对象的排序功能。
短板补充——自定义排序标准
Arrays.sort是不支持二维数组排序的,只能对基本数据类型进行排序,这里要将之保存为对应的集合进行保存!
- Collections.sort进行排序
- Comparator指定排序地元素
- 指定两个元素,并使用lambda表达式进行实现
import java.util.*;
class Main{
public static void main(String args[]){
List<int[]> list = new ArrayList<>();
int[][] nums = {{1,2,3},{4,5,6},{6,7,8},{4,9,0}};
for(int[] x:nums){
list.add(x);
}
// 排序方式1
Collections.sort(list, Comparator.comparingInt(p -> p[0]));
for(int[] x:list)
System.out.println(Arrays.toString(x));
// 排序方式2
Collections.sort(list, (p1,p2)->Integer.compare(p2[0] , p1[0]));
for(int[] x:list)
System.out.println(Arrays.toString(x));
}
}
使用Arrays.sort重构进行排序
- 新创建一个Comparator兑现
Arrays.sort(intervals,new Comparator<int[]>(){
public int compare(int[] interval1,int[] interval2){
return interval1[0] - interval2[0];
}
});
参考实现
参考代码的思路基本上跟我是一致的,但是写的比我的简洁,他是直接和数组中最后一个元素进行比较的,我每次都是单独和l或者r进行比较,然后费了精力!
class Solution {
public int[][] merge(int[][] nums) {
if(nums.length == 0) return new int[0][2];
Arrays.sort(nums,new Comparator<int[]>(){
public int compare(int[] interval1,int[] interval2){
return interval1[0] - interval2[0];
}
});
List<int[]> list = new ArrayList<>();
for(int[] x:nums){
int l = x[0];
int r = x[1];
if(list.size() == 0 || list.get(list.size() - 1)[1] < l){
list.add(new int[]{l,r});
}else{
list.get(list.size() - 1)[1] = Math.max(list.get(list.size() - 1)[1],r);
}
}
}
return list.toArray(new int[list.size()][]);
}
转轮数组
- 题目链接
注意 - 数组的长度是10的五次方
- 数组元素可以使用int进行存储,但是一旦进行运算,会超过界限
- 时间复杂度是O(1)
最终实现
这里创建了一个新的数组进行复制,然后这道题应该就是索引的变化,马上要面试了,有点紧张,就不推导了!
class Solution {
public void rotate(int[] nums, int k) {
int n = nums.length;
int[] newArr = new int[n];
for(int i = 0;i < n;i ++){
newArr[(i + k) % n] = nums[i];
}
System.arraycopy(newArr, 0, nums, 0, n);
}
}
除自身以外数组的乘积
题目连接
注意
- 时间复杂度是O(n)解决即可!
个人实现
- 这个题目完全是模拟,时间复杂度是限定在O(n),所以不能出现嵌套循环,串行两个循环,计算对应的前缀和和后缀和即可!
class Solution {
public int[] productExceptSelf(int[] nums) {
int m = nums.length;
int[] preVal = new int[m];
int[] nextVal = new int[m];
for(int i = 0;i < m;i ++) {
if(i < 1) preVal[i] = nums[i];
else preVal[i] = preVal[i - 1] * nums[i];
}
//System.out.println(Arrays.toString(preVal));
for(int i = m - 1;i >= 0 ;i--){
if(i == m - 1) nextVal[i] = nums[i];
else nextVal[i] = nextVal[i + 1] * nums[i];
}
//System.out.println(Arrays.toString(nextVal));
int[] res = new int[m];
for(int i = 0;i < m;i ++){
int pre = 1;
if(i > 0) pre = preVal[i - 1];
int next = 1;
if(i < m - 1) next = nextVal[i + 1];
res[i] = pre * next;
}
return res;
}
}
总结
- 拖得有点久了,差不多两三天才做完,数组这章,感觉没啥挑战!比较适合模拟!