zoukankan      html  css  js  c++  java
  • 剑指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;//左边界可取到
        }
    
  • 相关阅读:
    重定向管道
    系统安全
    Linux启动流程
    压缩解压
    Vim
    ssh
    sudo
    Raid
    rsync
    quota
  • 原文地址:https://www.cnblogs.com/xym4869/p/12326634.html
Copyright © 2011-2022 走看看