记录一下自己刷题的历程以及代码,会尽量把在本地测试包含main
函数的完整代码贴上,以及一些注释掉的输出语句。写题过程中参考了 代码随想录。会附上一些个人的思路,如果有错误,可以在评论区提醒一下。
原题链接
right = nums.length - 1;
是一种闭区间的写法,左右下标都在数组范围内,此时while
循环中需要加上=
,否则会在nums = [5],target = 5
的情况下报错。
class Solution {
public int search(int[] nums, int target){
if(target<nums[0] || target>nums[nums.length-1]){
return -1;
}
int left = 0;
int right = nums.length - 1;
int mid = 0;
while(left <= right){
//System.out.println("left:" + left + " mid:" + mid + " right:" + right);
mid = (left + right) / 2;
if(target > nums[mid]) {
left = mid + 1;
}else if(target < nums[mid]){
right = mid - 1;
}else{
return mid;
}
}
return -1;
}
}
public class main {
public static void main(String[] args) {
Solution solution = new Solution();
int[] nums = new int[]{-1,0,3,5,9,12};
int target = 9;
System.out.println(solution.search(nums,target));
}
}
原题链接
总共四种情况
①找到元素,返回middle
找不到元素,循环正常退出,此时有right = left-1
②在所有元素之前,此时left = 0;right = -1
target需要插入0,即left的位置
③在两个元素中间,此时nums[left]>target;nums[right]
④在所有元素之后,此时right=nums.length()-1;left=nums.length()
target需要插入left的位置
class Solution {
public int searchInsert(int[] nums, int target) {
int left = 0;
int right = nums.length - 1;
int mid = 0;
while(left <= right){
mid = (left + right) / 2;
//System.out.println("left:" + left + " mid:" + mid + " right:" + right);
if(target > nums[mid]) {
left = mid + 1;
}else if(target < nums[mid]){
right = mid - 1;
}else{
return mid;
}
}
return left;
}
}
public class main {
public static void main(String[] args) {
Solution solution = new Solution();
int[] nums = new int[]{1,3};
int target = 0;
System.out.println(solution.searchInsert(nums,target));
}
}
原题链接
方法一:找到nums[mid] == target
时在mid左右继续查找相等的值,在二分查找的代码上增加一些功能即可,但是当连续的数字过多时容易将复杂度退到O(n)
,相当于遍历的复杂度。可以通过这道题,但是时间复杂度较高。
方法二:分别寻找左右边界,左边界指向 该元素第一个位置之前的元素,右边界指向最后一个位置之后的元素。
例如找左边界:nums[mid] == target
时也要把范围划到mid左边,因为我们需要找的是target的边界
int left = 0;
int right = nums.length - 1;
int leftBorder = -2;
while(left <= right){
if(target > nums[mid]) {
left = mid + 1;
}else if(target < nums[mid]){
right = mid - 1;
leftBorder = right;
}else if(target == nums[mid]){
right = mid - 1;
leftBorder = right;
}
}
查找左右边界的函数内部的判断语句是由上面的判断语句三种情况合并而成,结果如下:
class Solution {
public int[] searchRange(int[] nums, int target) {
int leftBorder = searchLeft(nums, target);
int rightBorder = searchRight(nums, target);
if(leftBorder == -2 || rightBorder == -2) return new int[]{-1 ,-1};
if(rightBorder - leftBorder > 1) return new int[]{leftBorder + 1,rightBorder - 1};
return new int[]{-1 ,-1};
}
public int searchLeft(int[] nums, int target){
int left = 0;
int right = nums.length - 1;
int leftBorder = -2;
while(left <= right){
int mid = (left + right) / 2;
if(target > nums[mid]) {
left = mid + 1;
}else{
right = mid - 1;
leftBorder = right;
}
}
return leftBorder;
}
public int searchRight(int[] nums, int target){
int left = 0;
int right = nums.length - 1;
int rightBorder = -2;
while(left <= right){
int mid = (left + right) / 2;
if(target < nums[mid]) {
right = mid - 1;
}else{
left = mid + 1;
rightBorder = left;
}
}
return rightBorder;
}
}
public class main {
public static void main(String[] args) {
Solution solution = new Solution();
int[] nums = new int[]{2, 2};
int target = 3;
int[] array = solution.searchRange(nums,target);
for (int i = 0; i < array.length; i++) {
System.out.print(array[i] + " ");
}
}
}
原题链接
一上来会想到遍历,但是时间效率显然是比较低的
在二分的基础上做改进:题目给定0 <= x <= 2^31 - 1
。用 mid * mid
与 x
做比较时,int型的mid
做平方运算很容易超出int的数据显示范围
方法①:改成长整形long进行比较,输出时做类型转换。
方法②:用x / mid > mid
来做判断,注意该方法需要避免mid == 0
的情况,除数为0会报错(x<2
时,mid的初值为0,索性在开头将return 0
和 return 1
的方法都判断输出)。
class Solution {
public int mySqrt(int x) {
if(x < 1) return 0;
else if(x < 4) return 1;
int left = 0;
int right = x;
while(left <= right){
int mid = (left + right) / 2;
System.out.println("left:" + left + " mid:" + mid + " right:" + right);
if(x / mid > mid) {
left = mid + 1;
}else if(x / mid < mid){
right = mid - 1;
}else{
return mid;
}
}
return right;
}
}
public class main {
public static void main(String[] args) {
Solution solution = new Solution();
int x = 2147395599;
System.out.print(solution.mySqrt(x));
}
}