Java数组笔记及算法练习
本文档创作于代码随想录算法训练营一期
参考文献链接:代码随想录,Java数组完全解析,java数组(超详细!)
文章目录
1.数组基础
1.1一些基本说明
- 数组在Java中是一种引用数据类型(不是基本数据类型),父类为Object。
- 数组相当于容器,可以存储的数据类型:基本数据类型、引用数据类型。
- 数组的存储位置在JVM(java虚拟机)内存划分中的堆内存中,用new来创建的内存空间,是一串连续的内存地址。每一个元素类型相同,因此占用内存空间大小一样。由于很难在内存空间上找到连续的特大空间,因此数组无法存储大量数据。
- 元素查询/检索方便,每个元素效率相同。随机删除/添加元素时效率较低。
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
二分查找这是查找算法中较为常见的一种了,在写数组的二分查找时需要借助左右两个指针分别指向数组的首尾,通过判断中间值来决定移动左右指针中的哪一个指针。
我一开始写这一算法时在纸上将过程画了一遍,代码主要考虑:
- 左右两个指针怎么移动?
- right=mid,left=mid?(我一开始简单的想成了这个,但是会导致指针不动。测试用例:nums=[-1,0,3,5,9,12],target=2)
- right=mid-1,left=mid+1?
- 什么时候运行结束,也就是循环的结束条件是什么?
- 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
,生成一个包含 1
到 n2
所有元素,且元素按顺时针顺序螺旋排列的 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;
}
}