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

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

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

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


题目很简单, 做起来也确实容易, 但是面试官肯定不是想要暴力法的, 而且思路很多, 要是能说出来几种不同的思路, 绝对会让人眼前一亮的.

1. 暴力法, 从开始遍历, 如果相等, 就把result+=1, 直到结束.  最原始的暴力法, 有一个小地方可以优化, 就是结束的判定条件, 由于是递增的数组, 只要有nums[i]>target , 那么后续肯定也不会有相同的target了, 这个可以提前结束循环.

class Solution {
    
    func search(_ nums: [Int], _ target: Int) -> Int {
        
        var result = 0
        for i in 0..<nums.count {
            if nums[i]==target {
                result += 1
            }
            if nums[i]>target {
                break;
            }
        }
        return result        
    }

}

2. 可以先用二分查找找到target, 然后在向左向右探测找到左右的边界.  思路倒是很朴素,代码量有点大.

class Solution {
    
    func search(_ nums: [Int], _ target: Int) -> Int {

        var index = -1
        var left = 0
        var right = nums.count
        // 二分查找找出任意一个target的位置
        while left<right {

            let mid = (left+right)/2
            if nums[mid] == target {
                index = mid
                break
            }
            if nums[mid]>target {
                right = mid
            } else if nums[mid]<target {
                left = mid+1
            }
        }
        // 这个数字不存在
        if index == -1 {
            return 0
        }
        print("找到了\(index)")

        // 向左找最左边的target
        var leftIndex = 0
        while index-leftIndex>=0 {

            if index-leftIndex-1<0 {
                break
            }

            if nums[index-leftIndex] == target && nums[index-leftIndex-1] != target  {
                print("在左边找到了\(leftIndex)")
                break
            }
            leftIndex += 1

        }
        // 向右找最右边的target
        var rightIndex = 0
        while index+rightIndex<nums.count {

            if index+rightIndex+1>=nums.count {
                break
            }

            if nums[index+rightIndex] == target && nums[index+rightIndex+1] != target  {
                print("在右边找到了\(rightIndex)")
                break
            }
            rightIndex += 1
        }
        return leftIndex + rightIndex + 1


    }

}

3. 基于上面的优化, 一开始用二分查找找出最左边的target, 然后只需要向右探测找出最右边的即可. 需要在原来的二分查找上略作修改.

class Solution {
    
    func search(_ nums: [Int], _ target: Int) -> Int {

        var index = -1
        var left = 0
        var right = nums.count
        // 二分查找找出最左边的target的位置
        while left<right {

            let mid = (left+right)/2
            // 保证index是最左边的target, 左边的值 != target || 左边没有值了
            if nums[mid] == target  {
                // 左边的值 != target
                if mid-1>=0 && nums[mid-1] != target {
                    index = mid
                    break
                }
                // 左边没有值了
                if mid-1<0 {
                    index = mid
                    break
                }
                
            }
            if nums[mid]>=target {
                right = mid
            } else {
                left = mid+1
            }
        }
        // 这个数字不存在
        if index == -1 {
            return 0
        }
        print("找到了\(index)")

        // 向右找最右边的target
        var rightIndex = 0
        while index+rightIndex<nums.count {
            // 右边没有值了
            if index+rightIndex+1>=nums.count {
                break
            }
            // 右边的值 != target
            if nums[index+rightIndex] == target && nums[index+rightIndex+1] != target  {
                print("在右边找到了\(rightIndex)")
                break
            }
            rightIndex += 1
        }
        return rightIndex + 1
    }
    
}

4. 上面已经可以通过2分查找找到最左边的target了, 自然也可以类似的找到最右边的target, 然后最右边的target - 最左边的target+1 就是结果了.

class Solution {
    
    func search(_ nums: [Int], _ target: Int) -> Int {
        
        var leftIndex = -1
        var left = 0
        var right = nums.count
        // 二分查找找出最左边的target的位置
        while left<right {
            
            let mid = (left+right)/2
            // 保证index是最左边的target, 左边的值 != target || 左边没有值了
            if nums[mid] == target  {
                // 左边的值 != target
                if mid-1>=0 && nums[mid-1] != target {
                    leftIndex = mid
                    break
                }
                // 左边没有值了
                if mid-1<0 {
                    leftIndex = mid
                    break
                }
                
            }
            if nums[mid]>=target {
                right = mid
            } else {
                left = mid+1
            }
        }
        // 这个数字不存在
        if leftIndex == -1 {
            return 0
        }
        print("找到了\(leftIndex)")
        
        var rightIndex = leftIndex
        left = leftIndex
        right = nums.count
        // 二分查找找出最右边的target的位置
        while left<right {
            
            let mid = (left+right)/2
            // 保证index是最右边的target, 右边的值 != target || 右边没有值了
            if nums[mid] == target  {
                // 右边的值 != target
                if mid+1<=nums.count-1 && nums[mid+1] != target {
                    rightIndex = mid
                    break
                }
                // 右边没有值了
                if mid+1>=nums.count {
                    rightIndex = mid
                    break
                }
                
            }
            // 此处是>,可以保证找到最右边, 而上面是>=,是为了找最左边
            if nums[mid]>target {
                right = mid
            } else {
                left = mid+1
            }
        }
        return rightIndex-leftIndex+1
        
    }
    
}

5. 在看题解的过程中, 发现了一种很巧妙的思路, 由于传入的是一个int数组, 现在假设要往这个数组中插入新的值,那么target-0.5要插入的位置就是最左边的target位置, target+0.5的位置就是最右边的target的位置+1, 用(target+0.5) - (target-0.5)的位置差就是target的数量.

class Solution {
    
    func search(_ nums: [Int], _ target: Int) -> Int {
        // 相当于左边界的位置
        let leftInsert = insert(nums, Double(target)-0.5 )
        // 相当于右边界+1的位置
        let rightInsert = insert(nums, Double(target)+0.5 )
        return rightInsert-leftInsert
    }
    // 假设要在nums中插入一个数字, 找出要插入数字的位置
    func insert(_ nums: [Int], _ target: Double) -> Int {

        var left = 0
        var right = nums.count-1
        while left <= right {
            
            let mid = (left+right)/2

            if target > Double(nums[mid]) {
                left = mid+1
            }
            if target < Double(nums[mid]) {
                right = mid-1
            }
        }
        // left就是要插入值的位置
        return left
        
    }
    
}

总结一下目前的思路, 

 1.遍历过程中统计次数,nums[i]>target可以提前结束,O(N)

 2.二分查找出任意一个target的位置,然后左右扩展寻找,O(logN)

 3.二分查找出最左边的target位置,然后向右扩展寻找,O(logN)

 4.二分找出最左边的位置,二分找出最右边的位置,最右-最左+1即是结果,O(logN)

 5.找出target+0.5,target-0.5的插入位置,O(logN)

实际在运行的过程中, 除了第一个比较慢一点外, 其余的4种算法基本都是56ms, 可以打败99%.

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值