开启掘金成长之旅!这是我参与「掘金日新计划 · 2 月更文挑战」的第 1 天
本文通过分析3道经典leetcode题的解题思路和代码,对java数组进行总结
和ArrayList和Vector一样,同样的类似关系的类还有HashMap和HashTable,StringBuilder和StringBuffer,后者是前者线程安全版本的实现。
java数组底层结构:
classDiagram
Collection <|-- ArrayList
Collection : + add(object)
Collection : + remove(object)
class ArrayList{
+add()
+get()
+remove()
}
温故知新
在java中,数组可以用来存储相同数据类型的一组数据,创建数组的方法如下:
- 声明数组变量
dataType[] myList或者dataType myList[]
推荐使用第一种,举个例子:int[] myList
在内存中生成了一个myList数组变量,但还没有生成数组,也没有为数组分配空间 - 创建数组
为了在内存中给数组分配地址,java中采用new方式创建数组new datatype[length]
把数组变量指向该地址即可:int[] myList = new int[10] - 初始化数组
- 默认int数组初始化为0
- 手动初始化可以通过遍历赋值
myList[i] - 直接赋值
int[] myList = new int[]{1,2,3,4}或myList = {1,2,3,4}
扩容机制
new ArrayList()初始大小是0,当有数据插入时,默认大小DEFAULT_CAPACITY = 10
当添加元素时,如果元素个数+1> 当前数组长度 【size + 1 > elementData.length】时,进行扩容,扩容后的数组大小是: oldCapacity + (oldCapacity >> 1)
扩容倍数为什么k=1.5?
- 充分利用前面已经释放的空间。如果k >= 2,新容量刚刚好永远大于过去所有废弃的数组容量
- 充分利用移位操作(右移一位),减少浮点数或者运算时间和运算次数
题目一 双指针法
解题思路
| 思路 | 问题 | 改进 |
|---|---|---|
| 双指针 | 没有负数怎么办 | 包含在有负数的情况中 |
| 有负数怎么办 | left指针和right指针指向元素平方进行比较 | |
| 新数组从哪里开始更新 | 从大到小 |
代码
class Solution {
public int[] sortedSquares(int[] nums) {
int left = 0;
int right = nums.length - 1;
int[] result = new int[nums.length];
// if(right == 0){
// result[0] = nums[0] * nums[0];
// return result;
// }
int i = result.length - 1;
while(left <= right && i>=0) {
if(nums[left]*nums[left] < nums[right]*nums[right]) {
result[i] = nums[right]*nums[right];
right --;
}else{
result[i] = nums[left]*nums[left];
left ++;
}
i--;
}
return result;
}
}
小结
- 因为有序数组首先满足有序条件,所以考虑特殊解法降低算法复杂度
- 平方和最大的一定在原数组的两头
题目二 滑动窗口法
解题思路
| 思路 | 问题 | 改进 |
|---|---|---|
| 暴力 | 双循环遍历,n^2 | 滑动窗口 |
| 滑动窗口 | 对于每一个j[0,len-1],寻找i,使得j-i最小 | while(j<len) |
| [i,j]怎么更新i | 判断[i,j]区间满足题意,固定j,while(满足题意) i++更新result | |
| [i,j]怎么更新j | 判断[i,j]区间满足题意,不满足j++ |
代码
原始思路:
存在问题:
- 判断是否满足条件利用了sum,但是每次进入第一个while循环,局部变量sum都要重新计算
改进:
- 将sum做为全局变量,初始化
sum=nums[0],每次进循环sum += nums[j] - 更新minlen时,一定一定记得更新sum,
sum -= nums[i]
问题:j一定一定一定要从0开始
正确代码:
class Solution {
public int minSubArrayLen(int target, int[] nums) {
int i = 0;
int j = 0;
int minlen = Integer.MAX_VALUE;
int sum = 0;
while(j<nums.length) {
sum += nums[j];
while(i<=j && sum >= target){
minlen = Math.min(minlen,j-i+1);
//固定j,滑动i
sum -= nums[i];
i++;
}
j++;
}
return minlen == Integer.MAX_VALUE ? 0 : minlen;
}
}
小结
- 滑动窗口模板(参考leetcode题解)
while(j从0到len-1){
判断[i,j]满足条件所需变量
while([i,j]合法 && [i,j]满足条件){
不断更新结果(注意在while内更新!)
i++(最大程度的压缩i,使得滑窗尽可能的小)
}
j++
}
- 注意问题
- minlen初始化为
Integer.MAX_VALUE return minlen == Integer.MAX_VALUE ? 0 : minlen判断minlen是否更新过,若没有的话返回0
题目三
解题思路
| 思路 | 问题 | 改进 |
|---|---|---|
| 找规律 | 需要定义哪些变量 | 矩阵下标i、j |
| 当前处于第几层offset | ||
| 分单双数的情况 | 双数 | 循环n/2次 |
| 单数 | 循环n/2-1次,最后一次只有一个数字 |
代码
class Solution {
public int[][] generateMatrix(int n) {
int[][] result = new int[n][n];
int offset = 0;
int num = 1;
int loop = 0;
if(n % 2 == 0) {
while(loop < n / 2) {
int i = loop;
int j = loop;
for(; j<n-offset-1;j++) {
result[i][j] = num++;
}
for(;i<n-offset-1;i++) {
result[i][j] = num++;
}
for(;j>offset;j--) {
result[i][j] = num++;
}
for(;i>offset;i--) {
result[i][j] = num++;
}
offset ++;
loop ++;
}
}else {
while(loop < (n-1) / 2) {
int i = loop;
int j = loop;
for(; j<n-offset-1;j++) {
result[i][j] = num++;
}
for(;i<n-offset-1;i++) {
result[i][j] = num++;
}
for(;j>offset;j--) {
result[i][j] = num++;
}
for(;i>offset;i--) {
result[i][j] = num++;
}
offset ++;
loop ++;
}
result[n/2][n/2] = n*n;
}
return result;
}
}
小结
这道题难度不大,但是需要细心,注意以下几点即可:
- 分情况讨论
- 注意i--和j--的最小值是offset