《算法不好玩》专题 8:二分查找(上)代码

178 阅读2分钟

正式代码

「力扣」第 374 题讲课正式代码

/** 
 * Forward declaration of guess API.
 * @param  num   your guess
 * @return 	     -1 if num is lower than the guess number
 *			      1 if num is higher than the guess number
 *               otherwise return 0
 * int guess(int num);
 */

public class Solution extends GuessGame {
    public int guessNumber(int n) {
        int left = 1;
        int right = n;
        // 在 [1..n] 中查找 pick
        while (left <= right) {
            int mid = left + (right - left) / 2;
            if (guess(mid) == 0) {
                return mid;
            } else if (guess(mid) == -1) {
                // 下一轮搜索区间 [left..mid - 1]
                right = mid - 1;
            } else {
                // guess(mid) == 1
                // 下一轮搜索区间 [mid + 1..right]
                left = mid + 1;
            }
        }
        return -1;
    }
}

「力扣」第 704 题讲课正式代码

class Solution {
    public int search(int[] nums, int target) {
        int len = nums.length;
        int left = 0;
        int right = len - 1;
        // 循环不变量:在 nums[left..right] 查找 target 的下标
        while (left <= right) {
            int mid = (left + right) / 2;
            if (nums[mid] == target) {
                return mid;
            } else if (nums[mid] > target) {
                // 下一轮搜索区间是 [left..mid - 1]
                right = mid - 1;
            } else {
                // nums[mid] < target
                // 下一轮搜索区间是 [mid + 1..right]
                left = mid + 1;
            }
        }
        return -1;
    }
}

「力扣」第 35 题讲课正式代码 1

class Solution {

    public int searchInsert(int[] nums, int target) {
        int len = nums.length;
        if (nums[len - 1] < target) {
            return len;
        }

        // 在 nums[left..right] 里查找第一个大于等于 target 的下标
        int left = 0;
        int right = len - 1;
        while (left < right) {
            int mid = (left + right) / 2;
            if (nums[mid] < target) {
                // 下一轮搜索区间是 [mid + 1..right]
                left = mid + 1;
            } else {
                // nums[mid] >= target
                // 下一轮搜索区间是 [left..mid]
                right = mid;
            }
        }
        return left;
    }
}

「力扣」第 35 题讲课正式代码 2

class Solution {

    public int searchInsert(int[] nums, int target) {
        int len = nums.length;
        // 在 nums[left..right] 里查找第一个大于等于 target 的下标
        int left = 0;
        int right = len;
        while (left < right) {
            int mid = (left + right) / 2;
            if (nums[mid] < target) {
                // 下一轮搜索区间是 [mid + 1..right]
                left = mid + 1;
            } else {
                // nums[mid] >= target
                // 下一轮搜索区间是 [left..mid]
                right = mid;
            }
        }
        return left;
    }
}

「力扣」第 69 题讲课正式代码

class Solution {

    public int mySqrt(int x) {
        if (x == 0) {
            return 0;
        }

        if (x < 4) {
            return 1;
        }

        int left = 2;
        int right = x / 2;
        // (x / 2) ^ 2 >= x => x >= 4 或者 x < -4
        // 在区间 [left..right] 查找平方以后小于等于 x 的最大整数
        while (left < right) {
            int mid = (left + right + 1) / 2;
            // System.out.println("left = " + left + ", right = " + right);
            // System.out.println("mid = " + mid);
            
            if (mid > x / mid) {
                // 下一轮搜索区间是 [left..mid - 1]
                right = mid - 1;
            } else {
                // 下一轮搜索区间是 [mid..right]
                left = mid;
            }
        }
        return left;
    }
}

「力扣」第 34 题讲课正式代码

class Solution {

    public int[] searchRange(int[] nums, int target) {
        int len = nums.length;
        if (len == 0) {
            return new int[] {-1, -1};
        }

        int firstPosition = findFirstPosition(nums, target); 
        if (firstPosition == -1) {
            return new int[] {-1, -1};
        }
        int lastPosition = findLastPosition(nums, target); 
        return new int[]{firstPosition, lastPosition};
    }

    private int findFirstPosition(int[] nums, int target) {
        int left = 0;
        int right = nums.length - 1;
        // 在 nums[left..right] 查找 target 第一次出现的位置
        while (left < right) {
            int mid = (left + right) / 2;
            if (nums[mid] < target) {
                // 下一轮搜索区间 [mid + 1..right]
                left = mid + 1;
            } else {
                // 下一轮搜索区间 [left..mid]
                right = mid;
            }
        }

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

    private int findLastPosition(int[] nums, int target) {
        int left = 0;
        int right = nums.length - 1;
        // 在 nums[left..right] 查找 target 最后一次出现的位置
        while (left < right) {
            int mid = (left + right + 1) / 2;
            if (nums[mid] <= target) {
                // 下一轮搜索区间 [mid..right]
                left = mid;
            } else {
                // nums[mid] > target
                // 下一轮搜索区间 [left..mid - 1]
                right = mid - 1;
            }
        }
        return left;
    }
}

「力扣」第 287 题讲课正式代码

class Solution {

    public int findDuplicate(int[] nums) {
        int len = nums.length; // len = n + 1, n = len - 1
        int left = 1;
        int right = len - 1;
        // 在 [left..right] 里查找重复的整数
        while (left < right) {
            int mid = (left + right) / 2;

            // nums 中小于等于 mid 的元素的个数
            int count = 0;
            for (int num: nums) {
                if (num <= mid) {
                    count++;
                }
            }

            if (count > mid) {
                // 下一轮搜索区间是 [left..mid]
                right = mid;
            } else {
                // 下一轮搜索区间是 [mid + 1..right]
                left = mid + 1;
            }
        }
        return right;
    }
}

备课代码

「力扣」第 287 题备课代码

class Solution {


    public int findDuplicate(int[] nums) {
        // n + 1 = nums.length;
        int left = 1;
        int right = nums.length - 1;

        // 例子:有 5 个整数,范围都在 [1..4] 之间,至少有一个数出现了 2 次
        while (left < right) {
            int mid = (left + right) / 2;

            int count = 0;
            // 数出 [1..mid] 之间有多少个整数
						for (int num: nums) {
                if (num <= mid) {
                    count++;
                }
            }

            if (count > mid) {
                // 下一轮搜索区间是 [left..mid]
                right = mid;
            } else {
								// 尝试分析一下:count <= mid 如果小于等于 mid 的整数个数小于等于 mid
                left = mid + 1;
            }
        }
        return left;
    }
}

「力扣」第 69 题备课代码

class Solution {

    public int mySqrt(int x) {
        if (x == 0) {
            return 0;
        }

        if (x < 4) {
            return 1;
        }

        // 计算 (x / 2) ^ 2 > x => x > 4 或者 x < -4
        // x = 4 
        int left = 1;
        int right = x / 2;
        while (left < right) {
            int mid = (left + right + 1) / 2; // left = 1, right = 2, mid = 1
            // System.out.println("left => " + left + ", right => " + right);
            // System.out.println("mid => " + mid);

            if (mid > x / mid) { // 1 > 4 / 2
                // 下一轮搜索区间是 [left..mid - 1]
                right = mid - 1;
            } else {
                // 下一轮搜索区间是 [mid..right], [1..2]
                left = mid;
            }
        }
        return left;
    }
}

「力扣」第 374 题备课代码

/** 
 * Forward declaration of guess API.
 * @param  num   your guess
 * @return 	     -1 if num is lower than the guess number
 *			      1 if num is higher than the guess number
 *               otherwise return 0
 * int guess(int num);
 */

public class Solution extends GuessGame {
    public int guessNumber(int n) {
        int left = 0;
        int right = n;

        while (left <= right) {
            int mid = left + (right - left) / 2;
            if (guess(mid) == 0) {
                return mid;
            } else if (guess(mid) == -1) {
                // 太大了,下一轮在 [left..mid - 1] 里继续猜
                right = mid - 1;
            } else {
                // 太大小,下一轮在 [mid + 1..right] 里继续猜
                left = mid + 1;
            }
        }
        return -1;
    }
}

「力扣」第 34 题备课代码

class Solution {

    public int[] searchRange(int[] nums, int target) {
        int len = nums.length;
        if (len == 0) {
            return new int[]{-1, -1};
        }

        int firstPosition = findFirstPosition(nums, target);
        if (firstPosition == -1) {
            return new int[]{-1, -1};
        }
        int lastPosition = findLastPosition(nums, target);
        return new int[]{firstPosition, lastPosition};
    }

    private int findFirstPosition(int[] nums, int target) {
        int left = 0;
        int right = nums.length - 1;

        while (left < right) {
            int mid = left + (right - left) / 2;
            if (nums[mid] < target) {
                // 应该继续向右边找,即 [mid + 1, right] 区间里找
                left = mid + 1;
            } else {
                // 此时 nums[mid] > target,应该继续向左边找,即 [left..mid] 区间里找
                right = mid;
            }
        }
        if (nums[left] == target) {
            return left;
        }
        return -1;
    }

    private int findLastPosition(int[] nums, int target) {
        int left = 0;
        int right = nums.length - 1;

        while (left < right) {
            int mid = left + (right - left + 1) / 2;
            if (nums[mid] > target) {
                // 此时 nums[mid] > target,应该继续向左边找,即 [left..mid - 1] 区间里找
                right = mid - 1;
            } else  {
                // 即 [mid..right] 区间里找
                left = mid;
            }
        }
        return left;
    }
}