34. 在排序数组中查找元素的第一个和最后一个位置[中等] | 刷题打卡

145 阅读2分钟

问题

给定一个按照升序排列的整数数组 nums,和一个目标值 target。找出给定目标值在数组中的开始位置和结束位置。

如果数组中不存在目标值 target,返回 [-1, -1]。

思路

基本思路是用二分查找。当找第一个时,如果nums[mid]恰好为target,需要判断mid的左边的数字是否是target,如果是可以往左找;否则直接返回。找最后一个时,同理可得。

代码

class Solution {
    public int[] searchRange(int[] nums, int target) {
        int low = findLowIndex(nums, target);
        int high = findHighIndex(nums, target);
        return new int[] {low, high};
    }
​
    private int findLowIndex(int[] nums, int target) {
        if (nums == null || nums.length == 0) {
            return -1;
        }
        int low = 0;
        int high = nums.length - 1;
        
        while (low <= high) {
            int mid = (low + high) / 2;
            if (nums[mid] == target) {
                // mid-1后不要超过low
                if (mid - 1 >= low && nums[mid - 1] == target ) {
                    high = mid - 1;
                } else {
                    return mid;
                }
            }
            if (nums[mid] > target) {
                high = mid - 1;
            } 
            if (nums[mid] < target) {
                low = mid + 1;
            }
        }
        return -1;
    }
​
    private int findHighIndex(int[] nums, int target) {
        if (nums == null || nums.length == 0) {
            return -1;
        }
        int low = 0;
        int high = nums.length - 1;
        
        while (low <= high) {
            int mid = (low + high) / 2;
            if (nums[mid] == target) {
                if (mid + 1 <= high && nums[mid + 1] == target) {
                    low = mid + 1;
                } else {
                    return mid;
                }
            }
            if (nums[mid] > target) {
                high = mid - 1;
            } 
            if (nums[mid] < target) {
                low = mid + 1;
            }
        }
        return -1;
    }
}

代码二

public class Solution {
    public int[] searchRange(int[] nums, int target) {
        int[] result = {-1, -1};
        int start = 0; 
        int end = nums.length - 1;
        int mid = 0;
        while (start <= end) {
            mid = start + ((end - start) >>> 1);
            //target < nums[mid]肯定往左找
            //找下确界,即使相等也要往左找
            if (target <= nums[mid]) {
                end = mid - 1;
            } else {
                //target >= nums[mid]
                start = mid + 1;
            }
        }
        if (start < nums.length && nums[start] == target) {
            result[0] = start;
            end = nums.length - 1;
            while (start <= end) {
                mid = start + ((end - start) >>> 1);
                if (target < nums[mid]) {
                    end = mid - 1;
                } else {
                    start = mid + 1;
                }
            }
            result[1] = end;
            return result;
        }
       
        return result;
    }
}

官方

官方给的思路:相当于查找第一个大于等于target的下标,和第一个大于​target的下标-1。

如果查找第一个大于等于target的下标,只需要在原有二分法中,当大于等于就往左走​。

class Solution {
    public int[] searchRange(int[] nums, int target) {
        int left = binarySearch(nums, target, true);
        int right = binarySearch(nums, target, false) - 1;
        if (left <= right && nums[left] == target && nums[right] >= target) {
            return new int[] {left, right};
        }
        return new int[] {-1, -1};
    }
​
    // 第一个大于target或者第一个大于等于target的下标
    private int binarySearch(int[] nums, int target, boolean left) {
        int low = 0, high = nums.length - 1;
        // 这一步的赋值很关键,不能赋值为-1。
        // 有可能nums数字是,1,2,3,4,4,4   target是4   当找上确界时res是无法在循环体中赋值的。
        int res = nums.length;
        while (low <= high) {
            int mid = (low + high) / 2;
            if (nums[mid] > target || (left && nums[mid] >= target)) {
                high = mid - 1;
                res = mid;
            } else {
                low = mid + 1;
            }
        }
        return res;
    }
}

复杂度

时间复杂度:O(logn) 空间复杂度:O(1)

update20210325

class Solution {
    public int[] searchRange(int[] nums, int target) {
        int[] res = new int[] {-1, -1};
        int low = 0;
        int high = nums.length - 1;
        int mid;
        while (low <= high) {
            mid = (low + high) / 2;
            if (nums[mid] == target) {
                int i = mid -1;
                while (i >= 0 && nums[i] == target) {
                    i--;
                }
                res[0] = i+1;
                i = mid + 1;
                while (i < nums.length && nums[i] == target) {
                    i++;
                }
                res[1] = i - 1;
                return res;
            } else if (nums[mid] < target) {
                low = mid + 1;
            } else {
                high = mid - 1;
            }
        }

        return res;
    }
}

update20220106

public int[] searchRange(int[] nums, int target) {
    // 分两步:最小的下标和最大的小标
    int min = findMinIndex(nums, target);
    int max = findMaxIndex(nums, target);
    return new int[] {min, max};
}

public int findMinIndex(int[] nums, int target) {
    int start = 0, end = nums.length - 1;
    int res = -1;
    while (start <= end) {
        int mid = (start + end) / 2;
        if (nums[mid] == target) {
            res = mid;
            end = mid -1;
        } else if (nums[mid] > target) {
            end = mid - 1;
        } else {
            start = mid + 1;
        }
    }
    return res;
}

public int findMaxIndex(int[] nums, int target) {
    int start = 0, end = nums.length - 1;
    int res = -1;
    while (start <= end) {
        int mid = (start + end) / 2;
        if (nums[mid] == target) {
            res = mid;
            start = mid + 1;
        } else if (nums[mid] < target) {
            start = mid + 1;
        } else {
            end = mid - 1;
        }
    }
    return res;
}

硬广告

欢迎关注公众号:double6

final

本文正在参与「掘金 2021 春招闯关活动」, 点击查看 活动详情