1、基本思想
前提条件:有序数组+无重复元素(可有可无)
把数组(默认升序)从中间分开,每次与中间值比较,如果小于中间值则只需要找左边,如果大于中间值只需要找右边,等于中间值直接返回下标即可!
2、练习题(建议先去leetcode做,然后再看答案)
- 704二分查找
// 方式一:左闭右闭[left,right]
class Solution {
public int search(int[] nums, int target) {
// 闭区间:r=nums.length-1
int l = 0;int r = nums.length - 1;
// 注意:l<=r
// 为什么有等于号?
// [1],只有一个元素的时候,l=0,r=0,m=0,l==r是有意义的
while (l <= r){
int m = l + (r - l) / 2;
if (nums[m] == target) return m;
else if (nums[m] > target) {
// 闭区间,所以是r=m-1
r = m - 1;
}else{
l = m + 1;
}
}
return -1;
}
}
// 方式一:左闭右开[left,right)
class Solution {
public int search(int[] nums, int target) {
// 开区间:r=nums.length
int l = 0;int r = nums.length;
// [1],只有一个元素的时候,l=0,r=1,m=0,可以比较元素,所以l==r是可以退出的,没有意义的
while (l < r){
int m = l + (r - l) / 2;
if (nums[m] == target) return m;
else if (nums[m] > target) {
// 开区间,所以是r=m
r = m;
}
else {
l = m + 1;
}
}
return -1;
}
}
- 35.搜索插入位置
// 方法一:暴力破解
// 遍历数组,查找插入位置
// 如果有此元素则该位置就是插入位置;如果没有此元素,则第一个大于目标元素的数组位置则为插入位置
class Solution {
public int searchInsert(int[] nums, int target) {
for (int i = 0; i < nums.length; i ++) {
if (nums[i] >= target) return i;
}
return nums.length;
}
}
// 方法二:二分查找
// 如果数组中存在此元素,利用二分找到此元素位置返回
// 如果没有此元素,则r的位置正好是小于目标元素的位置,则r+1正是需要插入的位置
class Solution {
public int searchInsert(int[] nums, int target) {
int l = 0; int r = nums.length - 1;
while (l <= r){
int m = l + (r - l) / 2;
// 等于某个元素
if (nums[m] == target) return m;
else if (nums[m] > target) {
r = m - 1;
}else{
l = m + 1;
}
}
// 最左边
// 最右边
// 某个元素后面
return r + 1;
}
}
- 34.在排序数组中查找元素的第一个和最后一个位置
// 方法一:二分查找
// 首先通过二分查找到该元素位置(因为有重复元素找到的位置不一定是最左边或者最右边),然后左边滑动指针找到最左边,右边滑动直至找到最右边
class Solution {
public int[] searchRange(int[] nums, int target) {
int index = binarySearch(nums, target);
if (index == -1) {
return new int[]{-1, -1};
}
// 左滑指针
int left = index;
while (left - 1 >= 0 && nums[left - 1] == target){
left = left - 1;
}
// 右滑指针
int right = index;
while (right + 1 < nums.length && nums[right + 1] == target){
right = right + 1;
}
return new int[]{left, right};
}
public int binarySearch(int[] nums, int target){
int l = 0; int r = nums.length - 1;
while (l <= r){
int m = l + (r - l) / 2;
if (nums[m] == target) return m;
else if (nums[m] > target) r = m - 1;
else l = m + 1;
}
return -1;
}
}
// 方法二:二分查找
// 如果元素存在的话,通过二分查找找到左右边界
// 如果元素不存在直接返回{-1,-1}
class Solution {
public int[] searchRange(int[] nums, int target) {
int leftBorder = getLeftBorder(nums, target);
int rightBorder = getRightBorder(nums, target);
// 只有任何一个边界没有找到就是没有此元素
if (rightBorder == -2 || leftBorder == -2){
return new int[]{-1, -1};
}
// 左右边界找的是开区间,只要存在肯定是间隔大于1
if (rightBorder - leftBorder > 1){
return new int[]{leftBorder + 1, rightBorder - 1};
}
return new int[]{-1, -1};
}
// 找到左边界,此左边界是需要+1的
// 左边界是从右边一步一步找到第一个小于目标值的位置
public int getLeftBorder(int[] nums, int target){
int l = 0; int r = nums.length - 1;
int leftBorder = -2;
while (l <= r){
int m = l + (r - l) / 2;
// 如果中间值大于等于目标值,则r = m - 1;同时更新左边界的值
if (nums[m] >= target){
r = m - 1;
leftBorder = r;
}else{
l = m + 1;
}
}
return leftBorder;
}
// 找到右边界,此右边界是需要-1的
// 右边界,是从右边找到第一个大于目标值的位置
public int getRightBorder(int[] nums, int target){
int l = 0; int r = nums.length - 1;
int rightBorder = -2;
while (l <= r){
int m = l + (r - l) / 2;
if (nums[m] > target){
r = m - 1;
}else{
// 如果中间值小于等于目标值,则l = m + 1;同时更新右边界的值
l = m + 1;
rightBorder = l;
}
}
return rightBorder;
}
}
- 69.x 的平方根
// 方法一:暴力破解
// 遍历x,知道找到第一个数的平方大于目标值
class Solution {
public int mySqrt(int x) {
// x = 0特殊情况
int res = 0;
// x = 1特殊情况
if (x == 1) res = 1;
for (long i = 1; i <= x / 2; i++){
long tmp = i * i;
if (tmp > x){
res = (int)i - 1;
break;
}else{
res = (int)i;
}
}
return res;
}
}
// 方法二:二分查找
// 从0到x,通过二分查找,如果某个数的平发正好等于x则直接返回
// 如果没有直接等于的,则r就是最接近的整数值
class Solution {
public int mySqrt(int x) {
int l = 0; int r = x;
while (l <= r){
long m = l + (r - l) / 2;
long tmp = m * m;
if (tmp == x){
return (int)m;
}else if (tmp > x){
r = (int) m - 1;
}else{
l = (int) m + 1;
}
}
return r;
}
}
- 367.有效的完全平方数
// 二分查找
// 从0到num,如果某个数的平发正好等于num,则返回true,否则返回false
class Solution {
public boolean isPerfectSquare(int num) {
int l = 0; int r = num;
while (l <= r){
long m = l + (r - l) / 2;
long tmp = m * m;
if (tmp == num){
return true;
}else if (tmp > num){
r = (int) m - 1;
}else{
l = (int) m + 1;
}
}
return false;
}
}