Leetcode34. Find First and Last Position of Element in Sorted Array

陌路散爱 提交于 2020-01-31 12:49:12

Leetcode34. Find First and Last Position of Element in Sorted Array

Given an array of integers nums sorted in ascending order, find thestarting and ending position of a given target value
Your algorithm’s runtime complexity must be in the order of O(log n).
If the target is not found in the array, return [-1, -1].

Example 1:

Input: nums = [5,7,7,8,8,10], target = 8
Output: [3,4]

Example 2:

Input: nums = [5,7,7,8,8,10], target = 6
Output: [-1,-1]

解法一 线性扫描

target 检查每一个下标,一定能得到正确答案。时间复杂度O(N)O(N),不符合题意。

解法二 两次二分查找

普通二分查找
int binarySearch(int[] nums, int target) {
    int left = 0; 
    int right = nums.length - 1; 

    while(left <= right) {
        int mid = (right + left) / 2;
        if(nums[mid] == target)
            return mid; 
        else if (nums[mid] < target)
            left = mid + 1; 
        else if (nums[mid] > target)
            right = mid - 1; 
        }
    return -1;
}
  • 注意①:right = nums.length - 1left <= right:搜索区间为[left,right],循环终止条件为left == right + 1,写成区间的形式就是[right + 1, right],而while(left < right)的循环终止条件为left == right
  • 注意②:left = mid + 1right = mid - 1:因为 mid 已经搜索过,应该从搜索区间中去除。
找左侧边界的二分
int left_bound(int[] nums, int target) {
    if (nums.length == 0) return -1;
    int left = 0;
    int right = nums.length; 
    
    while (left < right) { 
        int mid = (left + right) / 2;
        if (nums[mid] == target) {
            right = mid;
        } else if (nums[mid] < target) {
            left = mid + 1;
        } else if (nums[mid] > target) {
            right = mid; 
        }
    }
    // target 比所有数都大
	if (left == nums.length) return -1;
	return nums[left] == target ? left : -1;
}
  • 注意①:right = nums.length;while (left < right):搜索区间为[left,right),循环终止条件为left == right
  • 注意②:left = mid + 1;right = mid:因为是左闭右开,所以当nums[mid] < target时,遍历区间为[mid+1,right),同理当nums[mid] > target时,遍历区间为[left,mid)(因为nums[mid] != target,所以舍弃)。
  • 注意③:返回left的含义:nums[]中小于target的值有left个,即target的下标正好为nums[left]
  • 注意④:nums[mid] == target后令right = mid:首先因为循环终止条件为left == right,故即使此mid为唯一一个target,程序也可以返回正确的结果;找到 target 时,在区间 [left, mid) 中继续搜索,不断向左收缩,达到锁定左侧边界的目的。
  • 注意⑤:返回leftright是一样的因为循环终止条件为left == right
找右侧边界的二分
int right_bound(int[] nums, int target) {
    if (nums.length == 0) return -1;
    int left = 0, right = nums.length;
    
    while (left < right) {
        int mid = (left + right) / 2;
        if (nums[mid] == target) {
            left = mid + 1; 
        } else if (nums[mid] < target) {
            left = mid + 1;
        } else if (nums[mid] > target) {
            right = mid;
        }
    }
    if (left == 0) return -1;
	return nums[left-1] == target ? (left-1) : -1;
}
  • 注意①:nums[mid] == target后令left = mid + 1,找到 target 时,在区间 [mid+1, right) 中继续搜索,不断向右收缩,达到锁定右侧边界的目的。循环的结束条件是left == right
  • 注意②:返回left - 1

Java

public 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) >>> 1;
            // 小于一定不是解
            if (nums[mid] < target) {
                // 下一轮搜索区间是 [mid + 1, right]
                left = mid + 1;
            } else {
                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 + 1) >>> 1;
            // 大于一定不是解
            if (nums[mid] > target) {
                // 下一轮搜索区间是 [left, mid - 1]
                right = mid - 1;
            } else {
                left = mid;
            }
        }
        return left;
    }
}

C++

#include <iostream>
#include <vector>

using namespace std;


class Solution {
private:
    int findFitstPosition(vector<int> &nums, int target) {
        int size = nums.size();
        int left = 0;
        int right = size - 1;
        while (left < right) {
            int mid = (left + right) >> 1;
            if (nums[mid] < target) {
                left = mid + 1;
            } else {
                right = mid;
            }
        }
        if (nums[left] != target) {
            return -1;
        }
        return left;
    }

    int findLastPosition(vector<int> &nums, int target) {
        int size = nums.size();
        int left = 0;
        int right = size - 1;
        while (left < right) {
            int mid = (left + right + 1) >> 1;
            if (nums[mid] > target) {
                right = mid - 1;
            } else {
                left = mid;

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

public:
    vector<int> searchRange(vector<int> &nums, int target) {
        int size = nums.size();
        if (size == 0) {
            return {-1, -1};
        }
        int fitstPosition = findFitstPosition(nums, target);

        if (fitstPosition == -1) {
            return {-1, -1};
        }
        int lastPosition = findLastPosition(nums, target);
        return {fitstPosition, lastPosition};
    }
};

Python

from typing import List


class Solution:
    def searchRange(self, nums: List[int], target: int) -> List[int]:
        size = len(nums)
        if size == 0:
            return [-1, -1]

        first_position = self.__find_first_position(nums, size, target)
        if first_position == -1:
            return [-1, -1]
        last_position = self.__find_last_position(nums, size, target)
        return [first_position, last_position]

    def __find_first_position(self, nums, size, target):
        left = 0
        right = size - 1
        while left < right:
            mid = (left + right) >> 1
            if nums[mid] < target:
                left = mid + 1
            else:
                right = mid

        if nums[left] == target:
            return left
        else:
            return -1

    def __find_last_position(self, nums, size, target):
        left = 0
        right = size - 1
        while left < right:
            mid = (left + right + 1) >> 1
            if nums[mid] > target:
                right = mid - 1
            else:
                left = mid

        # 由于能走到这里,说明在数组中一定找得到目标元素
        # 因此这里不用再做一次判断
        return left
标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!