剑指Offer04

03. 数组中重复的数字

找出数组中重复的数字。
在一个长度为 n 的数组 nums 里的所有数字都在 0~n-1 的范围内。数组中某些数字是重复的,但不知道有几个数字重复了,也不知道每个数字重复了几次。请找出数组中任意一个重复的数字。

示例 1:
输入: [2, 3, 1, 0, 2, 5, 3]
输出:2 或 3
思路:
(1)方式一:将输入的数组排序,从头到尾扫描数组
(2)方式二:哈希表,从头到尾扫描数组,每扫描一个元素时,判断哈希表里面是否已包含该元素,没有就加到哈希表里面,有就表示找到
(3)方式三: 从头到尾扫描整个数组中的数字,当扫描到下标为i的数字时,首先比较这个数字(用m表示)是不是等于下标i,如果是,接着比较下一个数字;如果不是,则将其与第m个数字比较,若与第m个数字相同,则说明它就是一个重复数字,如果不同,就将其与第m个数字进行交换,也就是把它放到自己应在的位置去。重复这个过程,直到该位置上的数与下标相同为止。即. 下标 0 的值为 0,下标 1 的值为 1,以此类推…。一旦某个下标的值不只一个,则找到了重复的数字。

class Solution {
public:
    int findRepeatNumber(vector<int>& nums) {
        if(nums.empty())
        {
            return -1;
        }
        sort(nums.begin(), nums.end());
        int size = nums.size();
        for(int i = 0; i < size; i++)
        {
            if(nums[i] < 0 || nums[i] > size-1)
                return -1;
            if(nums[i] == nums[i+1])
                return nums[i];
        }
        return -1;
    }
};
53(1).在排序数组中查找数字

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

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

输入: nums = [5,7,7,8,8,10], target = 6
输出: 0
思路:排序数组 nums 中的所有数字 target 形成一个窗口,记窗口的 左/右 边界 索引分别为 left 和 right ,分别对应窗口左边 / 右边的首个元素。
本题要求统计数字 target 的出现次数,可转化为:使用二分法分别找到 左边界 left 和 右边界 right ,易得数字 target 的数量为 right−left−1 。

在这里插入图片描述

class Solution {
public:
    int search(vector<int>& nums, int target) {
        int i = 0;
        int j = nums.size()-1;
        while(i <= j)
        {
            int m = (i+j)/2;
            if(nums[m] <= target)
                i = m+1;
            else
                j = m-1;
        }
        int right = i;
        if(j >= 0 && nums[j] != target) 
            return 0;
        i = 0;
        j = nums.size()-1;
        while(i <= j)
        {
            int m = (i+j)/2;
            if(nums[m] < target)
                i = m+1;
            else
                j = m-1;
        }
        int left = j;
        return right-left-1;
    }
};
53(2). 0~n-1中缺失的数字

一个长度为n-1的递增排序数组中的所有数字都是唯一的,并且每个数字都在范围0~n-1之内。在范围0~n-1内的n个数字中有且只有一个数字不在该数组中,请找出这个数字。

示例 1:

输入: [0,1,3]
输出: 2 示例 2:

输入: [0,1,2,3,4,5,6,7,9]
输出: 8
思路:
初始化: 左边界 left = 0 ,右边界 right = len(nums)−1 ;代表闭区间 [left, right] 。
循环二分: 当 left ≤ right 时循环 (即当闭区间 [left, j] 为空时跳出) ;
计算中点 mid = (left + right)//2 ,其中 “//” 为向下取整除法;
若 nums[mid] = mid ,说明mid前面的元素肯定都是完整的不少元素所以只需要继续二分右边的数组即可,则 “右子数组的首位元素” 一定在闭区间 [mid+1, right] 中,因此执行 left = mid+1;
若 nums[mid] != mid ,说明mid前面的元素就有少的所以只要继续二分左边的数组即可,则 “左子数组的末位元素” 一定在闭区间 [left, mid−1] 中,因此执行 right = mid−1;
返回值: 跳出时,变量 i 和 j 分别指向 “右子数组的首元素” 和 “左子数组的末元素” 。因此返回 i 即可。

class Solution {
public:
    int missingNumber(vector<int>& nums) {
        int i = 0;
        int j = nums.size()-1;
        while(i <= j)
        {
            int m = (i+j)/2;
            if(nums[m] != m)
            {
                if(m == 0 || nums[m-1] == m-1){
                    return m;
                }
                j = m - 1;
            }else{
                i = m + 1;
            }
        }
        if(i == nums.size()){
            return i;
        }
        return -1;
    }
};
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值