剑指offer系列——37.数字在排序数组中出现的次数/在排序数组中查找元素的第一个和最后一个位置

Q:统计一个数字在排序数组中出现的次数。
A:
1.用STL库函数解决
(1)count函数:algorithm头文件定义,其功能类似于find。这个函数使用一对迭代器和一个值做参数,返回这个值出现次数的统计结果。

public:
    int GetNumberOfK(vector<int> data ,int k) {
        return count(data.begin(),data.end(),k);
    }


(2)equal_range函数:函数equal_range()返回first和last之间等于val的元素区间.返回值是一对迭代器。此函数假定first和last区间内的元素可以使用<操作符或者指定的comp执行比较操作.

class Solution {
public:
    int GetNumberOfK(vector<int> data ,int k) {
        auto resultPair = equal_range(data.begin(), data.end(),k);
        return resultPair.second - resultPair.first;
    }
};

2.二分查找
P.S.我有一个疑问,题目提的是排序,没说升序还是降序……二分法不需要确定一下吗??还是先sort一下比较好吧。
后面就是直接找range_left和range_right就好。

public class Solution {
    public int GetNumberOfK(int [] array , int k) {
        int length = array.length;
        if(length == 0){
            return 0;
        }
        int firstK = getFirstK(array, k, 0, length-1);
        int lastK = getLastK(array, k, 0, length-1);
        if(firstK != -1 && lastK != -1){
             return lastK - firstK + 1;
        }
        return 0;
    }
    //递归写法
    private int getFirstK(int [] array , int k, int start, int end){
        if(start > end){
            return -1;
        }
        int mid = (start + end) >> 1;
        if(array[mid] > k){
            return getFirstK(array, k, start, mid-1);
        }else if (array[mid] < k){
            return getFirstK(array, k, mid+1, end);
        }else if(mid-1 >=0 && array[mid-1] == k){
            return getFirstK(array, k, start, mid-1);
        }else{
            return mid;
        }
    }
    //循环写法
    private int getLastK(int [] array , int k, int start, int end){
        int length = array.length;
        int mid = (start + end) >> 1;
        while(start <= end){
            if(array[mid] > k){
                end = mid-1;
            }else if(array[mid] < k){
                start = mid+1;
            }else if(mid+1 < length && array[mid+1] == k){
                start = mid+1;
            }else{
                return mid;
            }
            mid = (start + end) >> 1;
        }
        return -1;
    }

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

示例 1:
输入: nums = [5,7,7,8,8,10], target = 8
输出: [3,4]

示例 2:
输入: nums = [5,7,7,8,8,10], target = 6
输出: [-1,-1]

A:
分别找左和右边界。
复习二分查找。

寻找左侧边界的⼆分查找:

因为我们初始化 right = nums.length,所以决定了我们的「搜索区间」是 [left, right),所以决定了 while (left < right)
同时也决定了 left = mid + 1 (left不可取mid)和 right = mid(right可取mid)
因为我们需找到 target 的最左侧索引,所以当 nums[mid] == target 时不要立即返回,而要收紧右侧边界以锁定左侧边界,最后相等时left=right,取[left,right)。

寻找右侧边界的⼆分查找

因为我们初始化 right = nums.length,所以决定了我们的「搜索区间」是 [left, right),所以决定了 while (left < right)
同时也决定了 left = mid + 1 和 right = mid,因为我们需找到 target 的最右侧索引,所以当 nums[mid] == target 时不要⽴即返回,⽽要收紧左侧边界以锁定右侧边界
⼜因为收紧左侧边界时必须 left = mid + 1,所以最后⽆论返回 left 还是 right,必须减⼀

代码:

    public int[] searchRange(int[] nums, int target) {
        int[] result = new int[2];
        Arrays.fill(result, -1);
        if (nums.length == 0)
            return result;
        //取出来的边界是[left,right)
        int leftRound = leftRound(nums, target);
        int rightRound = rightRound(nums, target);
        if (rightRound == 0 || leftRound == nums.length)//右边界为-1或左边界为length
            return result;
        if (nums[leftRound] != target)
            return result;
        result[0] = leftRound;
        result[1] = rightRound - 1;
        return result;
    }

    private int rightRound(int[] nums, int target) {
        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
                right = mid;
        }
        return right;//右边界取不到
    }

    private int leftRound(int[] nums, int target) {
        int left = 0, 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
                right = mid;
        }
        return left;//左边界可取到
    }
posted @ 2020-02-18 16:09  Shaw_喆宇  阅读(221)  评论(0编辑  收藏  举报