【数据结构】数组 | 算法总结

82 阅读3分钟

开启掘金成长之旅!这是我参与「掘金日新计划 · 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中,数组可以用来存储相同数据类型的一组数据,创建数组的方法如下:

  1. 声明数组变量
    dataType[] myList或者dataType myList[]
    推荐使用第一种,举个例子:int[] myList
    在内存中生成了一个myList数组变量,但还没有生成数组,也没有为数组分配空间
  2. 创建数组
    为了在内存中给数组分配地址,java中采用new方式创建数组 new datatype[length]
    把数组变量指向该地址即可:int[] myList = new int[10]
  3. 初始化数组
  • 默认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?

  1. 充分利用前面已经释放的空间。如果k >= 2,新容量刚刚好永远大于过去所有废弃的数组容量
  2. 充分利用移位操作(右移一位),减少浮点数或者运算时间和运算次数

题目一 双指针法

977. 有序数组的平方 - 力扣(LeetCode)

image.png

解题思路

思路问题改进
双指针没有负数怎么办包含在有负数的情况中
有负数怎么办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;
    }
}

小结

  1. 因为有序数组首先满足有序条件,所以考虑特殊解法降低算法复杂度
  2. 平方和最大的一定在原数组的两头

题目二 滑动窗口法

209. 长度最小的子数组 - 力扣(LeetCode)

image.png

解题思路

思路问题改进
暴力双循环遍历,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++

代码

原始思路:

image.png

存在问题:
  1. 判断是否满足条件利用了sum,但是每次进入第一个while循环,局部变量sum都要重新计算
改进:
  1. 将sum做为全局变量,初始化sum=nums[0],每次进循环sum += nums[j]
  2. 更新minlen时,一定一定记得更新sum,sum -= nums[i]

image.png
问题: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;
    }
}

小结

  1. 滑动窗口模板(参考leetcode题解)
while(j从0到len-1){
    判断[i,j]满足条件所需变量
    while([i,j]合法 && [i,j]满足条件){
        不断更新结果(注意在while内更新!)
        i++(最大程度的压缩i,使得滑窗尽可能的小)
    }
    j++
}
  1. 注意问题
  • minlen初始化为Integer.MAX_VALUE
  • return minlen == Integer.MAX_VALUE ? 0 : minlen判断minlen是否更新过,若没有的话返回0

题目三

59. 螺旋矩阵 II - 力扣(LeetCode)

image.png

解题思路

思路问题改进
找规律需要定义哪些变量矩阵下标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;
        
    }
}

小结

这道题难度不大,但是需要细心,注意以下几点即可:

  1. 分情况讨论
  2. 注意i--和j--的最小值是offset