剑指offer系列

LCR 001.两数相除

给定两个整数 a 和 b ,求它们的除法的商 a/b ,要求不得使用乘号 '*'、除号 '/' 以及求余符号 '%' 。

注意:

  • 整数除法的结果应当截去(truncate)其小数部分,例如:truncate(8.345) = 8 以及 truncate(-2.7335) = -2
  • 假设我们的环境只能存储 32 位有符号整数,其数值范围是 [−231, 231−1]。本题中,如果除法结果溢出,则返回 231 − 1
class Solution {
    public int divide(int a, int b) {
        // 处理极端边界情况
        if (a == Integer.MIN_VALUE) {
            if (b == 1) return Integer.MIN_VALUE;
            if (b == -1) return Integer.MAX_VALUE; // 溢出返回最大值
        }
        if (b == Integer.MIN_VALUE) {
            return a == Integer.MIN_VALUE ? 1 : 0;
        }
        if (a == 0) return 0;

        // 转换为负数处理,避免溢出
        int negativeA = a > 0 ? -a : a;
        int negativeB = b > 0 ? -b : b;

        int result = 0;
        
        // 当被除数绝对值 >= 除数绝对值时继续(负数用 <= 判断)
        while (negativeA <= negativeB) {
            int currentDivisor = negativeB;
            int currentMultiple = 1;
            
            // 倍增:找到不超过当前被除数的最大除数倍数
            // 防止溢出:currentDivisor >= 0xc0000000(即 Integer.MIN_VALUE / 2)
            while (currentDivisor >= (Integer.MIN_VALUE >> 1) && negativeA <= (currentDivisor << 1)) {
                currentDivisor <<= 1;
                currentMultiple <<= 1;
            }
            result += currentMultiple;
            negativeA -= currentDivisor;
        }

        // 处理符号
        return (a > 0) == (b > 0) ? result : -result;
    }
}

LCR 002.二进制求和

给定两个 01 字符串 a 和 b ,请计算它们的和,并以二进制字符串的形式输出。

输入为 非空 字符串且只包含数字 1 和 0

class Solution {
    public String addBinary(String a, String b) {
        //定义可变字符串变量来方便存储输出结果
        StringBuilder res =new StringBuilder();
        //获取两个字符串的长度作为索引,0->1->2->3,字符串中索引最大代表最低位
        int l1 = a.length() -1;
        int l2 = b.length() -1;
        //定义变量来存储进位
        int carry =0;
        //只要字符串中有一个长度不为0就继续循环
        while(l1 >=0 || l2 >=0){
            //长度为0的字符串进行补0,否则根据索引获取各位相对应位上的字符并转换为int型
            int x = l1 <0 ? 0 :a.charAt(l1) - '0';//char类型转为int类型的方法:减去字符0,即-'0'
            int y = l2 <0 ? 0 :b.charAt(l2) - '0';
            //求和,顺便加上进位
            int sum = x + y + carry;
            //将求和结果取余,取余结果插入到结果字符串中
            res.append(sum%2);
            //计算是否有进位
            carry = sum /2;
            //索引递减,指针左移,获取更高位
            l1 --;
            l2 --;
        }
        //若最高位有进位,则向结果字符串插入进位,即 1
        if(carry!=0)
            res.append(carry);
        //由于在插入结果是在字符串后面插入,故这里需要倒序返回
        return res.reverse().toString();
    }
}

LCR 003.比特位计数

给定一个非负整数 n ,请计算 0 到 n 之间的每个数字的二进制表示中 1 的个数,并输出一个数组。

class Solution {
    public int[] countBits(int n) {
        //定义结果数组,数组大小注意+1,否则会超出界限
        int[] res = new int[n+1];
        //计数
        int count =0;
        //定义字符串方便后面整数转二进制字符串
        String str = "";
        //0 - n
        for(int i=0;i<=n;i++){
            //将0 - n 的整数转换为二进制字符串
            str = Integer.toBinaryString(i);
            //每个整数统计时需要先将计数变量置0
            count =0;
            //开始循环计数
            for(int j=0;j<str.length();j++){
                //利用charAt()循环统计 1 个数
                if(str.charAt(j)== '1'){
                    //若找到一个 1 ,则加一次
                    count++;
                }
                else
                    continue;
            }
            //将统计的数插入到数组中
            res[i]=count;
        }
        //返回
        return res;
    }
}

LCR 004.只出现一次的数字 II

给你一个整数数组 nums ,除某个元素仅出现 一次 外,其余每个元素都恰出现 三次 。请你找出并返回那个只出现了一次的元素。

class Solution {
    public int singleNumber(int[] nums) {
        //用count来统计数字出现的个数
        int count=0;
        //存储那个只出现一次的数字
        int num=0;
        //直接双重暴力循环
        for(int i=0;i<nums.length;i++){
            //每次循环前计数器都要置0
            count=0;
            //开始循环
            for(int j=0;j<nums.length;j++){
                //把自己和数组中的所有数字(包括自己)做比较
                if(nums[i]==nums[j]){
                    //出现相等情况则计数器+1
                    count++;
                    //由于要找出只出现一次的数字,故一旦count > 1就不要继续遍历了,没有意义,节省时间
                    if(count>1){
                        //直接退出第二层循环
                        break;
                    }
                }
            }
            //由于只有一个数字出现一次,所以一旦找到就退出整个循环,节省时间
            if(count==1){
                //将这个数字赋值给前面定义好的变量
                num=nums[i];
                break;
            }
        }
        //返回
        return num;
    }
}

LCR 005.最大单词长度乘积

给定一个字符串数组 words,请计算当两个字符串 words[i] 和 words[j] 不包含相同字符时,它们长度的乘积的最大值。假设字符串中只包含英语的小写字母。如果没有不包含相同字符的一对字符串,返回 0。

class Solution {
    public boolean hasCommonElement(String str1, String str2) {
        //利用Set集合中元素不能重复的特性,编写方法去对字符串进行判同
        Set<Character> set = new HashSet<>();
        //将第一个字符串插入到set集合中
        for (int i = 0; i < str1.length(); i++) {
            set.add(str1.charAt(i));
        }
        //使用contain()方法进行循环判断
        for (int i = 0; i < str2.length(); i++) {
            if (set.contains(str2.charAt(i))) {
                //有相同字符则方法返回true
                return true;
            }
        }
        //默认返回false
        return false;
    }

    public int maxProduct(String[] words) {
        //获取字符串数组的长度
        int n = words.length;
        // 计算最大可能的单词对数量,即每个字符串两两之间都不包含相同的字符,C 的n取2
        int maxPossiblePairs = n * (n - 1) / 2; 
        // 按需初始化数组
        int[] arr = new int[maxPossiblePairs];  
        int a = 0;
        //开始循环
        for (int i = 0; i < n - 1; i++) {
            for (int j = i + 1; j < n; j++) {
                if (!hasCommonElement(words[i], words[j])) {
                    //两个字符串不包含相同的字符,计算两者长度的乘积
                    arr[a] = words[i].length() * words[j].length();
                    //结构数组索引++
                    a++;
                }
            }
        }
        //如果所有字符串两两之间都包含相同字符则返回0,否则返回数组的最大值
        return a > 0 ? Arrays.stream(arr, 0, a).max().getAsInt() : 0;
    }
}

LCR 006.两数之和 II - 输入有序数组

给定一个已按照 升序排列  的整数数组 numbers ,请你从数组中找出两个数满足相加之和等于目标数 target 。

函数应该以长度为 2 的整数数组的形式返回这两个数的下标值numbers 的下标 从 0 开始计数 ,所以答案数组应当满足 0 <= answer[0] < answer[1] < numbers.length 。

假设数组中存在且只存在一对符合条件的数字,同时一个数字不能使用两次。

class Solution {
    public int[] twoSum(int[] numbers, int target) {
        //利用二分查找
        for(int i=0;i<numbers.length;i++){
            //定义左右端点
            int low=i+1,high=numbers.length -1;
            while(low<=high){
                //取中间值
                int mid=(high+low)/2 ;
                //如果中间值刚好等于目标值与初始值之差
                if(numbers[mid]==target-numbers[i]){
                    //则新建数组并插入当前循环轮次及中间值下标
                    return new int[]{i,mid};
                    //大于,由于数组有序,则意味着符合相等条件的值会出现在当前中间值的左边
                }else if(numbers[mid]>target-numbers[i]){
                    //则右指针调整位置
                    high=mid-1;
                }else{
                    //小于,则坐指针调整位置
                    low=mid+1;
                }
                
            }
        }
        return new int[] {-1,-1};
    }
}

LCR 007.三数之和

给定一个包含 n 个整数的数组 nums,判断 nums 中是否存在三个元素 a ,b ,c 使得 a + b + c = 0 ?请找出所有和为 0 且 不重复 的三元组。

class Solution {
    public List<List<Integer>> threeSum(int[] nums) {
        //题目要求返回值为 List<List<Integer>>,故新建结果数组为该返回值类型
        List<List<Integer>> ans = new ArrayList<List<Integer>>();
        int n= nums.length;
        //先对数组进行排序,方便后续遍历时减少时间复杂度
        Arrays.sort(nums);
        //第一重循环
        for(int first=0;first<n;++first){
            //需要和上一次枚举的数不同,避免重复比如(2,2,3,4)==》(2,3,4),(2,3,4)
            if(first>0 && nums[first]==nums[first-1]){
                continue;
            }
            int third=n-1;
            int target= -nums[first];
            //第二重循环
            for(int second=first+1;second<n;++second){
                //同上
                if(second>first+1 && nums[second]==nums[second-1]){
                    continue;
                }
                //同上
                while(second<third && nums[second]+nums[third]>target){
                    --third;
                }
                if(second==third){
                    break;
                }
                if(nums[second]+nums[third]==target){
                    List<Integer> list = new ArrayList<Integer>();
                    //插入
                    list.add(nums[first]);
                    list.add(nums[second]);
                    list.add(nums[third]);
                    ans.add(list);
                }
                
            }
        }
        return ans;
    }
}

LCR 008.长度最小的子数组

给定一个含有 n 个正整数的数组和一个正整数 target 。

找出该数组中满足其和 ≥ target 的长度最小的 连续子数组 [numsl, numsl+1, ..., numsr-1, numsr] ,并返回其长度如果不存在符合条件的子数组,返回 0 。

class Solution {
    public int minSubArrayLen(int target, int[] nums) {
        int n = nums.length;
        if(n==0){
            return 0;
        }
        int start=0,end=0,sum=0;
        int ans=Integer.MAX_VALUE;
        while(end < n){
            sum += nums[end];
            while(sum>=target){
                ans = Math.min(ans, end - start +1);
                sum -= nums[start];
                start++;
            }
            end++;
        }
        return ans == Integer.MAX_VALUE ? 0 : ans;
    }
}

LCR 009.乘积小于 K 的子数组

给定一个正整数数组 nums和整数 k ,请找出该数组内乘积小于 k 的连续的子数组的个数。

class Solution {
    public int numSubarrayProductLessThanK(int[] nums, int k) {
        int n = nums.length;
        int prod=1;
        int start=0,ans=0;
        for(int end=0;end<n;end++){
            prod *= nums[end];
            while(start<=end && prod >=k){
                prod = prod/nums[start];
                start++;
            }
            ans += end - start +1;
        }
        return ans;
    }
}

LCR 010.和为 K 的子数组

给定一个整数数组和一个整数 k ,请找到该数组中和为 k 的连续子数组的个数。

class Solution {
    public int subarraySum(int[] nums, int k) {
        int count=0,sum=0;
        for(int start=0;start<nums.length;++start){
            sum=0;
            for(int end=start;end>=0;--end){
                sum += nums[end];
                if(sum==k)
                    count++;
            }
        }
        return count;
    }
}

LCR 011.连续数组

给定一个二进制数组 nums , 找到含有相同数量的 0 和 1 的最长连续子数组,并返回该子数组的长度。

class Solution {
    public int findMaxLength(int[] nums) {
        Map<Integer, Integer> map = new HashMap<Integer, Integer>();
        int counter=0,num=0,maxLen=0;
        map.put(counter,-1);
        for(int i=0;i<nums.length;i++){
            num = nums[i];
            if (num == 1) {
                counter++;
            } else {
                counter--;
            }             
            if (map.containsKey(counter)){
                int preindex = map.get(counter);
                maxLen = Math.max(maxLen,i-preindex);
            }else{
                map.put(counter,i);
            }
        }
        return maxLen;
    }
}

LCR 012.寻找数组的中心下标

给你一个整数数组 nums ,请计算数组的 中心下标 

数组 中心下标 是数组的一个下标,其左侧所有元素相加的和等于右侧所有元素相加的和。

如果中心下标位于数组最左端,那么左侧数之和视为 0 ,因为在下标的左侧不存在元素。这一点对于中心下标位于数组最右端同样适用。

如果数组有多个中心下标,应该返回 最靠近左边 的那一个。如果数组不存在中心下标,返回 -1 。

class Solution {
    public int pivotIndex(int[] nums) {
        int total = Arrays.stream(nums).sum();
        int sum =0;
        for(int i=0;i<nums.length;i++){
            if(2 * sum + nums[i]==total){
                return i;
            }
            sum += nums[i];
        }
        return -1;
    }
}

LCR 013.二位区域和检索 - 矩阵不可变

给定一个二维矩阵 matrix以下类型的多个请求:

  • 计算其子矩形范围内元素的总和,该子矩阵的左上角为 (row1, col1) ,右下角为 (row2, col2) 。

实现 NumMatrix 类:

  • NumMatrix(int[][] matrix) 给定整数矩阵 matrix 进行初始化
  • int sumRegion(int row1, int col1, int row2, int col2) 返回左上角 (row1, col1) 、右下角 (row2, col2) 的子矩阵的元素总和。
class NumMatrix {

    int[][] newmatrix;

    public NumMatrix(int[][] matrix) {
        int n1=matrix.length;
        if(n1>0){
            int n2=matrix[0].length;
            newmatrix = new int[n1][n2+1];
            for(int i=0;i<n1;i++){
                for(int j=0;j<n2;j++){
                    newmatrix[i][j+1]=newmatrix[i][j]+ matrix[i][j];
                }
            }
        }
        
    }
    
    public int sumRegion(int row1, int col1, int row2, int col2) {
        int sum =0;
        for(int i=row1;i<=row2;i++){
            sum += newmatrix[i][col2+1] - newmatrix[i][col1];
        }
        return sum;
    }
}

LCR 014.字符串的排列

给定两个字符串 s1 和 s2,写一个函数来判断 s2 是否包含 s1 的某个变位词。

换句话说,第一个字符串的排列之一是第二个字符串的 子串 。

class Solution {
    public boolean checkInclusion(String s1, String s2) {
        int n=s1.length(),m=s2.length();
        if(n>m)
            return false;
        int[] cnt = new int[26];
        for(int i=0;i<n;i++){
            cnt[s1.charAt(i) - 'a']--;
        }
        int left=0;
        for(int rigth=0;rigth<m;rigth++){
            int x=s2.charAt(rigth) - 'a';
            cnt[x]++;
            while(cnt[x]>0){
                cnt[s2.charAt(left) - 'a']--;
                left++;
            }
            if(rigth - left +1==n)
                return true;
        }
        return false;
    }
}

LCR 015.找到字符串中所有字母异位词

给定两个字符串 s 和 p,找到 s 中所有 p 的 变位词 的子串,返回这些子串的起始索引。不考虑答案输出的顺序。

变位词 指字母相同,但排列不同的字符串。

class Solution {
    public List<Integer> findAnagrams(String s, String p) {
        int sLen = s.length(), pLen = p.length();

        if (sLen < pLen) {
            return new ArrayList<Integer>();
        }

        List<Integer> ans = new ArrayList<Integer>();
        int[] sCount = new int[26];
        int[] pCount = new int[26];
        for (int i = 0; i < pLen; ++i) {
            ++sCount[s.charAt(i) - 'a'];
            ++pCount[p.charAt(i) - 'a'];
        }

        if (Arrays.equals(sCount, pCount)) {
            ans.add(0);
        }

        for (int i = 0; i < sLen - pLen; ++i) {
            --sCount[s.charAt(i) - 'a'];
            ++sCount[s.charAt(i + pLen) - 'a'];

            if (Arrays.equals(sCount, pCount)) {
                ans.add(i + 1);
            }
        }

        return ans;
    }
}

LCR 016.无重复字符的最小子串

给定一个字符串 s ,请你找出其中不含有重复字符的 最长连续子字符串 的长度。

class Solution {
    public int lengthOfLongestSubstring(String s) {
        int n = s.length();
        Set<Character> set = new HashSet<>();
        int l=0;
        int max=0;
        for(int r=0;r<n;r++){
            char c= s.charAt(r);
            while(set.contains(c)){
                set.remove(s.charAt(l));
                l++;
            }
            set.add(c);
            max=Math.max(max,r-l+1);
        }
        return max;
    }
}

 

posted @ 2025-03-10 21:57  柒墨轩  阅读(37)  评论(0)    收藏  举报