数组——二分查找,双指针,滑动窗口,螺旋排列

目录

二分查找

1.返回目标值下标(704)

2.确定目标值在数组中的开始位置和结束位置。即序列的左右边界(34)

3.计算x的算术平方根,只保留整数部分(69)

4.判断 num 是否为一个完全平方数

双指针法

1.移除特定元素(27)--数值/链表

2.比较含退格的字符串(844)

3.非递减顺序 排序的整数数组(含负数),平方后排序  (997)

双指针法->滑动窗口法

1.长度最小的子数组(209)

2.至多包含两个不同字符的最长子串(904)

螺旋排列——螺旋矩阵 II


二分查找

1.返回目标值下标(704)

注意点:二分查找的边界设置,  闭则内移,开则直跳

                 while(low < high) = 左闭右开区间,则high = mid ;

                 while(low <= high) = 闭区间,则high = mid -1;

              防止溢出,超出数值范围,mid = low + (high-low)/2;     乘变除,加变加减

方式一:暴力解法:O(n)

方式二:二分查找:O(log(n))

class Solution {
    public int search(int[] nums, int target) {
        if(target < nums[0] || target > nums[nums.length-1]){
            return -1;  //有序数组,避免多余遍历操作
        }
        // //方式一:暴力算法
        //  for(int i = 0; i < nums.length; i++){
        //      if(nums[i] == target){
        //          return i;
        //      }
        //  }
        //  return -1;

        方式二:二叉树查找
        int low = 0;
        int high = nums.length-1;
        int mid; 
        while(low <= high){ //易错点:区间边界
            mid = low + (high-low)/2; //防止溢出
             if(nums[mid] == target){
                return mid;
            }else if(nums[mid] < target){
                low = mid + 1;
            }else if(nums[mid] > target){
                high = mid -1;
            }
        }
        return -1;
    }
}

2.确定目标值在数组中的开始位置和结束位置。即序列的左右边界(34)

注意点:java中矩阵的声明方式:new int[]{数值1 , 数值2,……}

方式一:暴力解法:O(n)  左右双向遍历+判别(1.是否为目标值,2.该边界是否已确定)

方式二:二分查找:O(2log(n)) 两次查找 分别确定左右边界,+判断边界内是否为空,确定返回值

         nums[mid] >= target ——遍历后,left = low; 确定左边界

         nums[mid] > target  —— 遍历后,right = high;确定右边界

class Solution {
    public int[] searchRange(int[] nums, int target) {
        //方式一:暴力解法
    //     int high = -1;
    //     int low = -1;
    //     for(int i = 0; i < nums.length; i++){
    //         if(nums[i] == target && low == -1){
    //             low = i;
    //         }
    //         if(nums[nums.length-1-i] == target && high == -1){
    //             high = nums.length-1-i;
    //         }
    //     }
    //    return new int[]{low , high};

        //方式二:二叉树
        int low;
        int high;
        int mid;
        int left=-2,right=-2;
        low = 0;
        high = nums.length-1;
        while(low <= high){
            mid = low + (high-low)/2;
            // >= 查找到左边界
            if(nums[mid] >= target){
                high = mid - 1;   
            }else{
                low = mid +1;
            }   
        }
        left = low;

        low = 0;
        high = nums.length-1;
        while(low <= high){
            mid = low + (high-low)/2;
            // > 最后查找到右边界
            if(nums[mid] > target){
                high = mid - 1;      
            }else{
                low = mid +1; 
            }   
        } 
        right = high;

        if((right-left) >= 0){
            return new int[]{left,right};
        }else{
            return new int[]{-1,-1};
        }
        
    }
}

3.计算x算术平方根,只保留整数部分(69)

注意点:运算范围(0,x/2),注意考虑特殊边界值情况 减少开销,避免出错

方法一:二分查找:下界=high ,二分查找结束是high<low

方法二:公式转换

方法三:牛顿法(迭代逼近)

class Solution {
    public int mySqrt(int x) {
        //特殊情况判断
        if(x==0 || x==1){
            return x;
        }
        int low = 2;
        int high = x/2;
        int mid;
        
        while(low <= high){
            mid = low+(high-low)/2;
            if(mid > x/mid){
                high = mid-1;
            }else if(mid < x/mid){
                low = mid + 1;
            }else{
                return mid;
            }
        }
        return high;

        //方式二:sqrt(x) = exp(1/2*(ln x))
        // if (x == 0) {
        //     return 0;
        // }
        // int ans = (int) Math.exp(0.5 * Math.log(x));
        // return (long) (ans + 1) * (ans + 1) <= x ? ans + 1 : ans;
    }
}

4.判断 num 是否为一个完全平方数

注意点:1.减少计算开销         2.数值范围,数据类型 long square = (long)mid*mid;

class Solution {
    public boolean isPerfectSquare(int num) {
        if(num == 1){
            return true;
        }
        int low = 0;
        int high =  num/2;
        int mid;
        long square;
        while(low <= high){   
            mid = low + (high-low)/2;
            square = (long)mid*mid;
            if(square > num){
                high = mid - 1;
            }else if(square < num){
                low = mid + 1;
            }else{
                return true;
            }
        }
        return false;
    }
}

双指针法

1.移除特定元素(27)--数值/链表

方式一:快慢双指针,快指针:寻找需要的数据位置,慢指针:确定数据放置的位置

方式二:相对双指针,左指针:从左侧寻找要被替换的位置,右指针:从右侧寻找用于替换的数据

注意点:left <= right:需要= 因为left = right 处需要判断数值,

               while(1 && 2)一直判断,且移动指针,直到找到需要的位置

                数据替换后,要移动指针

class Solution {
    public int removeElement(int[] nums, int val) {
        //方式一:快慢双指针
        // int slowIndex = 0;
        // for(int fastIndex = 0; fastIndex < nums.length; fastIndex++){
        //     if(nums[fastIndex] != val){                
        //         nums[slowIndex] = nums[fastIndex];
        //         slowIndex++;
        //     }
        // }
        // return slowIndex;

        //方式二:相对双指针
        int left = 0;
        int right = nums.length-1;
        while(left <= right){  //需要= 因为left = right 处需要判断数值
            while(left <= right && nums[left] != val){
                left++;
            }
            while(left <= right && nums[right] == val){
                right--;
            }
            if(left < right){
                nums[left] = nums[right];
                left++;
                right--;
            }
        }
        
        return left;
    }
}

2.比较含退格的字符串(844)

方法一:双指针:两个字符串各用一个指针,通过比较+移动,确定待比较元素的位置

方法二:重构法:写一个方法,将两个字符串表示原有结构,在比较字符串s.equals(t)

注意点: Java中string字符串为final的,其内容是不可更改的,不可继承

   1.字面量定义方式:String s = "abs"; 存放在方法区的字符串常量池中,相同内容的字符串为同一地址,对字符串修改,需重新制定内存区域(存储地址),不能在原有内存上改变

   2.构造器创建对象:String s = new String(" "); 一个是堆空间中new结构,另一个是char[]对应的常量池中的数据:" "

获取字符用.charAt(index),排序.sort(),修改字符.replace(oldChar:' ' ,newChar:' ')

若要进行字符串拼接应用StringBuffer数据类型,通过.append("添加字符")

class Solution {
    public boolean backspaceCompare(String s, String t) {
        //java 中String是 immutable的,也就是不可变,一旦初始化,其引用指向的内容是不可变的(注意:是内容不可变)。
        //方式一:双指针法
        // int sl = s.length()-1;
        // int tl = t.length()-1;
        // int skip = 0;
        // while( sl>=0 || tl>=0){
        //     //从后往前确定需比较的数据位置
        //     skip = 0;
        //     while( sl>=0){
        //         if(s.charAt(sl) == '#'){
        //             skip++;
        //             sl--;
        //         }else if(skip>0){
        //             skip--;
        //             sl--;
        //         }else{
        //             break;
        //         }
        //     }

        //     skip = 0;
        //     while( tl>=0){
        //         if(t.charAt(tl) == '#'){
        //             skip++;
        //             tl--;
        //         }else if(skip>0){
        //             skip--;
        //             tl--;
        //         }else{
        //             break;
        //         }
        //     }

        //     if(sl>=0 && tl>=0){
        //         if(s.charAt(sl) != t.charAt(tl)){
        //             return false;
        //         }
        //     }else if(sl>=0 || tl>= 0){
        //         return false;
        //     }
        //     sl--;
        //     tl--;
        // }
        // return true;

    
       return build(s).equals(build(t));
    }
    //在循环执行字符串拼接操作时,StringBuffer是通常的选择。
    public String build(String str) {
        StringBuffer ret = new StringBuffer();
        int length = str.length();
        for (int i = 0; i < length; ++i) {
            char ch = str.charAt(i);
            if (ch != '#') {
                ret.append(ch);
            } else {
                if (ret.length() > 0) {
                    ret.deleteCharAt(ret.length() - 1);
                }
            }
        }
        return ret.toString();


    }
}

3.非递减顺序 排序的整数数组(含负数),平方后排序  (997

方法一:暴力算法  平方后,排序,Java有封装好的排序函数sort()  O(n + nlog n)

方法二:双指针法  利用非递减顺序特性 两侧数据平方后最大,比较大小按顺序放入另一数组中O(n)

class Solution {
    public int[] sortedSquares(int[] nums) {
        // 方法一:平方后,排序,Java有封装好的排序函数sort()
        // int[] ans = new int[nums.length];
        // for (int i = 0; i < nums.length; ++i) {
        //     ans[i] = nums[i] * nums[i];
        // }
        // Arrays.sort(ans);
        // return ans;

        //方法二:双指针法 利用非递减顺序特性 两侧数据平方后最大,比较大小按顺序放入另一数组中
        int left = 0;
        int right = nums.length-1;
        int pos = nums.length-1;
        int[] ans = new int[right+1];
        while(left<=right){
            if(nums[left]*nums[left] > nums[right]*nums[right] ){
                ans[pos] = nums[left]*nums[left];
                left++;
            }else{
                ans[pos] = nums[right]*nums[right] ;
                right--;
            }
            pos--;
        }
        return ans;
    }
}

双指针法->滑动窗口法

方法:双指针法=滑动窗口法,不断调整求和序列的起始和终止位置,找到满足条件的序列范围

最小滑窗模板:给定数组 nums,定义滑窗的左右边界 i, j,求满足某个条件的滑窗的最小长度。while j < len(nums):
    判断[i, j]是否满足条件
    while 满足条件:
        不断更新结果(注意在while内更新!)
        i += 1 (最大程度的压缩i,使得滑窗尽可能的小)
    j += 1

最大滑窗模板:给定数组 nums,定义滑窗的左右边界 i, j,求满足某个条件的滑窗的最大长度。while j < len(nums):
    判断[i, j]是否满足条件
    while 不满足条件:
        i += 1 (最保守的压缩i,一旦满足条件了就退出压缩i的过程,使得滑窗尽可能的大)
    不断更新结果(注意在while外更新!)
    j += 1

1.长度最小的子数组(209)

找出该数组中满足其和 ≥ target 的长度最小的 连续子数组 [numsl, numsl+1, ..., numsr-1, numsr] ,并返回其长度

class Solution {
    public int minSubArrayLen(int target, int[] nums) {
        //双指针法、滑动窗口法
        int left = 0;
        int right = 0;
        int sum = 0;
        int sublen = Integer.MAX_VALUE;
        for(; right < nums.length; right++){
            sum += nums[right];
            while(sum >= target){
                sublen = (sublen < right-left+1? sublen:right-left+1);
                sum -= nums[left++];
            }
        }
        return (sublen == Integer.MAX_VALUE? 0:sublen);
    }
}

2.至多包含两个不同字符的最长子串(904)

class Solution {
    public int totalFruit(int[] tree) {
        int left = 0;
        int right = 0;
        int kinds = 0;
        int n = tree.length;
        int totalFruit = 0;
        int[] total =  new int[n];
        for(; right<n; right++){
            total[tree[right]]++;
            if(total[tree[right]] == 1){
                kinds++;
            }
            while(kinds > 2){
                total[tree[left]]--;
                if(total[tree[left]] == 0){
                    kinds--;
                }
                left++;
            }
            totalFruit = Math.max(totalFruit,right-left+1);
        }
        return totalFruit;
    }
}


螺旋排列——螺旋矩阵 II

坚持循环不变量原则:保证区间范围不重叠、不遗漏,坚持左闭右开或左开右闭。

确定循环次数,奇数、偶数

class Solution {
    public int[][] generateMatrix(int n) {
        int[][]  res = new int[n][n];
        int loop = 0;
        int count = 1;
        int i,j;
        while(loop<n/2){
            //左闭右开
            //左到右
            for(j = loop; j<n-loop-1; j++){
                res[loop][j] = count++;
            }
           //上到下
            for(i = loop; i<n-loop-1; i++){
                res[i][j] = count++;
            }
           //右到左
            for(; j>loop; j--){
                res[i][j] = count++;
            }
           //下到上
            for(; i>loop; i--){
                res[i][j] = count++;
            }
            loop++;
        }
       if( n%2==1 ){
           res[loop][loop]=count;
       } 
       return res;
    }
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值