[力扣]数组

  • 不同题涉及到不同解题方法 [有必要复习]
  • 二分法+双指针+滑动窗口+模拟行为

代码随想录[原文指路]

概述

  • 连续存储空间,存相同类型数据的集合
  • 二维数组内存空间
    在这里插入图片描述

二分查找

1.二分查找[704]

  • 前提:数组元素有序不重复
  • 时间复杂度:O(logn)
  • 注意
left与right边界取值情况

2.搜索插入位置[35]

  • 分析
    请添加图片描述
  • 暴力解法
时间复杂度:O(n)
------------------------
class Solution {
    public int searchInsert(int[] nums, int target) {
        for(int i=0;i<nums.length;i++){
            if(nums[i]>=target){
                return i;
            }
        }
        return nums.length;
    }
}
  • 二分法
class Solution {
    public int searchInsert(int[] nums, int target) {
        int left=0;
        int rigth = nums.length-1;
        int temp = nums.length;
        while(left<=rigth){
            int mid = (left+rigth)>>>1;
            if(nums[mid]>=target){
                temp = mid;//mid定义在循环体内,所以需要保存其值
                rigth = mid-1;
            }else{
                left = mid+1;
            }
        }
        return temp;
    }
}
---------------------------------------------------
class Solution {
    public int searchInsert(int[] nums, int target) {
        int left = 0;
        int right = nums.length-1;
        int mid = 0;
        while(left<=right){
        	mid = (left+right)>>>1;
            if(target>nums[mid]){
            	left=mid+1;
            }else if(target<nums[mid]){
            	right=mid-1;
            }else{
            	return mid;
            }
        }
        return right+1;
    }
}

3.在排序数组中查找元素的第一个和最后一个位置[34]

  • 暴力解法
class Solution {
    public int[] searchRange(int[] nums, int target) {
        if(nums.length<=0) return new int[]{-1,-1};
		
		int start = -1;//保存第一个target位置
		for(int i=0;i<nums.length;i++){
			if(nums[i]==target){
				start=i;
				break;
			}
		}
		
        if(start==-1) return new int[]{-1,-1};
        
        for(int i=start;i<=nums.length;i++){
        	if(i==nums.length || nums[i]!=target){
        		return new int[]{start,i-1};
        	}
        }
        return new int[]{-1,-1};
    }
}
  • 二分法
class Solution {
    public int[] searchRange(int[] nums, int target) {
        int left=searchLeft(nums, target);
        int right=searchRight(nums, target);
        return new int[]{left,right};
    }
    public int searchLeft(int[] nums, int target){
        int left=0;
        int right=nums.length-1;
        while(left<=right){
            int mid=left+(right-left)/2;//防止内存溢出
            if(nums[mid]==target){
                if(mid==0 || nums[mid-1]!=target){
                    return mid;
                }
                right = mid-1;
            }else if(nums[mid]>target){
                right = mid -1;
            }else{
                left = mid +1;
            }
        }
        return -1;
    }
    public int searchRight(int[] nums, int target){
        int left=0;
        int right=nums.length-1;
        while(left<=right){
            int mid=left+(right-left)/2;//防止内存溢出
            if(nums[mid]==target){
                if(mid==nums.length-1 || nums[mid+1]!=target){
                    return mid;
                }
                left = mid+1;
            }else if(nums[mid]>target){
                right = mid -1;
            }else{
                left = mid +1;
            }
        }
        return -1;
    }
}
---------------------------------------------------
class Solution {
    public int[] searchRange(int[] nums, int target) {
        int leftOrder=searchLeft(nums, target);
        int rightOrder=searchRight(nums, target);
        if(leftOrder==-2 || rightOrder==-2) return new int[]{-1,-1};
		if(rightOrder-leftOrder>1) return new int[]{leftOrder+1,rightOrder-1};
		return new int[]{-1,-1};
    }
    //[mid-1]<target<=[mid]
    public int searchLeft(int[] nums, int target){
        int left=0;
        int right=nums.length-1;
        int temp = -2;
        while(left<=right){
            int mid=left+(right-left)/2;//防止内存溢出
            if(nums[mid]>=target){
                right = mid -1;
                temp = right;
            }else{
                left = mid +1;
            }
        }
        return temp;
    }
    //[mid]<=target<[mid+1]
    public int searchRight(int[] nums, int target){
        int left=0;
        int right=nums.length-1;
        int temp = -2;
        while(left<=right){
            int mid=left+(right-left)/2;//防止内存溢出
            if(nums[mid]>target){
                right = mid-1;
            }else{
                left = mid +1;
                temp = left;
            }
        }
        return temp;
    }
}

4. x的平方根 [69]

  • 袖珍计算器算法
    在这里插入图片描述
利用指数函数与对数函数代替平方根函数
时间复杂度:O(1)
--------------------------------
class Solution {
    public int mySqrt(int x) {
        int result = (int) Math.exp(0.5*Math.log(x));
        //注意判断result+1的平方是否定于x
        return (result+1)*(result+1) == x? result+1:result;
    }
}
  • 二分法
问题转换:根ans满足mid*mid<=x,所以对k进行二分查找[0,x],每一步比较mid*mid与x的值
时间复杂度:O(logx)
-----------------------------------------
//mid*mid<=x<(mid+1)*(mid+1)
class Solution {
    public int mySqrt(int x) {
        int left = 0,right = x,result=-1;
        while(left<=right){
            int mid = left+(right-left)/2;
            //注意类型转换,防止精度丢失
            if((long)mid*mid<=x){
                result=mid;
                left=mid+1;
            }else{
                right=mid-1;
            }
        }
        return result;
    }
}
  • 牛顿迭代
    请添加图片描述
class Solution {
    public int mySqrt(int x) {
        if(x==0) return 0;
        double x0 = x;

        while(true){
            //求出通项公式
            double result = 0.5*(x0+x/x0);
            if(Math.abs(x0 - result) < 1e-7){
            	return (int)result;
            }
            x0=result;
        }
    }
}

5.有效的完全平方数[367]

class Solution {
    public boolean isPerfectSquare(int num) {
        //不要 使用任何内置的库函数 --> 袖珍计算器法排除
        int left = 0;
        int right = num;
        int result = -1;
        while(left<=right){
            int mid = left+(right-left)/2;
            if((long)mid*mid<=num){
                result = mid;
                left = mid +1;
            }else{
                right = mid -1;
            }
        }
        return result*result==num?true:false;
    }
}

移除元素

1.移除元素[27]

  • 无名
可能是二分法做太多的原因,做啥都像二分法
-------------------------------------------------
class Solution {
    public int removeElement(int[] nums, int val) {
        //1.排序
        Arrays.sort(nums);
        
        //2.找左右边界
        int leftOrder = searchLeftOrder(nums,val);
        int rightOrder = searchRightOrder(nums,val);
        
        //3.边界值判断
        if(leftOrder==-2 || rightOrder==-2) return nums.length;
        
        //4.拼接新数组
        int vals = rightOrder-leftOrder-1;
        if(rightOrder!=nums.length){
        	for(int i=rightOrder;i<nums.length;i++){
        		nums[++leftOrder]=nums[i];
        	}
        }
        return nums.length-vals;
    }

    //找左边界
    public static int searchLeftOrder(int[] nums,int val){
        int left = 0;
        int right = nums.length-1;
        int leftOrder = -2;
        while(left<=right){
            int mid = left+(right-left)/2;
            if(nums[mid]>=val){
                right = mid-1;
                leftOrder=right;
            }else{
                left = mid+1;
            }
        }
        return leftOrder;
    }
    //找右边界
    public static int searchRightOrder(int[] nums,int val){
        int left = 0;
        int right = nums.length-1;
        int rightOrder = -2;
        while(left<=right){
            int mid = left+(right-left)/2;
            if(nums[mid]>val){
                right = mid-1;
            }else{
                left = mid+1;
                rightOrder=left;
            }
        }
        return rightOrder;
    }
}
  • 快慢双指针
class Solution {
    public int removeElement(int[] nums, int val) {
        int left=0;
        for(int i=0;i<nums.length;i++){
            if(nums[i]!=val){
                nums[left]=nums[i];
                left++;
            }
        }
        return left;
    }
}
  • 首尾双指针
class Solution {
    public int removeElement(int[] nums, int val) {
        //指向首尾的双指针
        int left=0;
        int right = nums.length;
        while(left<right){
            if(nums[left]==val){
                nums[left] = nums[--right];
            }else{
                left++;
            }
        }
        return left;
    }
}

2.删除有序数组中的重复项[26]

  • 快慢双指针
class Solution {
    public int removeDuplicates(int[] nums) {
        //快慢指针-由于数组顺序不能改变,故不能使用首尾指针
        int left = 0;
        for(int i=1;i<nums.length;i++){
            if(nums[left]!=nums[i]){
                left++;
                nums[left]=nums[i];
            }
        }
        return left+1;
    }
}

3.移动零[283]

  • 快慢双指针
class Solution {
    public void moveZeroes(int[] nums) {
        //快慢指针,是0直接覆盖,到达length后将末尾值直接赋值为0
        int left=0;
        for(int i=0;i<nums.length;i++){
            if(nums[i]!=0){
                nums[left]=nums[i];
                left++;
            }
        }
        for(int i=left;i<nums.length;i++){
            nums[i]=0;
        }
    }
}

4.比较含退格的字符串[844]

  • 无名
class Solution {
    public boolean backspaceCompare(String s, String t) {
        if(s.equals(t)) return true;
        //返回退格完成过后的字符串
        String s1 = backspaceNew(s);
        String t1 = backspaceNew(t);
        return s1.equals(t1);
    }
    public static String backspaceNew(String str){
        int start=0;
        //借助StringBuffer,方便删除元素
        StringBuffer sb = new StringBuffer(str);
        while(start<sb.length()){
            int index = sb.indexOf("#",start);
            if(index==-1){
                break;
            }
            if(index==0){
            	sb.deleteCharAt(index);
            	start=index;
            	continue;
            }
            sb.delete(index-1,index+1);
            start=index-1;
        }
        return sb.toString();
    }
}
  • 双指针
class Solution {
    public boolean backspaceCompare(String s, String t) {
        return backspaceStr(s).equals(backspaceStr(t));
    }

    public static String backspaceStr(String str){
        int i=0,j=0;
        char[] ch = str.toCharArray();
        while(i<ch.length){
            if(ch[i]!='#'){
                ch[j]=ch[i];
                j++;
            }else if(j!=0){
                j--;
            }
            i++;
        }
        String res = "";
        for(i=0;i<j;i++){
            res+=ch[i];
        }
        return res;
    }
}

5.有序数组的平方[977]

  • Arrays.sort
class Solution {
    public int[] sortedSquares(int[] nums) {
        for(int i=0;i<nums.length;i++){
            nums[i] = nums[i]*nums[i];
        }
        Arrays.sort(nums);
        return nums;
    }
}
  • 双指针
class Solution {
    public int[] sortedSquares(int[] nums) {
        int left = 0;
        int rigth = nums.length-1;
        int[] numsNew = new int[nums.length];
        int index = nums.length-1;
        while(left<=rigth){
        	if(Math.abs(nums[left])<=Math.abs(nums[rigth])){
        		numsNew[index]=nums[rigth]*nums[rigth];
        		rigth--;
        	}else{
        		numsNew[index]=nums[left]*nums[left];
        		left++;
        	}
        	index--;
        }
        return numsNew;
    }
}

长度最小的子数组

1.长度最小的子数组[209]

  • 暴力解法
class Solution {
    public int minSubArrayLen(int target, int[] nums) {
        //长度最小的 连续子数组-->注意数组元素要连续
        int min=nums.length*2;
        int sum=0;
        for(int i=0;i<nums.length;i++){
            for(int j=i;j<nums.length;j++){
            	sum+=nums[j];
                if(sum>=target){
                    min=Math.min(min,j-i+1);
                    break;
                }
            }
            sum=0;
        }
        return min==nums.length*2?0:min;
    }
}
  • 滑动窗口(双指针)
class Solution {
    public int minSubArrayLen(int target, int[] nums) {
        //长度最小的 连续子数组-->注意数组元素要连续
        int start=0,end=0;
        int min = nums.length*2;
        int sum = 0;
        while(end<nums.length){
        	sum += nums[end];
            while(sum>=target){
                min=Math.min(end-start+1,min);
                sum -= nums[start++];
            }
            end++;
        }
        return min==nums.length*2?0:min;
    }
}

2.水果成篮[904]

class Solution {
    public int totalFruit(int[] fruits) {
        //fruits[i]:第i棵果树上果子的种类,类型相同值相同
        int type1 = -1;//篮子1中存放的水果种类
        int type2 = -1;//篮子2中存放的水果种类
        int left=0,rigth=0;
        int length = fruits.length;
        int max = -1;
        int index=-1;//标记类型二第一颗数的位置
        while(rigth<=length){
            if(rigth==length){max = Math.max(max,rigth-left); break;}
            if(type1==-1){type1=fruits[rigth];}
            if(type2==-1 && fruits[rigth]!=type1){
                type2=fruits[rigth];
                index=rigth;
            }
            if(fruits[rigth]!=type1 && fruits[rigth]!=type2){
                max = Math.max(max,rigth-left);
                left=index;
                rigth=index;
                type1=-1;
                type2=-1;
                break;
            }
            rigth++;
        }
        return max;
    }
}
  • 滑动窗口
class Solution {
    public int totalFruit(int[] fruits) {
        //fruits[i]:第i棵果树上果子的种类,类型相同值相同
        if (fruits==null || fruits.length==0) return 0;
        int length = fruits.length;
        int max = -1,left = 0;
        Map<Integer, Integer> map = new HashMap<>();//存放不同种类水果<水果类型,出现次数>

        for(int i=0;i<length;i++){//遍历所有水果
            if(map.get(fruits[i])==null){
                map.put(fruits[i],1);
            }else{
                map.put(fruits[i],map.get(fruits[i])+1);
            }
            //map.put(fruits[i],map.getOrDefault(fruits[i],0)+1); 
            while(map.size()>2){
                //移除key为左指针的值,left移动到map中只剩下两个种类为止
                map.put(fruits[left], map.get(fruits[left])-1);
                if(map.get(fruits[left])==0) map.remove(fruits[left]); 
                left++;
            }
            max = Math.max(max,i-left+1);//每次处理都计算依次max
        }
        return max;
    }
}

3.最小覆盖子串[76]

class Solution {
    public String minWindow(String s, String t) {
        //<字符,出现次数>
        HashMap<Character,Integer> maps = new HashMap<Character,Integer>();
        HashMap<Character,Integer> mapt = new HashMap<Character,Integer>();
        //统计t中所有字符以及响应次数
        for(int i=0;i<t.length();i++){
            mapt.put(t.charAt(i),mapt.getOrDefault(t.charAt(i),0)+1);
        }
        String res = "";//保存结果
        int count = 0;//保存滑动窗口中满足t字符个数
        int minLen = Integer.MAX_VALUE;//满足条件的子串自小长度

        for(int i=0,j=0;i<s.length();i++){//i遍历s中所有元素
            maps.put(s.charAt(i),maps.getOrDefault(s.charAt(i),0)+1);
            //如果t中包含该字符 && s中该字符的数量<=t中该字符的数量(该字符必须)
            if(mapt.containsKey(s.charAt(i)) && maps.get(s.charAt(i))<=mapt.get(s.charAt(i))){
                count++;
            }
            //当t中不包含窗口左边界字符 或 窗口左边界字符数量大于t中该字符数量 -->收缩窗口
            while(j<i && (!mapt.containsKey(s.charAt(j)) || maps.get(s.charAt(j))>mapt.get(s.charAt(j)))){
                int num = maps.get(s.charAt(j))-1;
                maps.put(s.charAt(j),num);
                j++;
            }
            //找到含t的子串,且长度最小则保存结果
            if(count==t.length() && i-j+1<minLen){
                minLen=i-j+1;
                res=s.substring(j,i+1);
            }
        }
        return res;
    }
}

螺旋矩阵

1.螺旋矩阵 II[59]

  • 模拟旋转过程
    请添加图片描述
class Solution {
    public int[][] generateMatrix(int n) {
        //模拟法:模拟旋转过程
        int [][]matrix = new int[n][n];//保存旋转结果
        int l=0,r=n-1,t=0,b=n-1;//左右上下
        int num = 1;//初值
        while(num<=n*n){
            for(int i=l;i<=r;i++) matrix[t][i]=num++;
            t++;
            for(int i=t;i<=b;i++) matrix[i][r]=num++;
            r--;
            for(int i=r;i>=l;i--) matrix[b][i]=num++;
            b--;
            for(int i=b;i>=t;i--) matrix[i][l]=num++;
            l++;
        }
        return matrix;
    }
}

2.螺旋矩阵[54]

  • 模拟旋转过程
上一题的逆过程,但注意数组边界与循环退出时机
-------------------------------------------
class Solution {
    public List<Integer> spiralOrder(int[][] matrix) {
        List<Integer> list = new ArrayList<Integer>();
        int n = matrix.length;//行数
        int m = matrix[0].length;//列数
        int length = n*m;//元素总数
        int l=0,r=m,t=0,b=n;
        //注意数组越界问题与循环结束时机
        while(list.size()<length){
            for(int i=l;i<r;i++) list.add(matrix[t][i]);
            t++;
            if(t==b) break;
            for(int i=t;i<b;i++) list.add(matrix[i][r-1]);
            r--;
            if(r==l) break;
            for(int i=r-1;i>=l;i--) list.add(matrix[b-1][i]);
            b--;
            for(int i=b-1;i>=t;i--) list.add(matrix[i][l]);
            l++;
        }
        return list;
    }
}

3.顺时针打印矩阵[剑指29]

  • 模拟旋转过程
注意边界!!!!!
-------------------------------------------
class Solution {
    public int[] spiralOrder(int[][] matrix) {
        int n = matrix.length;//行数
        if(n==0){return new int[0];}
        int m = matrix[0].length;//列数
        int length = n*m;//元素总数
        int num=0;//记录元素总数
		int[] array = new int[length];
        int l=0,r=m,t=0,b=n;
        //注意数组越界问题与循环结束时机
        while(num<length){
            for(int i=l;i<r;i++) array[num++]=matrix[t][i];
            t++;
            if(t==b) break;
            for(int i=t;i<b;i++) array[num++]=matrix[i][r-1];
            r--;
            if(r==l) break;
            for(int i=r-1;i>=l;i--) array[num++]=matrix[b-1][i];
            b--;
            if(t==b) break;
            for(int i=b-1;i>=t;i--) array[num++]=matrix[i][l];
            l++;
            if(r==l) break;
        }
        return array;
    }
}

总结

1.二分法

  • 有序不重复

2.双指针法

  • 通过快指针和慢指针在一个for循环下完成两个for循环的工作
  • 可以改变原数组顺序使用首尾双指针

3.滑动窗口

  • 相当于首尾双指针,但又不完全是
  • 两层while循环,外层改变尾指针(窗口中的元素不满足要求),内层改变首指针(窗口中的元素满足要求,直到不满足要求时跳出到外层循环)

4.模拟行为

  • 模拟事件发生的过程
  • 不涉及算法,考察对代码的掌控能力
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值