教林算法-二分算法

二分算法看似思路很简单,但是细节很重要。
二分算法套路:

var mySqrt = function(x) {
    let left = 0;
    let right = x;
    while (left <= right) {
        let mid = Math.floor(left + (right - left) / 2);
        // 在目标值的右边
        if (在目标值右边) {
            right = mid - 1;
        } else if (在目标值左边) {
            left = mid + 1;
        } else if (找到符合条件的目标值) {
            return 目标值
        }
    }
    return -1;
};

在解题过程中需要思考左右边界和目标值的跳出的条件,注意细节!!! 注意细节!!! 注意细节!!!

1.

在这里插入图片描述

解法一:暴力

var missingNumber = function(nums) {
    for (var i = 0; i < nums.length; i++) {
        if (nums[i] != i) {
            return i;
        }
    }
    return nums.length;
};
  • 时间复杂度分析:查找范围为0nums.length-1,及0n,复杂度为o(n);
  • 空间复杂度:没有使用额外的空间,空间复杂度为o(1);

解法二:二分法

排序数组中首先想到二分法
存在的数学关系:目标值的left:nums[i] = i; 目标值的right:nums[i] > i;

var missingNumber = function(nums) {
     let left = 0;
     let right = nums.length - 1;
     while (left <= right) {
         let mid = Math.floor(left + (right - left) / 2);
         // 目标值的左边,左边界右移
         if (nums[mid] == mid) {
             left = mid + 1;
         } else if (nums[mid] > mid) {
             // 目标值的右边
             right = mid - 1;
         }
     }
     return left;
};

需要注意细节,左右边界是闭区间[0, n-1]

  • why is left <= right not left < right?
    先确定要找哪一个值,left <= right 跳出循环的条件是?—》 left > right;这个题中需要找到最小符合的目标值,nums[i] > i && nums[i-1] = i;
  • 时间复杂度分析:由于每次二分后的查找范围是n、n\2…n/2^k,
    k就是查找了多少次,及折叠了多少次,n / 2^k >= 1-------> k = log2n 所以时间复杂度为o(logn);
    空间复杂度,没有使用额外的空间,只有left和right,所以为o(1);

2、

在这里插入图片描述

题目分析

这一题和上一题一样,可以理解为在一个数组0~x 查找一个目标值,首先思考满足的数学不等式:n * n <= x, 及在目标值的left和包括目标值 :nums[i] <= x/nums[i], right:nums[i] > x/nums[i];
目标值满足,yy <=x && (y+1)(y+1) >x; 可以照搬二分算法写出以下

var mySqrt = function(x) {
    let left = 0;
    let right = x;
    while (left <= right) {
        let mid = Math.floor(left + (right - left) / 2);
        if (mid <= x / mid && (mid + 1) > x / (mid + 1)) {
            return mid;
        } else if (mid <= x / mid) {
            left = mid + 1;
        } else {
            right = mid - 1;
        }
    }
    return 0;
};
var mySqrt = function(x) {
    let left = 0;
    let right = x;
    while (left < right) {
        // 由于0不能作为除数
        let mid = Math.floor(left + (right - left) / 2 + 1);
        if (mid <= x / mid) {
            left = mid;
        } else if (mid > x / mid) {
            right = mid - 1;
        }
    }
    return left;
};

首先分析细节什么时候跳出循环。由于目标值包括在左边界中,所以left = right就要跳出循环;

var mySqrt = function(x) {
    if (x == 0) return 0;
    let left = 1;
    let right = x;
    while (left <= right) {
        // 这时候不会出现0 作为除数
        let mid = Math.floor(left + (right - left) / 2);
        if (mid <= x / mid) {
            left = mid + 1;
        } else {
            right = mid - 1;
        }
    }
    return right;
};

这里为什么是left <= right ,在这里left = mid + 1,所以它跳出循环的是比目标值要大1;这里return left-1,也是符合条件的;

3、

在这里插入图片描述

二分解法

对于这道题的二分解法可以理解为,在数组numbers中先确定一个值i,然后利用二分查找是否存在一个值为target-i;

var twoSum = function(numbers, target) {
    for (let i = 0; i < numbers.length-1; i++) {
        let temp = target - numbers[i];// 二分算法中的target值
        let left = 0;
        let right = numbers.length - 1;
        while (left <= right) {
            let mid = Math.floor(left + (right - left) / 2);
            if (numbers[mid] > temp) {
                right = mid - 1;
            } else if (numbers[mid] < temp || i == mid) {
                left = mid + 1;
            } else {
                return [i, mid];
            }
        }
    }
    return [-1, -1];
};
  • 这道题是就是一个转化问题,如果利用暴力是o(n^2),再这就需要思路是不是可以改进,由于是排序数组,那么在确定一个值的时候是不是就可以采用二分算法了,二分算法的时间复杂度是o(nlogn)
  • 是不是还有更快的算法呢,接下来就是以一种双指针,自己先理解一下,再下一章节讲解。

快慢指针(双指针)

var twoSum = function(numbers, target) {
    let left = 0;
    let right = numbers.length - 1;
    while (left < right) {
        if (numbers[left] + numbers[right] > target) {
            right--;
        } else if (numbers[left] + numbers[right] < target) {
            left++;
        } else {
            return [left, right];
        }
    }
    return [-1, -1];
};
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值