数组
二分查找
704. 二分查找 - 力扣(LeetCode) (leetcode-cn.com)
题解:
class Solution {
public int search(int[] nums, int target) {
int mid;
int left = 0, right = nums.length-1;
while (left <= right){
// mid = (left + right) / 2;
mid = left + ((right-left) / 2);//可以防止溢出
if (nums[mid] == target){
return mid;
}
else if (nums[mid] > target){
right = mid-1;
}
else if(nums[mid] < target){
left = mid+1;
}
}
return -1;
}
}
特点:数组有序且不重复。
难点:区间的确定,体会初状态left=0,right=length和初状态left=0和right=length-1的区别。
35. 搜索插入位置 - 力扣(LeetCode) (leetcode-cn.com)
class Solution {
public int searchInsert(int[] nums, int target) {
int left = 0, right = nums.length-1;
int mid = left;
while (left <= right) {
mid = left + (right - left) / 2;
if (nums[mid] == target)
return mid;
else if(nums[mid] < target)
left = mid + 1;
else
right = mid - 1;
}
/* if (nums[mid] > target)
return mid;
else
return mid + 1;*/
//根据if的判断条件,left左边的值一直保持小于target,right右边的值一直保持大于等于target,而且left最终一定等于right+1,这么一来,循环结束后,在left和right之间画一条竖线,恰好可以把数组分为两部分:left左边的部分和right右边的部分,而且left左边的部分全部小于target,并以right结尾;right右边的部分全部大于等于target,并以left为首。所以最终答案一定在left的位置
}
}
34. 在排序数组中查找元素的第一个和最后一个位置 - 力扣(LeetCode) (leetcode-cn.com)
class Solution {
public int[] searchRange(int[] nums, int target) {
int left = 0, right = nums.length - 1;
int mid = left,flag = -1;
int loc[] = new int[2];
loc[0] = -1;
loc[1] = -1;
while (left <= right) {
mid = left + (right-left) / 2;
if (nums[mid] == target){
flag = 0;
break;
}
else if(nums[mid] > target){
right = mid - 1;
}
else
left = mid + 1;
}
if(flag == 0) {
loc[0] = mid;
loc[1] = mid;
while ( loc[0] >= 0&&nums[loc[0]] == nums[mid]){
loc[0]--;
}
loc[0]++;
while ( loc[1] <= nums.length - 1&&nums[loc[1]] == nums[mid]){
loc[1]++;
}
loc[1]--;
}
return loc;
}
}
我的原始解法,首先使用二分找到一个target的位置,然后分别向前读,向后读,找到自己想要的起点值和终点值。
但是,实际上有一种对于二分法了解的够深之后的其他方法。
例如:
[1, 3, 4, 4, 4, 5, 6]
0 1 2 3 4 5 6
很直观的可以看出,我们需要查到的结果分别是2和4。那么我们可以把2和4分别当作我们需要查找的下标。
这种算法的核心是,在已经查到的结果的左侧或者右侧区间,再次搜索。
class Solution {
public int[] searchRange(int[] nums, int target) {
int left = 0, right = nums.length - 1;
int mid = left,flag = -1;
int loc[] = new int[2];
loc[0] = -1;
loc[1] = -1;
while (left <= right) {
mid = left + (right-left) / 2;
if(nums[mid] > target) {
right = mid - 1;
}
else if (nums[mid] < target) {
left = mid + 1;
}
else
{
loc[0] = mid;
right = mid - 1;
}
}
left = 0;
right = nums.length - 1;
while (left <= right) {
mid = left + (right-left) / 2;
if(nums[mid] > target) {
right = mid - 1;
}
else if (nums[mid] < target) {
left = mid + 1;
}
else
{
loc[1] = mid;
left = mid + 1;
}
}
return loc;
}
}
这种改进,好处是,省去了遍历,只用了二分。坏处是,代码复用率很低。观察可以得到,两次二分的代码十分相似。因此,我们可以将其写成一个函数,通过传入一个变量进行控制输出。()下面的代码来自leetcode大佬。
class Solution {
public int[] searchRange(int[] nums, int target) {
int[] res = new int[] {-1, -1};
res[0] = binarySearch(nums, target, true);
res[1] = binarySearch(nums, target, false);
return res;
}
//leftOrRight为true找左边界 false找右边界
public int binarySearch(int[] nums, int target, boolean leftOrRight) {
int res = -1;
int left = 0, right = nums.length - 1, mid;
while(left <= right) {
mid = left + (right - left) / 2;
if(target < nums[mid]) //说明目标在此时的mid左边的区间
right = mid - 1;
else if(target > nums[mid]) //说明此时目标在mid右边的区间
left = mid + 1;
else {
res = mid; //此时,res=target 的情况,res已经是一个下标的位置。
//处理target == nums[mid]
if(leftOrRight) //如果是选择最小值,则在此时的res左边区间再次搜索。
right = mid - 1;
else
left = mid + 1; //如果是选择最大值,则在此时的res右边区间再次搜索。
}
}
return res;
}
}
69. x 的平方根 - 力扣(LeetCode) (leetcode-cn.com)
本题也可以使用二分的算法来求解。
class Solution {
public int mySqrt(int x) {
if(x == 0 || x == 1)
return x;
int left = 1 ,right = x;
int mid = 1;
while (left <= right) {
mid = left + (right - left) / 2;
if ( mid > x / mid) {
right = mid - 1;
}
else if (mid < x / mid) {
left = mid + 1;
}
else {
return mid;
}
}
return right;
/* 这里是对最终返回值的判断。但是实际上,直接返回right即可
if (mid > x / mid) {
return mid - 1; // 即为right
}
else
return mid; //即为right
*/
}
}
367. 有效的完全平方数 - 力扣(LeetCode) (leetcode-cn.com)
此题,首先给出最容易想到的解法。
class Solution {
public boolean isPerfectSquare(int num) {
if (num == 1)
return true;
int left = 1, right = num - 1;
int mid = 1;
while ( left <= right) {
mid = left + (right - left) / 2;
if (mid > num / mid) {
right = mid - 1;
}
else if (mid < num / mid) {
left = mid + 1;
}
else
break;
}
if ((long)mid * mid == num)
return true;
return false;
}
}
这里最终判断加long的原因是,由于整数乘法会直接取余,因此,如果直接使用mid == num / mid,会出现误判断。例如,2 == 5 / 2,的判定结果是true。这种问题,可以在获得mid之后,进行判断处理。
当然,还有一种更加简单的判定方法。
class Solution {
public boolean isPerfectSquare(int num) {
if (num == 1)
return true;
int left = 1, right = num - 1;
int mid = 1;
while ( left <= right) {
mid = left + (right - left) / 2;
if (mid > num / mid) {
right = mid - 1;
}
else if (mid < num / mid) {
left = mid + 1;
}
else{
if (num % mid == 0) //通过% 来判定是否整除
return true;
break; // 如果不加这个break,程序可能会陷入死循环
}
}
return false;
}
}