zoukankan      html  css  js  c++  java
  • 剑指Offer_#53

    剑指Offer_#53 - I_在排序数组中查找数字

    Contents

    题目

    统计一个数字在排序数组中出现的次数。

    示例 1:

    输入: nums = [5,7,7,8,8,10], target = 8
    输出: 2

    示例 2:

    输入: nums = [5,7,7,8,8,10], target = 6
    输出: 0

    限制:

    0 <= 数组长度 <= 50000

    思路分析

    因为是有序数组,所以想到使用二分查找。某一个数字在有序数组中会出现多次,所以需要分别确定重复数字所在范围的左右边界。
    这里的左右边界是不在重复数字范围内的,而是在重复数字范围的向左或向右一个数字的位置。在二分查找时,不是用mid来定位。

    • 左边界是使用右指针j定位,因为最后一次循环开始时应该满足i==j==mid,之后执行j = m - 1,此时跳出循环,j刚好是重复数字范围左边第一个数字的下标。
    • 右边界同理,不同的是当nums[mid] == target时,应该右移左指针i

    解答1:循环

    class Solution {
        public int search(int[] nums, int target) {
            //前后双指针,用于不断缩小范围
            int i = 0;
            int j = nums.length - 1;
            //搜索左边界
            while(i <= j){
                int m = (i + j) / 2;
                if(nums[m] > target) j = m - 1;
                if(nums[m] < target) i = m + 1;
                if(nums[m] == target) j = m - 1;
            }
            int left = j;
            //如果数组里没有target,直接返回0
            if(i > 0 && i <nums.length && nums[i] != target) return 0;
            //搜索右边界
            i = 0;
            j = nums.length - 1;
            while(i <= j){
                int m = (i + j) / 2;
                if(nums[m] > target) j = m - 1;
                if(nums[m] < target) i = m + 1;
                //只有等于的情况不同
                if(nums[m] == target) i = m + 1;
            }
            int right = i;
            return right - left - 1;
        }
    }

    感觉这个代码并没有处理到只有一个元素的情况,虽然最后结果是对的,但是感觉好像是因为巧合正好对了,所以不是很推荐这种写法吧。

    解答2:剑指Offer写法

    这是书中的解法,感觉逻辑非常清晰,所有的情况都被“显式”的考虑到了,面试尽量用这个方法。
    递归版本:

    class Solution {
        public int search(int[] nums, int target) {
            int first = getFirstTarget(nums, target, 0, nums.length - 1);
            int last = getLastTarget(nums, target, 0, nums.length - 1);
            if(first != -1 && last != -1) return last - first + 1;
            else return 0;
            
        }
    
        private int getFirstTarget(int[] nums, int target, int start, int end){
            if(start > end) return -1;
            int mid = (start + end);
            if(nums[mid] == target){
                if((mid >= 1 && nums[mid - 1] != target) || mid == 0){
                    return mid;
                }else{
                    end = mid - 1;
                }
            }else if(nums[mid] > target){
                end = mid - 1;
            }else{
                start = mid + 1;
            }
            return getFirstTarget(nums, target, start, end);
        }
    
        private int getLastTarget(int[] nums, int target, int start, int end){
            if(start > end) return -1;
            int mid = (start + end);
            if(nums[mid] == target){
                if((mid < nums.length - 1 && nums[mid + 1] != target) || mid == nums.length - 1){
                    return mid;
                }else{
                    end = mid + 1;
                }
            }else if(nums[mid] > target){
                end = mid - 1;
            }else{
                start = mid + 1;
            }
            return getLastTarget(nums, target, start, end);
        }
    }

    循环版本

    class Solution {
        public int search(int[] nums, int target) {
            int first = getFirstTarget(nums, target, 0, nums.length - 1);
            int last = getLastTarget(nums, target, 0, nums.length - 1);
            if (first != -1 && last != -1) return last - first + 1;
            else return 0;
        }
    
        private int getFirstTarget(int[] nums, int target, int start, int end) {
            while (start <= end) {
                int mid = (start + end);
                if (nums[mid] == target) {
                    if ((mid >= 1 && nums[mid - 1] != target) || mid == 0) {
                        return mid;
                    } else {
                        end = mid - 1;
                    }
                } else if (nums[mid] > target) {
                    end = mid - 1;
                } else {
                    start = mid + 1;
                }
            }
            return -1;
        }
    
        private int getLastTarget(int[] nums, int target, int start, int end) {
            while (start <= end) {
                int mid = (start + end);
                if (nums[mid] == target) {
                    if ((mid < nums.length - 1 && nums[mid + 1] != target) || mid == nums.length - 1) {
                        return mid;
                    } else {
                        end = mid + 1;
                    }
                } else if (nums[mid] > target) {
                    end = mid - 1;
                } else {
                    start = mid + 1;
                }
            }
            return -1;
        }
    }
  • 相关阅读:
    51nod 1081 子段求和
    51nod 1085 背包问题
    51nod 1012 最小公倍数LCM
    51nod 1046 A^B Mod C
    51nod 1057 N的阶乘
    死锁 必然
    two-sum
    一些基本定义
    常用命令
    python_99_面向对象多态
  • 原文地址:https://www.cnblogs.com/Howfars/p/13355205.html
Copyright © 2011-2022 走看看