二分搜索总结

这篇博客详细介绍了二分搜索的三种模板,包括基本的二分查找、lowerBound和upperBound,并列举了多个与旋转数组相关的LeetCode题目,如寻找旋转排序数组中的最小值、搜索旋转排序数组等,同时提供了相关题解的链接供参考。
摘要由CSDN通过智能技术生成

1.模板一:

boolean binarySearch(long[] A, long target) {

  if (A == null || A.length == 0) {

    return false;

  }

  // 首先设定初始区间,这里我们采用开闭原则[l, r)

  int l = 0, r = A.length;

  // 循环结束的判断条件是整个区间为空区间,那么运行结束。

  // 我们使用的是开闭原则来表示一个区间,所以当l < r的时候

  // 我们要查找的区间还不是一个空区间。

  while (l < r) {

    final int m = l + ((r - l) >> 1);

    if (A[m] == target) {

      return true;

    }

    if (A[m] < target) {

      // 当中点比目标值小时,需要把左边的部分扔掉。即[l, m]

      // 这个区间扔掉,由于我们采用的是开闭原则,所以新的区间需要是

      // [m + 1, r), 因引需要将l = m + 1

      l = m + 1;

    } else {

      // 当中点比目标值大时,需要把右边的部分扔掉,即[m, r)这个区间扔掉。

      // 那么新区间变成[l, m)。由于我们使用的是开闭原则,

      // 只需要设置r = m即可。

      r = m;

    }

  }

  return false;

}

题目1

704. 二分查找
代码展示:

class Solution {
    public int search(int[] nums, int target) {
        return binarySearch(nums, target);
    }

    private int binarySearch(int[] nums, int target) {
        int left = 0;
        int right = nums.length;
        while (left < right) {
            final int mid = left + ((right - left) >> 1);
            if (nums[mid] == target) {
                return mid;
            } else if (nums[mid] < target) {
                left = mid + 1;
            } else {
                right = mid;
            }
        }

        return -1;
    }
}

2.模板二:lowerBound(找最左边的数据)

int lowerBound(long[] A, int n, long target) {

  int l = 0, r = n;

  while (l < r) {

    final int m = l + ((r - l) >> 1);

    if (A[m] < target) {

      l = m + 1;

    } else {

      r = m;

    }

  }

  return l;

}

3.模板三:upperBound(找最右边的数据)

int upperBound(long[] A, int n, long target) {

  int l = 0, r = n;

  while (l < r) {

    final int m = l + ((r - l) >> 1);

    if (A[m] <= target) {

      l = m + 1;

    } else {

      r = m;

    }

  }

  return l;

}

题目2

34. 在排序数组中查找元素的第一个和最后一个位置

class Solution {
    public int[] searchRange(int[] nums, int target) {
        int[] res = new int[2];
        res[0] = lowerBinarySearch(nums, target);
        res[1] = upperBinarySearch(nums, target);

        return res;
    }

    private int lowerBinarySearch(int[] nums, int target) {

        if (null == nums || 0 == nums.length) {
            return -1;
        }

        int left = 0;
        int right = nums.length;
        while (left < right) {
            final int mid = left + ((right - left) >> 1);
            if (nums[mid] < target) {
                left = mid + 1;
            } else {
                right = mid;
            }
        }

        if (left == nums.length) {
            return -1;
        }
        
        return nums[left] == target ? left : -1;
    }

    private int upperBinarySearch(int[] nums, int target) {
        
        if (null == nums || 0 == nums.length) {
            return -1;
        }

        int left = 0;
        int right = nums.length;

        while (left < right) {
            final int mid = left + ((right - left) >> 1);
            if (nums[mid] <= target) {
                left = mid + 1;
            } else {
                right = mid;
            }
        }

        if (0 == left) {
            return -1;
        }

        return nums[left - 1] == target ? left - 1 : -1;

    }
}

题目2

35. 搜索插入位置

class Solution {
    public int searchInsert(int[] nums, int target) {
        return binarySearch(nums, target);
    }

    private int binarySearch(int[] nums, int target) {
        
        int left = 0;
        int right = nums.length;
        while (left < right) {
            final int mid = left + ((right - left) >> 1);
            if (nums[mid] == target) {
                return mid;
            } else if (nums[mid] < target) {
                left = mid + 1;
            } else {
                right = mid;
            }
        }

        return left;
    }
}

题目3

852. 山脉数组的峰顶索引

class Solution {
    public int peakIndexInMountainArray(int[] arr) {
        return binarySearch(arr);
    }

    private int binarySearch(int[] arr) {
        int left = 0;
        int right = arr.length;
        while (left < right) {
            final int mid = left + ((right - left) >> 1);
            if (arr[mid] > arr[mid - 1] && arr[mid] > arr[mid + 1]) {
                return mid;
            } else if (arr[mid] > arr[mid - 1] && arr[mid] < arr[mid + 1]) {
                left = mid + 1;
            } else {
                right = mid;
            }
        }

        return -1;
    }
}

题目4

33. 搜索旋转排序数组

class Solution {
    public int search(int[] nums, int target) {
        return binarySearch(nums, target);
    }

    private int binarySearch(int[] nums, int target) {
       int left = 0;
       int right = nums.length;
       while (left < right) {

           final int mid = left + ((right - left) >> 1);

           if (nums[left] == target) {
               return left;
           }

           if (nums[right - 1] == target) {
               return right - 1;
           }

           if (nums[mid] == target) {
               return mid;
           }

           if (nums[mid] > nums[0]) {
               if (target > nums[left] && target < nums[mid]) {
                   right = mid;
               }  else {
                   left = mid + 1;
               }
           } else {
               if (target > nums[mid] && target < nums[nums.length - 1]) {
                   left = mid + 1;
               } else {
                   right = mid;
               }
           }
       }

       return -1;
    }
}
相关题目

69. Sqrt(x)

class Solution {
    public int mySqrt(int x) {
        return binarySearch(x);
    }

    private int binarySearch(int x) {
        if (0 == x || 1 == x) {
            return x;
        }
        long left = 0;
        long right = x;
        while (left < right) {
            long mid = left + ((right - left) >> 1);
            if (mid * mid == x) {
                return (int)mid;
            }else if (mid * mid > x) {
                right = mid;
            } else {
                left = mid + 1;
            }
        }

        return (int)(left - 1);
    }
}

374. 猜数字大小

public class Solution extends GuessGame {
    public int guessNumber(int n) {
        return binarySearch(n);
    }

    private int binarySearch(int n) {
        int left = 0;
        int right = n;
        while (left < right) {
            final int mid = left + ((right - left) >> 1);
            if (guess(mid + 1) == 0) {
                return mid + 1;
            } else if (guess(mid + 1) == 1) {
                left = mid + 1;
            } else {
                right = mid;
            }
        }

        return -1;
    }
}

278. 第一个错误的版本

public class Solution extends VersionControl {
    public int firstBadVersion(int n) {
        return lowerBoundBinarySearch(n);
    }

    private int lowerBoundBinarySearch(int n) {
        int left = 0;
        int right = n;
        while (left < right) {
            final int mid = left + ((right - left) >> 1);
            if (isBadVersion(mid + 1)) {
                right = mid;
            } else {
                left = mid + 1;
            }
        }

        return left + 1;
    }
}

162. 寻找峰值

class Solution {
    public int findPeakElement(int[] nums) {
        return binarySearch(nums);
    }

    private int binarySearch(int[] nums) {
        if (1 == nums.length) {
            return 0;
        }

        if (2 == nums.length) {
            return nums[0] > nums[1] ? 0 : 1;
        }
        
        int left = 0;
        int right = nums.length;
        while (left < right) {
            final int mid = left + ((right - left) >> 1);
            if ((mid == 0 || nums[mid] > nums[mid - 1]) && (mid == nums.length - 1 || nums[mid] > nums[mid + 1])) {
                return mid;
            } else if (nums[mid] > nums[mid - 1] && nums[mid] < nums[mid + 1]) {
                left = mid + 1;
            } else {
                right = mid;
            }
        }

        return -1;
    }
}

153. 寻找旋转排序数组中的最小值

class Solution {
    public int findMin(int[] nums) {
        int left = 0;
        int right = nums.length - 1;
        while (left < right) {
            int mid = left + (right - left) / 2;
            if (nums[mid] > nums[right]) {          
                left = mid + 1;
            } else {                                
                right = mid;
            }
        }
        return nums[left];
    }
}

658. 找到 K 个最接近的元素

class Solution {
    public List<Integer> findClosestElements(int[] arr, int k, int x) {
        int left = 0;
        int right = arr.length - k;
        while (left < right) {
            final int mid = left + ((right - left) >> 1);
            if (x - arr[mid] > arr[mid + k] - x) {
                left = mid + 1;
            } else {
                right = mid;
            }
        }

        List<Integer> res = new ArrayList<>();
        for (int i = left; i < left + k; i ++) {
            res.add(arr[i]);
        }

        return res;
    }
}

162. 寻找峰值

class Solution {
    public int findPeakElement(int[] nums) {
        return binarySearch(nums);
    }

    private int binarySearch(int[] nums) {
        if (1 == nums.length) {
            return 0;
        }

        if (2 == nums.length) {
            return nums[0] > nums[1] ? 0 : 1;
        }
        
        int left = 0;
        int right = nums.length;
        while (left < right) {
            final int mid = left + ((right - left) >> 1);
            if ((mid == 0 || nums[mid] > nums[mid - 1]) && (mid == nums.length - 1 || nums[mid] > nums[mid + 1])) {
                return mid;
            } else if (nums[mid] > nums[mid - 1] && nums[mid] < nums[mid + 1]) {
                left = mid + 1;
            } else {
                right = mid;
            }
        }

        return -1;
    }
}

270. 最接近的二叉搜索树值

class Solution {
    public int closestValue(TreeNode root, double target) {
        List<Integer> res = new ArrayList<>();
        midOrder(root, res);
        int[] arr = listToArr(res);
        return binarySearch(arr, target);
    }

    private int binarySearch(int[] arr, double target) {

        int left = 0;
        int right = arr.length;
        double grap = Integer.MAX_VALUE;
        while (left < right) {
            final int mid = left + ((right - left) >> 1);
            double midL = (double)arr[mid];
            if (midL == target) {
                return arr[mid];
            } else if (midL < target) {
                left = mid + 1;
                grap = Math.abs(grap) < Math.abs(midL - target) ? grap : (midL - target);
            } else {
                right = mid;
                grap = Math.abs(grap) < Math.abs(midL - target) ? grap : (midL - target);
            }
        }

        return (int)(grap + target);

    }

    private int[] listToArr(List<Integer> res) {

        if (null == res || 0 == res.size()) {
            return new int[0];
        }

        int[] arr = new int[res.size()];
        int index = 0;
        for (Integer item : res) {
            arr[index ++] = item;
        }

        return arr;
    }

    private void midOrder(TreeNode root, List<Integer> res) {

        if (null == root) {
            return;
        }

        midOrder(root.left, res);
        res.add(root.val);
        midOrder(root.right, res);

    }
}

702. 搜索长度未知的有序数组

class Solution {
    public int search(ArrayReader reader, int target) {
        int arrSize = 10000 * 2;
        while (reader.get(arrSize) == 2147483647) {
            arrSize --;
        }

        return binarySearch(reader, arrSize, target);
    }

    private int binarySearch(ArrayReader reader, int arrSize, int target) {
        int left = 0;
        int right = arrSize + 1;
        while (left < right) {
            final int mid = left + ((right - left) >> 1);
            if (reader.get(mid) == target) {
                return mid;
            } else if (reader.get(mid) < target) {
                left = mid + 1;
            } else {
                right = mid;
            }
        }

        return -1;
    }
}

367. 有效的完全平方数

class Solution {
    public boolean isPerfectSquare(int num) {
        return binarySearch((long)num, (long)num);
    }

    private boolean binarySearch(long num, long target) {
        long left = 0;
        long right = num + 1;
        while (left < right) {
            final long mid = (long)(left + ((right - left) >> 1));
            if (mid * mid == target) {
                return true;
            } else if (mid * mid < target) {
                left = mid + 1;
            } else {
                right = mid;
            }
        }

        return false;
    }
}

744. 寻找比目标字母大的最小字母

class Solution {
    public char nextGreatestLetter(char[] letters, char target) {
        return binarySearch(letters, target);
    }

    private char binarySearch(char[] letters, char target) {
        int left = 0;
        int right = letters.length;
        while (left < right) {
            final int mid = left + ((right - left) >> 1);
            if ((int)letters[mid] <= (int)target) {
                left = mid + 1;
            } else {
                right = mid;
            }
        }
        
        return left == letters.length ? letters[0] : letters[left];
    }
}

153. 寻找旋转排序数组中的最小值

class Solution {
    public int findMin(int[] nums) {
        return binarySearch(nums);
    }

    private int binarySearch(int[] nums) {

        if (nums[0] < nums[nums.length - 1]) {
            return nums[0];
        }

        int target = nums[nums.length - 1];
        int left = 0;
        int right = nums.length;
        int minValue = target;
        while (left < right) {
            final int mid = (left + ((right - left) >> 1));
            if (nums[mid] > target) {
                left = mid + 1;
            } else {
                right = mid;
                minValue = nums[mid];
            }
        }

        return minValue;
    }
}

154. 寻找旋转排序数组中的最小值 II

class Solution {
    public int findMin(int[] nums) {
        return binarySearch(nums);
    }

    private int binarySearch(int[] nums) {

        int n = nums.length;

        if (nums[0] < nums[n - 1]) {
            return nums[0];
        }

        int left = 0;
        while (left < n - 1 && nums[left] == nums[left + 1]) {
            left ++;
        }
        int right = n;
        int minValue = nums[n - 1];
        while (left < right) {

            final int mid = left + ((right - left) >> 1);
            if (nums[mid] > minValue) {
                left = mid + 1;
            } else {
                right = mid;
                minValue = minValue > nums[mid] ? nums[mid] : minValue;
            }
        }

        return minValue;
    }
}

4. 寻找两个正序数组的中位数

class Solution {
    public double findMedianSortedArrays(int[] nums1, int[] nums2) {

        if (nums1.length > nums2.length) {
            int[] temp = nums1;
            nums1 = nums2;
            nums2 = temp;
        }

        int m = nums1.length;
        int n = nums2.length;

        int totalLeft = (m + n + 1) / 2;

        int left = 0;
        int right = m;

        while (left < right) {
            int i = (left + right + 1) / 2;
            int j = totalLeft - i;

            if (nums1[i - 1] > nums2[j]) {
                right = i - 1;
            } else {
                left = i;
            }
        }

        int i = left;
        int j = totalLeft - i;

        int nums1LeftMax = i == 0 ? Integer.MIN_VALUE : nums1[i - 1];
        int nums1RightMin = i == m ? Integer.MAX_VALUE : nums1[i];
        int nums2LeftMax = j == 0 ? Integer.MIN_VALUE : nums2[j - 1];
        int nums2RightMin = j == n ? Integer.MAX_VALUE : nums2[j];

        if ((m + n) % 2 == 1) {
            return Math.max(nums1LeftMax, nums2LeftMax);
        } else {
            return (Math.max(nums1LeftMax, nums2LeftMax) + Math.min(nums1RightMin, nums2RightMin)) / 2.0;
        }
    }
}

参考题解:
https://leetcode-cn.com/problems/median-of-two-sorted-arrays/solution/he-bing-yi-hou-zhao-gui-bing-g

旋转数组篇

参考:https://leetcode-cn.com/problems/search-rotate-array-lcci/solution/xuan-zhuan-shu-zu-cong-yi-dao-nan-ge-ge-dcv7a/

189. 轮转数组

class Solution {
    public void rotate(int[] nums, int k) {

        if (null == nums || nums.length < 2) {
            return ;
        }

        int len = nums.length;

        // 数组旋转len次变回原来的数组
        if (len <= k) {
            k %= len;
        }

        reserveArr(nums, 0, len - 1);
        reserveArr(nums, 0, k - 1);
        reserveArr(nums, k, len - 1);
    }

    private void reserveArr(int[] nums, int left, int right) {

        while (left < right) {
            int temp = nums[left];
            nums[left] = nums[right];
            nums[right] = temp;
            left ++;
            right --;
        }

    }
}

153. 寻找旋转排序数组中的最小值

class Solution {
    public int findMin(int[] nums) {
        return binarySearch(nums);
    }

    private int binarySearch(int[] nums) {

        if (nums[0] < nums[nums.length - 1]) {
            return nums[0];
        }

        int target = nums[nums.length - 1];
        int left = 0;
        int right = nums.length;
        int minValue = target;
        while (left < right) {
            final int mid = (left + ((right - left) >> 1));
            if (nums[mid] > target) {
                left = mid + 1;
            } else {
                right = mid;
                minValue = nums[mid];
            }
        }

        return minValue;
    }
}

154. 寻找旋转排序数组中的最小值 II

class Solution {
    public int findMin(int[] nums) {
        return binarySearch(nums);
    }

    private int binarySearch(int[] nums) {

        int n = nums.length;

        if (nums[0] < nums[n - 1]) {
            return nums[0];
        }

        int left = 0;
        while (left < n - 1 && nums[left] == nums[left + 1]) {
            left ++;
        }
        int right = n;
        int minValue = nums[n - 1];
        while (left < right) {

            final int mid = left + ((right - left) >> 1);
            if (nums[mid] > minValue) {
                left = mid + 1;
            } else {
                right = mid;
                minValue = minValue > nums[mid] ? nums[mid] : minValue;
            }
        }

        return minValue;
    }
}

33. 搜索旋转排序数组

class Solution {
    public int search(int[] nums, int target) {
        return binarySearch(nums, target);
    }

    private int binarySearch(int[] nums, int target) {
       int left = 0;
       int right = nums.length;
       while (left < right) {

           final int mid = left + ((right - left) >> 1);

           if (nums[left] == target) {
               return left;
           }

           if (nums[right - 1] == target) {
               return right - 1;
           }

           if (nums[mid] == target) {
               return mid;
           }

           if (nums[mid] > nums[0]) {
               if (target > nums[left] && target < nums[mid]) {
                   right = mid;
               }  else {
                   left = mid + 1;
               }
           } else {
               if (target > nums[mid] && target < nums[nums.length - 1]) {
                   left = mid + 1;
               } else {
                   right = mid;
               }
           }
       }

       return -1;
    }
}

81. 搜索旋转排序数组 II

class Solution {
    public boolean search(int[] nums, int target) {
        return binarySearch(nums, target);
    }

    private boolean binarySearch(int[] nums, int target) {
        int left = 0;
        int right = nums.length;
        while (left < right) {

            final int mid = left + ((right - left) >> 1);

            if (nums[left] == target) {
                return true;
            }
            if (nums[right - 1] == target) {
                return true;
            }
            if (nums[mid] == target) {
                return true;
            }

            if (nums[left] == nums[mid]) {
                left ++;
                continue;
            }

            if (nums[mid] > nums[0]) {
                if (target > nums[left] && target < nums[mid]) {
                    right = mid;
                } else {
                    left = mid + 1;
                }
            } else {
                if (target < nums[nums.length - 1] && target > nums[mid]) {
                    left = mid + 1;
                } else {
                    right = mid;
                }
            }
        }

        return false;
    }
}

面试题 10.03. 搜索旋转数组

class Solution {
    public int search(int[] arr, int target) {
        return binarySearch(arr, target);
    }

    private int binarySearch(int[] arr, int target) {
        int left = 0;
        int right = arr.length;
        while (left < right) {
            int mid = left + ((right - left) >> 1);

            while (arr[left] == target) {
                if (left > 0 && arr[left - 1] == arr[left]) {
                    left --;
                } else {
                    return left;
                }
            }

            while (arr[right - 1] == target) {
                if (right > 1 && arr[right - 2] == arr[right - 1]) {
                    right --;
                } else {
                    return right - 1;
                }
                
            }

            while (arr[mid] == target) {
                if (mid > 0 && arr[mid - 1] == arr[mid]) {
                    mid --;
                } else {
                    return mid;
                }
            }

            if (arr[left] == arr[mid]) {
                left ++;
                continue;
            }

            if (arr[mid] > arr[0]) {
                if (target > arr[left] && target < arr[mid]) {
                    right = mid;
                } else {
                    left = mid + 1;
                }
            } else {
                if (target > arr[mid] && target < arr[arr.length - 1]) {
                    left = mid + 1;
                } else {
                    right = mid;
                }
            }
        }

        return -1;
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值