1、整数除法
class Solution {
public int divide(int a, int b) {
if(a==0) return 0;
if(a==b) return 1;
if(a==Integer.MIN_VALUE&&b==-1) return Integer.MAX_VALUE;
int res = 0;
//用flag标志算式最终符号,将a、b转换成负数进行计算
int flag = (a>0)^(b>0)?-1:1;
if(a>0) a=-a;
if(b>0) b=-b;
while(a<=b) {
//相减的次数就是最终答案
a = a - b;
++res;
}
res = (flag==1)? res:-res;
return res;
}
}
2、二进制加法
class Solution {
public String addBinary(String a, String b) {
StringBuilder stringBuilder=new StringBuilder();
int i=a.length()-1,j=b.length()-1;
//定义一个临时值,存储两个字符的进位
int temp=0;
while (i>=0||j>=0){
//判断是否到了a字符串中的字符是否全部相加完,全部相加完就直接返回0,没有直接返回该字符串的Ascll码值
int a1=i>=0?a.charAt(i--)-'0':0;
//判断是否到了b字符串中的字符是否全部相加完,全部相加完就直接返回0,没有直接返回该字符串的Ascll码值
int b1=j>=0?b.charAt(j--)-'0':0;
//求两个字符和进位的和
int sum=a1+b1+temp;
//如果和大于等于2,进位为1,否则为0
temp=sum>=2?1:0;
//如果和大于等于2,将和减2,否则为原值
sum=sum>=2?sum-2:sum;
//将sum加入stringBuilder中;
stringBuilder.append(sum);
}
//当两个字符串的索引都小于0,将最后的进位加入stringBuilder
if (temp==1){
stringBuilder.append(1);
}
//将stringBuilder反向遍历,并返回
return stringBuilder.reverse().toString();
}
}
3、前 n 个数字二进制中 1 的个数
class Solution {
public int[] countBits(int n) {
//新建一个数组,大小n+1,用于存放[0,n]的所有结果,初始res[0]=0
//当i为偶数,i的二进制位数和i>>1相同,i为奇数,二进制位数等于i>>1+1,比如i=5,res[5] = res[5/2]+1=res[2]+1=2
//最后把结果数组返回
int[] res = new int[n+1];
res[0] = 0;
for(int i = 1; i <= n; i++){
res[i] = res[i>>1] + i % 2;
}
return res;
}
}
4、只出现一次的数字
位运算,采用二进制,将每一位的数字相加,对3取余不为零就记录
class Solution {
public int singleNumber(int[] nums) {
int ret = 0;
for (int i = 0; i < 32; i++) {
int cnt = 0;
for (int num : nums) {
//取最后一位数字,各位相加
cnt += num >> i & 1;
}
if (cnt % 3 != 0) {
ret += 1 << i;
}
}
return ret;
}
}
5、单词长度的最大乘积
a|=b的意思就是把a和b按位或然后赋值给a。 按位或的意思就是先把a和b都换成2进制,然后用或操作,相当于a=a|b。
class Solution {
public int maxProduct(String[] words) {
int length = words.length;
int[] masks = new int[length];
for (int i = 0; i < length; i++) {
String word = words[i];
int wordLength = word.length();
for (int j = 0; j < wordLength; j++) {
char c = word.charAt(j);
//二进制表示从低到高的第 0 位到第 25 位的每一位分别表示单词中是否出现 a 到 z 的每个字母,如果一个二进制位是 1 则表示该位对应的字母在单词中出现,如果一个二进制位是 0 则表示该位对应的字母不在单词中出现。
masks[i] |= 1 << (c - 'a');
}
}
int maxValue = 0;
for (int i = 0; i < length; i++) {
for (int j = i + 1; j < length; j++) {
//如果按位与运算结果是 0,则这两个单词不含有公共字母
if ((masks[i] & masks[j]) == 0) {
int value = words[i].length() * words[j].length();
maxValue = Math.max(maxValue, value);
}
}
}
return maxValue;
}
}
6、排序数组中两个数字之和
解法一:二分查找
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 + low;
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};
}
}
解法二:双指针
class Solution {
public int[] twoSum(int[] numbers, int target) {
int low = 0, high = numbers.length - 1;
while (low < high) {
int sum = numbers[low] + numbers[high];
if (sum == target) {
return new int[]{low, high};
} else if (sum < target) {
++low;
} else {
--high;
}
}
return new int[]{-1, -1};
}
}
7、数组中和为 0 的三个数
class Solution {
public List<List<Integer>> threeSum(int[] nums) {
int len = nums.length;
List<List<Integer>> res = new ArrayList<>();
if(nums == null || len < 3) return res;
Arrays.sort(nums);
for(int i = 0; i < len - 2;i++){
// 如果最小的数比0大,直接退出循环
if (nums[i] > 0) {
break;
}
if (i > 0 && nums[i] == nums[i - 1]) {
continue;
}
// 在 i + 1 ... nums.length - 1 中查找相加等于 -nums[i] 的两个数
int target = -nums[i];
int left = i + 1;
int right = len - 1;
while(left < right){
int sums = nums[left] + nums[right];
if(target == sums){
res.add(Arrays.asList(nums[i],nums[left],nums[right]));
left++;
right--;
// 去重
while (left < right && nums[left] == nums[left - 1]) {
left++;
}
while (left < right && nums[right] == nums[right + 1]) {
right--;
}
}else if(sums < target){
left++;
}else{
right--;
}
}
}
return res;
}
}
8、和大于等于 target 的最短子数组
class Solution {
public int minSubArrayLen(int s, int[] nums) {
int n = nums.length;
if (n == 0) {
return 0;
}
int ans = Integer.MAX_VALUE;
// start 和 end 分别表示子数组(滑动窗口窗口)的开始位置和结束位置,维护变量 sum 存储子数组中的元素和(即从 nums[start] 到 nums[end] 的元素和)
int start = 0, end = 0;
int sum = 0;
while (end < n) {
sum += nums[end];
while (sum >= s) {
ans = Math.min(ans, end - start + 1);
sum -= nums[start];
start++;
}
end++;
}
return ans == Integer.MAX_VALUE ? 0 : ans;
}
}
9、乘积小于 K 的子数组
class Solution {
public int numSubarrayProductLessThanK(int[] nums, int k) {
int left = 0;
int ret = 0;
int total = 1;
for (int right = 0; right < nums.length; right++) {
total *= nums[right];
while (left <= right && total >= k) {
total /= nums[left++];
}
if (left <= right) {
//窗口每次移动后,ret都可以增加 right - left + 1个子数组
ret += right - left + 1;
}
}
return ret;
}
}
10、和为 k 的子数组
class Solution {
public int subarraySum(int[] nums, int k) {
//设sum[i]表示[0,i)的和,题目转换为求sum[end] - sum[start]=k的个数,转换为两数之差的个数
int sum = 0;
int count = 0;
//存储sum[end]-k对应的个数
Map<Integer, Integer> map = new HashMap();
map.put(0, 1);
for(int i = 0; i < nums.length; i++){
sum += nums[i];
//sum[end]-k=sum[start],从end查start的个数,不能从start查end的个数
count += map.getOrDefault(sum - k, 0);
//为后边计数
map.put(sum, map.getOrDefault(sum, 0) + 1);
}
return count;
}
}
11、0 和 1 个数相同的子数组
class Solution {
public int findMaxLength(int[] nums) {
HashMap<Integer, Integer> map = new HashMap<>();
// map存储的是下标,假设中间计算出前10个元素和为0,那么子数组长度为9-(-1)=10, 9是当前遍历数组下标
map.put(0, -1);
int pre_sum = 0;
int ret = 0;
for (int i = 0; i < nums.length; i++) {
// 将数组中的0换成-1,求和为0的最长子数组,转换成前缀和问题
pre_sum += nums[i] == 0 ? -1 : 1;
// 判断哈希表中是否存在值为pre_sum的key
if (map.containsKey(pre_sum)) {
// 若存在pre_sum的key,更新ret为max(ret, 当前下标 - key对应的value + 1)
ret = Math.max(ret, i - map.get(pre_sum));
} else {
map.put(pre_sum, i);
}
}
return ret;
}
}
12、左右两边子数组的和相等
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;
}
}
13、二维子矩阵的和
class NumMatrix {
//辅助矩阵,坐标sums[row + 1][col + 1]存入的是matrix[0][0]到matrix[row][col]矩阵的和
private int[][] sums;
//初始化辅助矩阵,为方便计算,sums行列数各+1
public NumMatrix(int[][] matrix) {
if (matrix.length == 0 || matrix[0].length == 0) {
return;
}
//遍历计算辅助矩阵,每行的值为上一个单元格的值加当前行的和
sums = new int[matrix.length + 1][matrix[0].length + 1];
for (int i = 0; i < matrix.length; i++) {
//记录每行元素的前缀和
int rowSum = 0;
for (int j = 0; j < matrix[0].length; j++) {
rowSum += matrix[i][j];
sums[i+1][j+1] = sums[i][j + 1] + rowSum;
}
}
}
//使用辅助矩阵计算指定矩阵区域的和
public int sumRegion(int row1, int col1, int row2, int col2) {
return sums[row2 + 1][col2 + 1] - sums[row1][col2 + 1] - sums[row2 + 1][col1] + sums[row1][col1];
}
}
/**
* Your NumMatrix object will be instantiated and called as such:
* NumMatrix obj = new NumMatrix(matrix);
* int param_1 = obj.sumRegion(row1,col1,row2,col2);
*/
14、字符串中的变位词?
class Solution {
public boolean checkInclusion(String s1, String s2) {
int len1 = s1.length(), len2 = s2.length();
if(len1 > len2) return false;
// charDiffs统计每一个字符的出现(++)和消失(--)
int[] charDiffs = new int[26];
// 统计有差异的字符的个数,取值范围[0, 25]
int diffCounts = 0;
// 考察第一个窗口,并得到考察后的charDiffs[]
for(int i = 0; i < len1; i++) {
charDiffs[s1.charAt(i) - 'a']--;
charDiffs[s2.charAt(i) - 'a']++;
}
// 考察第一个窗口是否为变位词
for(int charDiff : charDiffs) {
if(charDiff != 0) diffCounts++;
}
// 是则直接返回true
if(diffCounts == 0) return true;
// 否则滑动窗口,每次滑动一位
for(int i = len1; i < len2; i++) {
int removed = s2.charAt(i - len1) - 'a';
int added = s2.charAt(i) - 'a';
// 离开窗口和进入窗口的字符相同,跳过
if(removed == added) continue;
// 关于removed的字符的差异为0时,因为removed离开,导致字符种类差分+1,且在charDiffs上该字符差分--
if(charDiffs[removed] == 0) diffCounts++;
charDiffs[removed]--; // 离开
// 因为有上一行的离开动作,需要再次确认该字符的差异数
if(charDiffs[removed] == 0) diffCounts--;
// 关于added的字符的差异为0时,因为added加入,导致字符种类差分+1,且在charDiffs上该字符差分++
if(charDiffs[added] == 0) diffCounts++;
charDiffs[added]++; // 进入
// 因为有上一行的进入动作,需要再次确认该字符的差异数
if(charDiffs[added] == 0) diffCounts--;
if(diffCounts == 0) return true;
}
return false;
}
}
15、字符串中的所有变位词?
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;
}
}
16、不含重复字符的最长子字符串
class Solution {
public int lengthOfLongestSubstring(String s) {
// 哈希集合,记录每个字符是否出现过
Set<Character> num = new HashSet<Character>();
int n = s.length();
// 右指针,初始值为 -1,相当于我们在字符串的左边界的左侧,还没有开始移动
int right = -1, ans = 0;
for (int left = 0; left < n; left++) {
if (left != 0) {
// 左指针向右移动一格,移除一个字符
num.remove(s.charAt(left - 1));
}
while (right + 1 < n && !num.contains(s.charAt(right + 1))) {
// 不断地移动右指针
num.add(s.charAt(right + 1));
right++;
}
// 第 left 到 right 个字符是一个极长的无重复字符子串
ans = Math.max(ans, right - left + 1);
}
return ans;
}
}
17、含有所有字符的最短字符串?
class Solution {
public String minWindow(String s, String t) {
int len1 = s.length(),len2 = t.length();
//输出的字符串,初始为空
String res = "";
if(len1<len2) return res;
//t1为t的数组,s1为s的数组
int[] t1 = new int[100];
int[] s1 = new int[100];
for(int i=0;i<len2;i++){
t1[t.charAt(i)-'A']++;
}
//diff标记不同,当为0时代表已找到
int diff = 0;
for(int i:t1){
if(i!=0) diff++;
}
//left,right双指针
int left = 0;
int min=Integer.MAX_VALUE;
for(int right=0;right<len1;right++){
//s1[x]++后如果等于t1[x],代表字符已全部找到
int x = s.charAt(right)-'A';
s1[x]++;
if(t1[x]==s1[x]) diff--;
while(diff==0){
//对比赋值res
int l = right-left+1;
if(l<min){
min=l;
res=s.substring(left,right+1);
}
//t1[y]!=0表示 t 含有此字符,只要s1[y]的值不小于t1[y],那么结果不影响,如果小于则diff++;
int y = s.charAt(left)-'A';
s1[y]--;
if(t1[y]!=0&&s1[y]<t1[y]){
diff++;
}
left++;
}
}
return res;
}
}
18、有效的回文
class Solution {
public boolean isPalindrome(String s) {
int left = 0;
int right = s.length() - 1;
// 利用双指针思想向字符串中间逼近
while (left <= right) {
if (!Character.isLetterOrDigit(s.charAt(left))) {
left += 1;
} else if (!Character.isLetterOrDigit(s.charAt(right))) {
right -= 1;
} else {
char char1 = Character.toLowerCase(s.charAt(left++));
char char2 = Character.toLowerCase(s.charAt(right--));
if (char1 != char2) {
return false;
}
}
}
return true;
}
}
19、最多删除一个字符得到回文
class Solution {
public boolean validPalindrome(String s) {
for(int left = 0, right = s.length() - 1; left < right; left++, right--){
// 如果不相等,则分两种情况:删除左边的元素,或者右边的元素,再判断各自是否回文,满足一种即可。
if(s.charAt(left) != s.charAt(right))
return isPalindrome(s, left+1, right) || isPalindrome(s, left, right - 1);
}
return true;
}
// 判断字符串 s 的 [left, right] 是否回文
private boolean isPalindrome(String s, int left , int right){
while (left < right){
if (s.charAt(left++) != s.charAt(right--))
return false;
}
return true;
}
}
20、回文子字符串的个数
class Solution {
public int countSubstrings(String s) {
if (s == null || s.length() == 0) {
return 0;
}
int count = 0;
//字符串的每个字符都作为回文中心进行判断,中心是一个字符或两个字符
for (int i = 0; i < s.length(); ++i) {
count += countPalindrome(s, i, i);
count += countPalindrome(s, i, i+1);
}
return count;
}
//从字符串的第start位置向左,end位置向右,比较是否为回文并计数
private int countPalindrome(String s, int start, int end) {
int count = 0;
while (start >= 0 && end < s.length() && s.charAt(start) == s.charAt(end)) {
count++;
start--;
end++;
}
return count;
}
}