Java数组笔记及算法练习

Java数组笔记及算法练习

本文档创作于代码随想录算法训练营一期

参考文献链接:代码随想录Java数组完全解析java数组(超详细!)

1.数组基础

1.1一些基本说明
  1. 数组在Java中是一种引用数据类型(不是基本数据类型),父类为Object。
  2. 数组相当于容器,可以存储的数据类型:基本数据类型、引用数据类型。
  3. 数组的存储位置在JVM(java虚拟机)内存划分中的堆内存中,用new来创建的内存空间,是一串连续的内存地址。每一个元素类型相同,因此占用内存空间大小一样。由于很难在内存空间上找到连续的特大空间,因此数组无法存储大量数据。
  4. 元素查询/检索方便,每个元素效率相同。随机删除/添加元素时效率较低。
1.2数组的初始化

常见的两种初始化方式如下:

  1. 静态初始化(指定内容,长度等于内容个数)
  2. 动态初始化(指定长度,默认初始值由数组的字符类型而定)

数组有定长特性,长度一经定下不可修改。需要使用可变长度的“数组”可考虑集合等其他存储结构。

(以下示例以一维数组为主)

静态初始化:

int[] arr = new int[]{1,2,3,4,5};
int[] arr = {1,2,3,4,5};

// 拆分
int[] arr;
arr = new int[]{1,2,3,4,5};

动态初始化:

int[] arr = new int[3];
int arr[] = new int[3];

//拆分
int[] arr;
arr = new int[3];

补充:

Arrays.fill可以快速填充一维数组中的值,从而得到一些特殊用途的数组。多维数组需要利用循环填充。

//使用前需导包java.util.Arrays

//创建[1,1,1,1,1]
int[] arr = new int[5];
Arrays.fill(arr,1);
1.3数组的访问与遍历

利用索引(index)访问数组中的元素,索引从0开始,到数组名.length-1结束。

访问:

//给第4个数据赋值13
arr[3] = 13;
//将第1个数据赋值给i
int i = arr[0];

遍历:

  • for循环遍历

    public static void main(String[] args) { 
        int[] arr = { 1, 2, 3, 4, 5 }; 
        for (int i = 0; i < arr.length; i++) { 
            System.out.println(arr[i]); 
    	}
    }
    
  • foreach遍历(for增强)

    public static void main(String[] args) { 
        int[] arr = { 1, 2, 3, 4, 5 }; 
        for (int i : arr) { 
            System.out.println(i); 
    	}
    }
    

2.常见有关API

2.1Arrays.toString()输出字符串
int[] arr = {1,2,3,4,5,6};
System.out.println(Arrays.toString(arr));
2.2Arrays.asList()将数组转为List,也可用来转为Set
String[] array = {"a", "b", "c", "d"};
System.out.println(array);

List list = new ArrayList(Arrays.asList(array));
System.out.println(list);   // [a, b, c, d]
list.add("GG");             // 可以添加元素
System.out.println(list);   // [a, b, c, d, GG]

Set set = new HashSet(Arrays.asList(array));
System.out.println(set);
2.3List的toArray()转为数组
List list = new ArrayList();
list.add("a");
list.add("b");
list.add("c");
String[] array = new String[list.size()];
list.toArray(array);
for (String s : array)
	System.out.println(s);

3.一些有关数组算法题

双指针思想:二分查找、快慢指针、减少for循环等

在Java中并没有指针这以说法,但是在处理java中的数组时“双指针”这一思路显得比较灵活,可以提高处理效率。

3.1力扣704_二分查找

跳转链接

给定一个 n 个元素有序的(升序)整型数组 nums 和一个目标值 target ,写一个函数搜索 nums 中的 target,如果目标值存在返回下标,否则返回 -1。

输入: nums = [-1,0,3,5,9,12], target = 9
输出: 4
解释: 9 出现在 nums 中并且下标为 4

二分查找这是查找算法中较为常见的一种了,在写数组的二分查找时需要借助左右两个指针分别指向数组的首尾,通过判断中间值来决定移动左右指针中的哪一个指针。

我一开始写这一算法时在纸上将过程画了一遍,代码主要考虑:

  1. 左右两个指针怎么移动?
    • right=mid,left=mid?(我一开始简单的想成了这个,但是会导致指针不动。测试用例:nums=[-1,0,3,5,9,12],target=2)
    • right=mid-1,left=mid+1?
  2. 什么时候运行结束,也就是循环的结束条件是什么?
    • left!=right?(一开始我也是单纯以为这个就可以,但是当只有一个元素时,那么循环就变成无限循环了!测试用例:nums=[5],target=5)
    • left<=right?
class Solution {
    public int search(int[] nums, int target) {
        int left=0,right=nums.length-1;
        int mid=0;
        while(left<=right){
            mid = left+(right-left)/2;
            if(nums[mid]==target){
                return mid;
            }else if(nums[mid]>target){
                right = mid-1;
            }else{
                left = mid+1;
            }
        }
        return -1;
    }
}
3.2力扣27_移除元素

跳转链接

给你一个数组 nums 和一个值 val,你需要 原地 移除所有数值等于 val 的元素,并返回移除后数组的新长度

不要使用额外的数组空间,你必须仅使用 O(1) 额外空间并 原地 修改输入数组。

元素的顺序可以改变。你不需要考虑数组中超出新长度后面的元素。

输入:nums = [3,2,2,3], val = 3
输出:2, nums = [2,2]
解释:函数应该返回新的长度 2, 并且 nums 中的前两个元素均为 2。你不需要考虑数组中超出新长度后面的元素。例如,函数返回的新长度为 2 ,而 nums = [2,2,3,3] 或 nums = [2,2,0,0],也会被视作正确答案。

这道题的第一想法就是使用两个指针来做,大家叫一下这种用法为快慢指针。一个指针在后边等待处理,一个指针在前面寻找可以用来处理的数据。当然,快慢指针也还可以有其他走法,比如一个指针走一步,另一个指针走两步这样子。

(不给图不做多余注释,看不懂就拿个测试案例自己按着代码一步一步画图走一遍)

class Solution {
    public int removeElement(int[] nums, int val) {
        int left = 0;
        int count = 0;
        for(int right=0;right<nums.length;right++){
            if(nums[right]!=val){
                nums[left]=nums[right];
                count++;
                left++;
            }
        }
        return count;
    }
}
3.3力扣944_删列构造

跳转连接

给你由 n 个小写字母字符串组成的数组 strs,其中每个字符串长度相等。

这些字符串可以每个一行,排成一个网格。例如,strs = [“abc”, “bce”, “cae”] 可以排列为:

你需要找出并删除 不是按字典序升序排列的 列。在上面的例子(下标从 0 开始)中,列 0(‘a’, ‘b’, ‘c’)和列 2(‘c’, ‘e’, ‘e’)都是按升序排列的,而列 1(‘b’, ‘c’, ‘a’)不是,所以要删除列 1 。

返回你需要删除的列数。

输入:strs = [“cba”,“daf”,“ghi”]
输出:1
解释:网格示意如下:
cba
daf
ghi
列 0 和列 2 按升序排列,但列 1 不是,所以只需要删除列 1 。

起初写着道题目因为没看懂题目无从下手,后来看别人的解析后才明白,然后根据理解自己敲代码。这道题其实思路很简单,直接嵌套for循环暴力求解就可以做了的。二刷时希望自己可以一遍过。

class Solution {
    public int minDeletionSize(String[] strs) {
        int count=0;
        for(int i=0;i<strs[0].length();i++){
            for(int j=0;j<strs.length-1;j++){
                if(strs[j].charAt(i)>strs[j+1].charAt(i)){
                    count++;
                    break;
                }
            }
        }
        return count;
    }
}
3.4力扣209_长度最小的子数组

跳转链接

给定一个含有 n 个正整数的数组和一个正整数 target 。

找出该数组中满足其和 ≥ target 的长度最小的 连续子数组 [numsl, numsl+1, …, numsr-1, numsr] ,并返回其长度。如果不存在符合条件的子数组,返回 0 。

刚开始做这个题时,把大于等于想成了等于,还有没有看见要求是连续的子数组!

优秀的超时暴力法

class Solution {
    public int minSubArrayLen(int target, int[] nums) {
        int result = 0;
        int sum=0,temp_result=0;
        for(int i=0;i<nums.length;i++){
            sum = 0;
            for(int j=i;j<nums.length;j++){
                sum+=nums[j];
                if(sum>=target){
                    temp_result = j-i+1;
                    if(result==0 || result>temp_result){
                        result = temp_result;
                    }
                    break;
                }
            }
        }
        return result;
    }
}

由于暴力法超时了,所以看了讲解,才知道有移动窗口这种方法,其实思路和暴力法时一样的,只是这里**利用了两个指针巧妙地绕开了一个for循环!**欣赏一下以下这个代码吧:

class Solution {
    public int minSubArrayLen(int target, int[] nums) {
        int left = 0;
        int sum = 0,result = Integer.MAX_VALUE;
        for(int right=0;right<nums.length;right++){
            sum+=nums[right];
            while(sum>=target){
                result = Math.min(result,right-left+1);
                //最精妙的一句代码!
                sum-=nums[left++];
            }
        }
        return result==Integer.MAX_VALUE?0:result;
    }
}
3.5力扣59_螺旋矩阵

跳转链接

给你一个正整数 n ,生成一个包含 1n2 所有元素,且元素按顺时针顺序螺旋排列的 n x n 正方形矩阵 matrix

输入:n = 3
输出:[[1,2,3],[8,9,4],[7,6,5]]

细读这道题在作图写几个案例,根据之前双指针的思想不难想到这道题需要对类似指针的两个坐标i,j处理进而达到遍历矩阵和赋值的功能。

观察n值较小的几个矩阵可以总结出需要依次对矩阵上、右、下、左的数据进行赋值,这样一圈一圈地赋值,直到最里边地一圈停止,因此我们可以得到这个循环单元和结束循环的条件!

直接上代码:

class Solution {
    public int[][] generateMatrix(int n) {
        int nums[][] = new int[n][n];
        int i,j;//i为第i行,j为第j列
        int start = 0;//每次循环的起始行数
        int loop = 0;//矩阵圈离矩阵外的距离,一圈一圈处理,到最里边结束。因此决定了循环次数
        int num = 1;//要写入的数据
        while(loop++ < n/2){
            //上
            for(j=start;j<n-loop;j++){
                nums[start][j] = num++;
            }
            //右
            for(i=start;i<n-loop;i++){
                nums[i][j] = num++;
            }
            //下
            for(;j>=loop;j--){
                nums[i][j] = num++;
            }
            //左
            for(;i>=loop;i--){
                nums[i][j] = num++;
            }
            start++;
        }
        if(n%2==1){
            nums[start][start] = num;
        }
        return nums;
    }
}
  • 33
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 3
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

吞了一只鳄鱼

或许我可以得到一个大鸡腿!

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值