Java数组相关基础知识总结
一维数组
int[] ids;//声明
//1.1 静态初始化:数组的初始化和数组元素的赋值操作同时进行
ids = new int[]{1001,1002,1003,1004};
//1.2动态初始化:数组的初始化和数组元素的赋值操作分开进行
int[] names = new int[5];
int[] arr4 = {1,2,3,4,5};//类型推断
二维数组
//1.1 规则矩形数组,其中每一行都具有相同数量的列,类似于一个表格。
int rows = 4; // 行数 也就是子数组的个数
int columns = 5; // 列数 也就是子数组的大小
int[][] rectangularArray = new int[rows][columns];
//1.2 不规则数组,其中每一行可以具有不同数量的列
int[][] raggedArray = new int[rows][];
可变数组
ArrayList:线程不安全的,效率高;底层使用Object[] elementData存储
Vector:线程安全的,效率低;底层使用Object[] elementData存储
1.二分查找
题目链接:二分查找
✅ 二分法易错点总结:
(1)外层循环: while(left<right) or while(left<=right)
(2)left=middle+1; or left=middle;
(3)right=middle-1; or right=middle;
其本质是你如何定义区间,这里采用闭区间的方法,即left和right都有可能等于target。那么(1)终止条件应该是while(left<=right),因为存在target=right=left的可能。(2)采用边界加减1的方法:left=middle+1;right=middle-1;因为此时已知target!=middle,middle应该被排除在区间外;
✅ 防止(left+right)溢出
mid=(left+right)/2; ===> mid=left+(right-left)/2;
class Solution {
public int search(int[] nums, int target) {
int left=0;
int right=nums.length-1;
while(left<=right){
int mid=left+(right-left)/2;
if(target==nums[mid]){
return mid;
}
else if(target>nums[mid]){
left=mid+1;
}
else{
right=mid-1;
}
}
return -1;
}
}
2.在排序数组中查找元素的第一个和最后一个位置
题目链接:在排序数组中查找元素的第一个和最后一个位置
✅ 简单易懂的方法
给定的是有序数组,很容易想到先通过二分法,否则暴力搜索的话就没有利用有序的这个特性。
(1)通过二分法找到一个和target相等的元素。
(2)再通过循环移动左右指针,寻找第一个和最后一个位置的位置。
class Solution {
public int[] searchRange(int[] nums, int target) {
int left=0,right=nums.length-1;
int start=-1,end=-1;
//二分法找到一个和target相等的元素
while(left<=right){
int mid=(left+right)/2;
if(target==nums[mid]){
start=end=mid;
break;
}
else if(target>nums[mid]){
left=mid+1;
}
else{
right=mid-1;
}
}
if(start>-1){
//左右指针移动寻找开始和结束的位置
while(start>-1 && nums[start]==target){
start--;
}
while(end<nums.length && nums[end]==target){
end++;
}
return new int[]{start+1,end-1};
}
return new int[]{-1,-1};
}
}
✅ 进阶方法
使用闭区间二分法时,left指针其实具有特性。
我就假设left最终会停在第一个>=x的位置,由于循环条件是while(left<=right),那么最后时刻right一定停在left的左边。
其他判断保持不变,但当target=nums[mid]时,由于需要保持left最终会停在第一个>=x的假设前提,应该使得right=mid-1,left会不断靠近right并最终停在右侧即left=mid。(可以想象一下,如果使得left=mid+1,right会不断靠近left(mid+1),并最终停在左侧即right=mid left=mid+1不符合假设)。
所以二分法最重要的就是最终时刻,可以想象清楚,倒推各种条件即可。
参考:灵山茶b站二分法
class Solution {
public int[] searchRange(int[] nums, int target) {
int left=0,right=nums.length-1;
//假设left最终会停在第一个>=x的位置
while(left<=right){
int mid=(left+right)/2;
if(target<=nums[mid]){
right=mid-1;
}
else{
left=mid+1;
}
}
if(left>nums.length-1 || nums[left]!=target){
return new int[]{-1,-1};
}
//只需循环找到结尾
int end=left;
while(end<nums.length && nums[end]==target){
end++;
}
return new int[]{left,end-1};
}
}
3.搜索插入的位置
题目链接:搜索插入的位置
题目可翻译为在有序数组中找到第一个>=target的位置,所以和上一题一样直接返回left即可。
class Solution {
public int searchInsert(int[] nums, int target) {
int left=0;
int right=nums.length-1;
while(left<=right){
int mid=(left+right)/2;
if(target>nums[mid]){
left=mid+1;
}
else{
right=mid-1;
}
}
return left;
}
}
4.x 的平方根
题目链接:x 的平方根
关键就是想到正整数问题可以用二分法解决。题目可翻译为在[0,X]中找到第一个平方<=target的位置。所以假设right是第一个平方<=target的数。那么当mid * mid=target时,应该移动的是left指针,最终返回right指针。
✅ 注意平方会溢出
Java中,int : -2^31 到 2^31-1
long : -2^63 到 2^63-1
long square = (long)mid*mid
class Solution {
public int mySqrt(int x) {
int left=1, right=x;
while(left<=right){
int mid=left+(right-left)/2;
if(x>=(long)mid*mid){
left=mid+1;
}
else{
right=mid-1;
}
}
return right;
}
}
5.移除元素
题目链接:移除元素
原地将数组的前k个元素全部变成不等于val的。也就是将k之后的不等于val的元素和k之前等于k的元素互换。故想到使用双指针。
(1)左指针从前往后遍历找到==val的元素。
(2)右指针从后往前遍历找到!=val的元素。
✅ 思路较为简单,关键是边界条件。
当循环条件是数组索引时,一定注意不要超出边界范围。
class Solution {
public int removeElement(int[] nums, int val) {
int left=0,right=nums.length-1;
int k=nums.length;
while(left<=right){
if(nums[left]==val){
//从右往前找到不等于val的数
while(nums[right]==val){
k--;
right--;
if(right<left){
return k;
}
}
nums[left]=nums[right];
right--;
k--;
}
left++;
}
return k;
}
}
6.有序数组的平方
题目链接:有序数组的平方
有点类似于合并两个有序数组,左指针从前往后,右指针从后往前,通过比较大小添加和移动。小难点在于指针的平方是从大到小的,但结果集要求从小到大的顺序,故而想到从后往前填充结果集。
class Solution {
public int[] sortedSquares(int[] nums) {
int left=0,right=nums.length-1;
int[] array = new int[nums.length];
int i=nums.length-1;
while(left<=right){
if(nums[left]*nums[left]<nums[right]*nums[right]){
array[i]=nums[right]*nums[right];
right--;
}
else{
array[i]=nums[left]*nums[left];
left++;
}
i--;
}
return array;
}
}