剑指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;
}
}

浙公网安备 33010602011771号