面试150题-二分查找、位运算、数学
📕文章题目选自LeetCode面试150题-二分查找+位运算+数学部分
😊该文章内容基于作者本人思考总结,如有错误欢迎指正
🏠题单地址:https://leetcode.cn/studyplan/top-interview-150/
二分查找
LeetCode150-搜索插入位置-LC35
/**
* @ProjectName: Mounts
* @Author: DSXRIIIII
* @CreateDate: 2024/8/12 15:09
* @Email: lihh53453@hundsun.com
* @Description: 搜索插入位置
*/
public class LC35 {
public int searchInsert(int[] nums, int target) {
int left = 0;
int right = nums.length - 1;
while(left <= right){
int mid = left + (right - left) / 2;
if(target == nums[mid]){
return mid;
}
if(target < nums[mid]) right = mid - 1;
if(target > nums[mid]) left = mid + 1;
}
return left;
}
}
LeetCode150-搜索二维矩阵-LC74
/**
* @ProjectName: Mounts
* @Author: DSXRIIIII
* @CreateDate: 2024/8/12 15:32
* @Email: lihh53453@hundsun.com
* @Description: 搜索二维矩阵
* 二分查找
* 将二位数组转化为一维
* 坐标位置就是matrix[mid/matrix[0].length][mid%matrix[0].length]
* 注意区分行列
*/
public class LC74 {
public boolean searchMatrix(int[][] matrix, int target) {
int left = 0;
//int right = matrix.length * matrix[0].length;
int right = matrix.length * matrix[0].length - 1;
while(left <= right){
int mid = left + (right - left) / 2;
//int value = matrix[mid/matrix.length][mid%matrix.length];
int value = matrix[mid/matrix[0].length][mid%matrix[0].length];
if(target < value){
right = mid - 1;
}else if(target > value){
left = mid + 1;
}else{
return true;
}
}
return false;
}
}
LeetCode150-寻找峰值-LC162
/**
* @ProjectName: Mounts
* @Author: DSXRIIIII
* @CreateDate: 2024/8/12 16:15
* @Email: lihh53453@hundsun.com
* @Description: 寻找峰值
* 1.直接找最大值 最大值就是峰值
* 2.时间复杂度为log(n) 使用二分查找
* 判断如果mid此时位于下坡路 那么此时可能找不到峰值
* 如果mid走上坡路 那么一定有峰值
*/
public class LC162 {
public int findPeakElement(int[] nums) {
int left = 0;
int right = nums.length - 1;
while(left <= right){
int mid = left + (right - left) / 2;
if(nums[mid + 1] < nums[mid]){
right = mid;
}else{
left = mid + 1;
}
}
return left;
}
}
LeetCode150-搜索旋转排序数组-LC33
/**
* @ProjectName: Mounts
* @Author: DSXRIIIII
* @CreateDate: 2024/8/12 17:04
* @Email: lihh53453@hundsun.com
* @Description: 搜索旋转排序数组
* 二分查找
* 左节点从0开始 右节点到n-1结束
* 找到严格递增区间 nums[0] <= nums[mid]
* 1.true:说明左区间为严格递增区间 3 4 5 6 7 1 2
* 1.1.target在[0,mid]之间 right = mid - 1 缩小到左递增区间
* 1.2 target在该区间内 left = mid + 1;
* 2.false:说明右区间为严格递增区间 6 7 1 2 3 4 5
* 2.1.target在[mid,n - 1]之间 left = mid + 1 缩小到右递增区间
* 2.2 target在不该区间内 right = mid - 1;
*/
public class LC33 {
public int search(int[] nums, int target) {
int left = 0;
int right = nums.length - 1;
while(left <= right){
int mid = left + (right - left) / 2;
if (target == nums[mid]) {
return mid;
}
//选择一个有序数组进行处理
if (nums[0] <= nums[mid]) {
if (nums[0] <= target && target < nums[mid]) {
right = mid - 1;
} else {
left = mid + 1;
}
} else {
if (nums[mid] < target && target <= nums[nums.length - 1]) {
left = mid + 1;
} else {
right = mid - 1;
}
}
}
return -1;
}
}
LeetCode150-在排序数组中查找元素的第一个和最后一个位置-LC34
/**
* @ProjectName: Mounts
* @Author: DSXRIIIII
* @CreateDate: 2024/8/12 17:47
* @Email: lihh53453@hundsun.com
* @Description: 在排序数组中查找元素的第一个和最后一个位置
* 最后一个位置:满足条件nums[mid] <= target left = mid + 1 此时走到target后一位 所以rightIndex要-1
* 元素的第一个位置:满足条件nums[mid] >= target right = mid - 1 此时会向左走 直到走到边界
*/
public class LC34 {
public int[] searchRange(int[] nums, int target) {
int leftIndex = bin_search(nums,target,true);
int rightIndex = bin_search(nums,target,false) - 1;
if(leftIndex < rightIndex && rightIndex < nums.length && nums[leftIndex] == target && nums[target] == target){
return new int[]{leftIndex,rightIndex};
}
return new int[]{-1,-1};
}
private int bin_search(int[] nums, int target, boolean flag) {
int left = 0;
int right = nums.length - 1;
int ans = -1;
while(left <= right){
int mid = left + (left - right) / 2;
if(nums[mid] > target || (flag&&(nums[mid] >= target))){
right = mid - 1;
ans = mid;
}else {
//nums[mid] <= target 走到target满足条件 此时步数+1 走到target后一位
left = mid + 1;
}
}
return ans;
}
}
LeetCode150-寻找旋转排序数组中的最小值-LC153
/**
* @ProjectName: Mounts
* @Author: DSXRIIIII
* @CreateDate: 2024/8/13 9:43
* @Email: lihh53453@hundsun.com
* @Description: 寻找旋转排序数组中的最小值
* 将中间值与最后一个元素比较
* 1.nums[mid] < nums[nums.length - 1] 说明中间值在第二段数组 并且最小值在mid的左边
* 2,nums[mid] >= nums[nums.length - 1] 说明中间值在第一段数组 最小值在第二段数组 更新left = mid + 1;
* 3.right初始化为 nums.length - 2 因为不断要和nums[n-1]比较 假如nums[n-1]为最小值 left=mid+1 也会走到target
*/
public class LC153 {
public int findMin(int[] nums) {
int left = 0;
int right = nums.length - 2;
while(left <= right){
int mid = left + (right - left) / 2;
if(nums[mid] < nums[nums.length - 1]){
right = mid - 1;
}else {
left = mid + 1;
}
}
return nums[left];
}
}
(待解决)LeetCode150-寻找两个正序数组的中位数-LC4
二分查找总结
- 搜索插入位置:常规二分
- 搜索二维矩阵:坐标位置转换
nums[i/col][i%col]
+常规二分
- 寻找峰值:判断当前是否是下坡路
nums[mid + 1] < nums[mid]
下坡路没有峰值 更新left
和right
- 搜索旋转排序数组:找到严格递增数列 并且更新
left
和right
的坐标位置 二分
- 在排序数组中查找元素的第一个和最后一个位置:找到第一个target的位置 和 最后一个target的后一个位置 条件即为
nums[mid] >= target
或者 nums[mid] > target
- 寻找旋转排序数组中的最小值:二分条件
nums[mid] < nums[nums.length - 1]
区间从[0,n-2] 因为要不断与n-1进行比较 mid的位置也会不断改变 当相等时left也会递增到最后一位
位运算
LeetCode150-二进制求和-LC190
/**
* @ProjectName: Mounts
* @Author: DSXRIIIII
* @CreateDate: 2024/8/13 15:22
* @Email: lihh53453@hundsun.com
* @Description: 二进制求和
* 逢2进一 保存进位值
*/
public class LC67 {
public String addBinary(String a, String b) {
StringBuilder sb = new StringBuilder();
int temp = 0;
for(int i = a.length() - 1,j = b.length() - 1;i >=0 || j >=0;i--,j--){
int sum = temp;
sum+= i >= 0 ? a.charAt(i) - '0' : 0;
sum+= j >= 0 ? b.charAt(j) - '0' : 0;
sb.append(sum%2);
temp = sum / 2;
}
sb.append(temp == 1 ? "1":"");
return sb.reverse().toString();
}
}
LeetCode150-颠倒二进制位-LC190
/**
* @ProjectName: Mounts
* @Author: DSXRIIIII
* @CreateDate: 2024/8/13 13:25
* @Email: lihh53453@hundsun.com
* @Description:
* (x >> k) & 1 获取第k位数字
* (x << 1) | 1/0 将数字1/0添加到数字末尾
*/
public class LC190 {
public int reverseBits(int n) {
int temp = 0;
for (int i = 0; i < 32; i++) {
//1.(n & 1)取n的最后一位
//2.x<<(n) 将x左移n位
//3.temp = (n & 1) << (31 - i) | temp 将最后一位左移的结果添加到temp的末尾 或运算
temp |= (n & 1) << (31 - i);
//逻辑右移 去除最后一位
n>>>=1;
}
return temp;
}
}
LeetCode150-位1的个数-LC191
/**
* @ProjectName: Mounts
* @Author: DSXRIIIII
* @CreateDate: 2024/8/13 14:09
* @Email: lihh53453@hundsun.com
* @Description: 位1的个数
* (1<<i) i左移i位
* (n & (1<<i)) n的第(1<<i)位 1/0
* n & (n−1) 将最低为1 变为 0
* 当n不为0时 不断将1变成0
*
*/
public class LC191 {
public int hammingWeight(int n) {
int res = 0;
for (int i = 0; i < 31; i++) {
if((n & (1<<i)) != 0) res++;
}
return res;
}
public int hammingWeight_1(int n) {
int res = 0;
while(n != 0){
n &= (n - 1);
res++;
}
return res;
}
}
LeetCode150-只出现过一次的数字-LC136
/**
* @ProjectName: Mounts
* @Author: DSXRIIIII
* @CreateDate: 2024/8/13 14:23
* @Email: lihh53453@hundsun.com
* @Description: 只出现过一次的数字
* 异或 a⊕0=a 异或0为0 异或自己为0
* 异或的运算规则为 a⊕b⊕a = b⊕a⊕a = b⊕(a⊕a) = b⊕0 = b
*/
public class LC136 {
public int singleNumber(int[] nums) {
int res = 0;
for(int num:nums){
res ^= num;
}
return res;
}
}
LeetCode150-只出现过一次的数字II-LC137
/**
* @ProjectName: Mounts
* @Author: DSXRIIIII
* @CreateDate: 2024/8/13 14:40
* @Email: lihh53453@hundsun.com
* @Description: 只出现一次的数字 II
* 和只出现一次的数字一样 但是有一个数学的推导
* 异或的本质是做了比特位上做了模2的加法
* 所以此题采用比特位上做模3的加法
* 每一位数字mod三之后拼接即为只出现一次的数字
*/
public class LC137 {
public int singleNumber(int[] nums) {
int ans = 0;
for(int i = 0;i < 32;i++){
int temp = 0;
for(int num:nums){
temp += (num>>i) & 1;
//(num>>i) & 1 取出第i位数字 temp保存
}
ans |= temp % 3 << i;
//temp % 3 取模 并且左移
//左移结果 通过|=相加保存
//001 | 110 -> 111
}
return ans;
}
}
LeetCode150-数字范围按位取-LC201
/**
* @ProjectName: Mounts
* @Author: DSXRIIIII
* @CreateDate: 2024/8/13 15:07
* @Email: lihh53453@hundsun.com
* @Description:
* 1.从区间left到right的闭区间 每一个元素进行与运算
* 对于每一位 例如 数字范围按位与
* 9 1 0 0 1
* 10 1 0 1 0
* 11 1 0 1 1
* 12 1 1 0 0
* 这四个数进行&运算 一旦遇到0就一定是0
* 所以要找到这个区间的公共前缀 公共前缀对应的树即为要找的数
* 当 left >= right时 此时右区间的数已经比left小了 此时就代表找到了最小的公共前缀
* 利用 n&(n-1) 不断取出最后位的1
*/
public class LC201 {
public int rangeBitwiseAnd(int left, int right) {
while(left < right){
right &= right - 1;
}
return right;
}
}
位运算总结
(n - 1) & 1 取最后一位
n << i n左移i位补0
n >> i n右移i位左边补0
n >>> 1 逻辑右移 不会添加0或1
(n - 1) & n 不断删除最后一位的1
二进制求和:遍历取得最后一位的字符 遇2进1
余数添加 进位保存
颠倒二进制位:temp |= (c) << (31 - i)
; n>>>=1
; (n & 1)取n的最后一位 并且左移取最后一位 右移 去除最后一位 最后或运算合并数字
位1的个数:(n -1)&n
可以不断去除最后一位的1
只出现过一次的个数I:异或运算(^) a^0 = a
-> a^a = 0
只出现过一次的个数II:异或的逻辑就是模2
将每一位取出来temp += (num>>i) & 1
最后将(temp%3)<<(位数) | res
合并 即ans |= temp % 3 << i;
数字范围按位取:找前缀 [n,m]
当n <= m
时(n -1)&n
可以不断去除最后一位的1 找到前缀和
数学
LeetCode150-回文数-LC9
/**
* @ProjectName: Mounts
* @Author: DSXRIIIII
* @CreateDate: 2024/8/13 16:28
* @Email: lihh53453@hundsun.com
* @Description: 回文数
* 1.字符串反转比较即可
* 2.while(x > reverseNum)将数从最后一位不断的添加 如12321 最后比较即为 12 与 123 比较 此时是奇数情况
* 如1221 最后比较即为 12 与 12 比较 此时是偶数情况
* 3.最后比较两个值是否相等
* 4.注意是否是10的倍数 同样地,如果数字的最后一位是 0,为了使该数字为回文,则其第一位数字也应该是 0 只有0满足这个条件
*/
public class LC9 {
public boolean isPalindrome(int x) {
if (x < 0 || (x % 10 == 0 && x != 0)) {
return false;
}
int reverseNum = 0;
while(x > reverseNum){
reverseNum = reverseNum * 10 + x % 10;
x/=10;
}
return x == reverseNum || x == reverseNum / 10;
}
}
LeetCode150-加一-LC66
/**
* @ProjectName: Mounts
* @Author: DSXRIIIII
* @CreateDate: 2024/8/13 16:47
* @Email: lihh53453@hundsun.com
* @Description: 加一
* 如果没有满足进位的条件 digit[i]++ 后直接返回即可
* 如果有进位 989 在第二次循环时 会以 990的情况返回
* 如果 999 全部都进位了 创建新数组 第一位为1 其余位未被初始化都是0 直接返回即可
*/
public class LC66 {
public int[] plusOne(int[] digits) {
for (int i = digits.length - 1; i >= 0 ; i--) {
digits[i]++;
digits[i] = digits[i] % 10;
if(digits[i] != 0) return digits;
}
int[] res = new int[digits.length + 1];
res[0] = 1;
return res;
}
}
LeetCode150-阶乘后的零-LC172
/**
* @ProjectName: Mounts
* @Author: DSXRIIIII
* @CreateDate: 2024/8/13 17:00
* @Email: lihh53453@hundsun.com
* @Description: 阶乘后的零
* 即找五的个数 10就是2 * 5 但是在阶乘中5的数量少于2 多少个0就取决于多少个5
* 1 * 2 * 3 * 4 * 5 * 7 * 8 * 9 * 10
* 5的倍数个数为2 -> 10/5=2
* 对于阶乘 100
* 5的倍数个数为 -> 100/5=20
* 5 * 5 的倍数个数为 100/5*5=44 25 50 75 100 四个 以此类推
*/
public class LC172 {
public int trailingZeroes(int n) {
int ans = 0;
while(n != 0){
n /= 5;
ans += n;
}
return ans;
}
}
LeetCode150-x 的平方根-LC69
/**
* @ProjectName: Mounts
* @Author: DSXRIIIII
* @CreateDate: 2024/8/14 9:26
* @Email: lihh53453@hundsun.com
* @Description: x 的平方根
* 二分法
*/
public class LC69 {
public int mySqrt(int x) {
if(x == 1) return 1;
if(x == 0) return 0;
int left = 1;
int right = x / 2;
while(left <= right){
int mid = left + (right - left + 1)/2;
if(mid > x / mid){
right = mid - 1;
}else if(mid == x / mid){
return mid;
}else{
left = left + 1;
}
}
return left + 1;
}
}
LeetCode150-Pow(x, n)-LC50
/**
* @ProjectName: Mounts
* @Author: DSXRIIIII
* @CreateDate: 2024/8/14 9:47
* @Email: lihh53453@hundsun.com
* @Description: Pow(x, n)
* 递归求平方
* 1. 正数quickPow(x,N) 负数 1/quickPow(x,-N)
* 2. 递归 如X**2 = X*X X**3 = X**X**X
*/
public class LC50 {
public double myPow(double x, int n) {
long N = n;
return N >= 0 ? quickPow(x,N) : 1 / quickPow(x,-N);
}
public double quickPow(double x,Long N){
if(N == 0) return 1.0;
double y = quickPow(x,N / 2);
return N % 2 == 0 ? y * y : y * y * x;
}
}