二分法相关题目

704. 查找排序数组(最基本的二分查找)

给定一个 n 个元素有序的(升序)整型数组 nums 和一个目标值 target ,写一个函数搜索 nums 中的 target,如果目标值存在返回下标,否则返回 -1。

class Solution {
    /**
     * 二分法模板
     * @param arr
     * @param target
     * @return
     */
    public static int binarySearch(int[] arr, int target) {
        if (arr.length == 0) {
            return -1;
        }
        int start = 0;
        int end = arr.length - 1;
        int mid;
        // start和 end相邻的时候退出循环
        while (start + 1 < end) {
            mid = start + (end - start) / 2;
            if (arr[mid] == target) {
                return mid;
            } else if (arr[mid] < target) {
                start = mid;
            } else {
                end = mid;
            }
        }
        if (arr[start] == target) {
            return start;
        }
        if (arr[end] == target) {
            return end;
        }
        return -1;
    }

    /**
     * 递归查找
     * @param nums
     * @param target
     * @return
     */
    public int search(int[] nums, int target) {
        return recursionSearch(nums, 0, nums.length - 1, target);
    }

    public int recursionSearch(int[] nums, int l, int r, int target) {
        if (l > r) {
            return -1;
        }
        int mid = l + (r - l) / 2;
        if (nums[mid] == target) {
            return mid;
        } else if (nums[mid] > target) {
            return recursionSearch(nums, l, mid - 1, target);
        } else {
            return recursionSearch(nums, mid + 1, r, target);
        }
    }
}

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

给定一个按照升序排列的整数数组 nums,和一个目标值 target。找出给定目标值在数组中的开始位置和结束位置。你的算法时间复杂度必须是 O(log n) 级别。如果数组中不存在目标值,返回 [-1, -1]。

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

    }

    public int findFirst(int[] arr, int target) {
        if (arr.length == 0) {
            return -1;
        }
        int start = 0;
        int end = arr.length - 1;
        int mid;
        while (start + 1 < end) {
            mid = start + (end - start) / 2;
            if (arr[mid] == target) {
                //1、如果是任意位置,在此返回
                //return mid;
                //2、如果是找第一个
                end = mid;
                //3、如果是找最后一个
                //start = mid;
            } else if (arr[mid] < target) {
                start = mid;
            } else {
                end = mid;
            }
        }
        /*first position这个判断在前*/
        if (arr[start] == target) {
            return start;
        }
        /*last position这个判断在前*/
        if (arr[end] == target) {
            return end;
        }
        return -1;
    }

    public int findLast(int[] arr, int target) {
        if (arr.length == 0) {
            return -1;
        }
        int start = 0;
        int end = arr.length - 1;
        int mid;
        while (start + 1 < end) {
            mid = start + (end - start) / 2;
            if (arr[mid] == target) {
                //1、如果是任意位置,在此返回
                //return mid;
                //2、如果是找第一个
                // end = mid;
                //3、如果是找最后一个
                start = mid;
            } else if (arr[mid] < target) {
                start = mid;
            } else {
                end = mid;
            }
        }

        /*last position这个判断在前*/
        if (arr[end] == target) {
            return end;
        }
        /*first position这个判断在前*/
        if (arr[start] == target) {
            return start;
        }
        return -1;
    }
}

35. 搜索插入位置

给定一个排序数组和一个目标值,在数组中找到目标值,并返回其索引。如果目标值不存在于数组中,返回它将会被按顺序插入的位置。你可以假设数组中无重复元素。

/**
 * 输入: [1,3,5,6], 5
 * 输出: 2
 *
 * 输入: [1,3,5,6], 2
 * 输出: 1
 *
 * 输入: [1,3,5,6], 0
 * 输出: 0
 *
 * 在数组中找到一个数,当这个数 >= target 时,此位置就是插入的位置
 * 如果数组中有重复的,根据情况看放在整体相等元素的前面,还是后面
 */
class Solution {
    public static void main(String[] args) {
        int[] arr = {1, 2, 3, 3, 4, 5};
        System.out.println(searchInsert(arr, 3));
    }
    public static int searchInsert(int[] nums, int target) {
        if (nums.length == 0) {
            return -1;
        }
        int start = 0;
        int end = nums.length - 1;
        int mid;
        while (start + 1 < end) {
            mid = start + (end - start) / 2;
            if (nums[mid] == target) {
                // 数组中无重复元素,找到直接返回
                return mid;
                // 如果有重复的,放到重复元素的前面就end = mid,后面就start = mid
                // start = mid;
                // end = mid;
            } else if (nums[mid] < target) {
                start = mid;
            } else {
                end = mid;
            }
        }
        // 跳出循环没有返回,则说明这个数可能在 start或者 end位置上
        // 如果有重复的,放在重复的前面先判断start,放在后面先判断end
        if (nums[start] >= target) {
            return start;
        } else if (nums[end] >= target) {
            return end;
        } else {
            // 都没有>=target的,则target比所有数都大,插入到末尾
            return end + 1;
        }
    }
}

36. 第一个错误版本

public class Solution extends VersionControl {
    public int firstBadVersion(int n) {
        int start = 1;
        int end = n;
        while (start + 1 < end) {
            int mid = start + (end - start) / 2;
            if (isBadVersion(mid)) {
                // 如果当前版本是错误的,end = mid
                end = mid;
            } else {
                // 如果当前版本是好的,start = mid
                start = mid;
            }
        }
        // 跳出循环,start和end相邻,判断两个中哪一个是错误的
        if (isBadVersion(start)) {
            return start;
        } else {
            return end;
        }
    }
}

class VersionControl {
    /**
     * 判断当前版本是否出错
     * @param version
     * @return
     */
    public boolean isBadVersion(Integer version) {
        return true;
    }
}

162. 寻找峰值

峰值元素是指其值大于左右相邻值的元素。

class Solution {
    /**
     * 峰值元素是指其值大于左右相邻值的元素。
     * @param nums
     * @return
     */
    public int findPeakElement(int[] nums) {
        int start = 0;
        int end = nums.length - 1;
        while(start + 1 < end){
            int mid = start + (end - start)/2;
            // 如果当前数小于前面一个数,则当前数左边存在峰值
            if(nums[mid] < nums[mid - 1]){
                end = mid;
            } else {
                // 否则右边存在峰值
                start = mid;
            }
        }
        if(nums[start] < nums[end]){
            return end;
        } else {
            return start;
        }
    }
}

33. 搜索旋转排序数组

/**
 * 数组 [0,1,2,4,5,6,7] 可能变为 [4,5,6,7,0,1,2]
 * 搜索一个给定的目标值,如果数组中存在这个目标值,则返回它的索引,否则返回 -1 。
 * 你可以假设数组中不存在重复的元素。
 * 你的算法时间复杂度必须是 O(logN) 级别。
 */

class Solution {
    public int search(int[] nums, int target) {
        if (nums == null || nums.length == 0) {
            return -1;
        }
        int start = 0;
        int end = nums.length - 1;
        int mid;
        while (start + 1 < end) {
            mid = start + (end - start) / 2;
            if (nums[mid] == target) {
                return mid;
            }
            // mid被分到旋转的部分
            if (nums[start] < nums[mid]) {
                // target在start和mid之间
                if (nums[start] <= target && target <= nums[mid]) {
                    end = mid;
                } else {
                    // target在mid右边
                    start = mid;
                }
            } else { // mid被分到为旋转的部分
                // target在mid到end之间
                if (nums[mid] <= target && target <= nums[end]) {
                    start = mid;
                } else {
                    // target在mid左边
                    end = mid;
                }
            }
        }
        if (nums[start] == target) {
            return start;
        }
        if (nums[end] == target) {
            return end;
        }
        return -1;
    }
}

153. 搜索旋转排序数组中的最小值(无重复元素)

class Solution {
    /**
     * @param nums: a rotated sorted array
     * @return: the minimum number in the array
     */
    public int findMin(int[] nums) {
        if (nums == null || nums.length == 0) {
            return -1;
        }
        int start = 0, end = nums.length - 1;
        int target = nums[nums.length - 1];
        // find the first element <= target
        while (start + 1 < end) {
            int mid = start + (end - start) / 2;
            //如果mid位置上的数字小于等于最右端的数字时,区间向左移动
            if (nums[mid] <= target) {
                end = mid;
            } else {
                // 区间右移
                start = mid;
            }
        }
        //最终返回start和end位置上较小的数字即可
        return Math.min(nums[start], nums[end]);
    }
}

class Solution {
    public static int findMin(int[] nums) {
        if(nums.length == 1){
            return nums[0];
        }
        int l = 0;
        int r = nums.length - 1;
        while(l < r){
            // 中间节点的值
            int mid = l + (r - l) / 2;
            // 中间位置的值小,则最小值出现在[0 ~ mid]
            if(nums[mid] < nums[r]) r = mid;
            // mid的值大于r 则最小值出现在[mid+1 ~ r]
            else l = mid + 1;
        }
        return nums[l];
    }
}

154. 搜索旋转排序数组中的最小值 (有重复元素)

数组中有最小值,其实这种情况是考察如果有重复元素的最差情况下的时间复杂度

public class Solution {
    public int findMin(int[] num) {
        //  这道题目在面试中不会让写完整的程序
        //  只需要知道最坏情况下 [1,1,1....,1] 里有一个0
        //  这种情况使得时间复杂度必须是 O(n)
        //  因此写一个for循环就好了。
        //  如果你觉得,不是每个情况都是最坏情况,你想用二分法解决不是最坏情况的情况,那你就写一个二分吧。
        //  反正面试考的不是你在这个题上会不会用二分法。这个题的考点是你想不想得到最坏情况。
        int min = num[0];
        for (int i = 1; i < num.length; i++) {
            if (num[i] < min)
                min = num[i];
        }
        return min;
    }
}

public class Solution {
    /**
     * @param num: a rotated sorted array
     * @return: the minimum number in the array
     */
    public int findMin(int[] nums) {
        if (nums == null || nums.length == 0) {
            return -1;
        }
        int start = 0, end = nums.length - 1;
        while (start + 1 < end) {
            int mid = start + (end - start) / 2;
            if (nums[mid] == nums[end]) {
                // if mid equals to end, that means it's fine to remove end
                // the smallest element won't be removed
                end--;
            } else if (nums[mid] < nums[end]) {
                end = mid;
            } else {
                start = mid;
            }
        }
        if (nums[start] <= nums[end]) {
            return nums[start];
        }
        return nums[end];
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值