二分查找之习题分析(在旋转有序的数组中搜索、在旋转有序的数组中寻找最小数值)

273 阅读1分钟

这是我参与8月更文挑战的第25天,活动详情查看:8月更文挑战

一、在旋转有序的数组中搜索

(一)、题目需求

​ 给你一个升序排列的整数数组 nums ,和一个整数 target 。

​ 假设按照升序排序的数组在预先未知的某个点上进行了旋转。(例如,数组 [0,1,2,4,5,6,7] 可能变为 [4,5,6,7,0,1,2] )。

​ 请你在数组中搜索 target ,如果数组中存在这个目标值,则返回它的索引,否则返回 -1 。

示例 1:

输入:nums = [4,5,6,7,0,1,2], target = 0

输出:4

示例 2:

输入:nums = [4,5,6,7,0,1,2], target = 3

输出:-1

示例 3:

输入:nums = [1], target = 0

输出:-1

(二)、解法

public class Solution {

    public static void main(String[] args) {
        System.out.println(Solution.search(new int[]{4, 5, 6, 7, 0, 1, 2}, 0));
    }

    public static 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;
            }
            if (nums[mid] > nums[start]) {
                if (nums[mid] >= target && target >= nums[start]) {
                    end = mid;
                } else {
                    start = mid;
                }
            } else {
                if (nums[mid] <= target && target <= nums[end]) {
                    start = mid;
                } else {
                    end = mid;
                }
            }
        }
        if (nums[start] == target) {
            return start;
        }
        if (nums[end] == target) {
            return end;
        }
        return -1;
    }
}

(三)、代码解析

1、设置开始指针、结束指针、中间指针

int start = 0;

int end = nums.length - 1;

int mid;

2、计算mid值,通过该方式计算可避免大数的情况

mid = start + (end - start) / 2;
if (nums[mid] == target) {
    return mid;
}

3、进行判断:

假如中间值大于开始值:则意味着旋转发生在中间值的后面。

情况1:目标值小于中间值,同时目标值大于开始值。所以寻找的目标位于中间值的前面,因此将结束值往前移动。

情况2:目标值大于中间值。所以寻找的目标位于中间值的后面,因此将开始值往后移动。

情况3:目标值小于中间值,同时目标值小于开始值。所以寻找的目标位于中间值的后面,因此将开始值往后移动。

假如中间值小于开始值:则意味着旋转发生在中间值的前面。

情况1:目标值大于中间值,同时目标值小于开始值。所以寻找的目标位于中间值的后面,因此将开始值往后移动。

情况2:目标值小于中间值。所以寻找的目标位于中间值的前面,因此将结束值往前移动。

情况3:目标大于中间值,同时目标值大于开始值。所以寻找的目标位于中间值的前面,因此将结束值往前移动。

if (nums[mid] > nums[start]) {
    if (nums[mid] >= target && target >= nums[start]) {
        end = mid;
    } else {
        start = mid;
    }
} else {
    if (nums[mid] <= target && target <= nums[end]) {
        start = mid;
    } else {
        end = mid;
    }
}

二、在旋转有序的数组中寻找最小数值

(一)、题目需求

​ 假设按照升序排序的数组在预先未知的某个点上进行了旋转。

​ ( 例如,数组 [0,1,2,4,5,6,7] 可能变为 [4,5,6,7,0,1,2] )。

​ 请找出其中最小的元素。

​ 你可以假设数组中不存在重复元素。

示例 1:

输入: [3,4,5,1,2]

输出: 1

示例 2:

输入: [4,5,6,7,0,1,2]

输出: 0

(二)、解法

public class Solution {
    public static void main(String[] args) {
        Solution.findMin(
                new int[]{5, 1, 2, 3, 4}
        );
    }

    public static int findMin(int[] nums) {
        if (nums == null || nums.length == 0) {
            return -1;
        }
        if (nums[0] <= nums[nums.length - 1]) {
            return nums[0];
        }
        int start = 0;
        int end = nums.length - 1;
        int mid;
        while (start + 1 < end) {
            mid = start + (end - start) / 2;
            if (nums[mid] >= nums[end]) {
                start = mid;
            } else {
                end = mid;
            }
        }
        return nums[start] <= nums[end] ? nums[start] : nums[end];
    }
}

(三)、代码解析

1、首先判断是否有序,如果第一个值小于等于最后一个值,说明无发生旋转,直接返回第一个值即可。

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

2、设置开始指针、结束指针、中间指针

int start = 0;

int end = nums.length - 1;

int mid;

3、计算中间值,同时判断中间值和结束值的大小。

情况1:中间值大于结束值,说明旋转发生在中间值后面,所以将开始值往后移动。

情况2:中间值小于结束值,说明旋转发生在中间值前面,所以将结束值往前移动。

while (start + 1 < end) {
    mid = start + (end - start) / 2;
    if (nums[mid] >= nums[end]) {
        start = mid;
    } else {
        end = mid;
    }
}