数组part01 + 数组知识总结

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;
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值