[1..n]间的数,有的出现2次、有的1次、有的缺失,找缺失的那些数(in-place算法)

vector<int> findDisappearedNumbers(vector<int>& nums){
    // 把数x当作下标(1-based),将nums[x-1]标记为负数
    for (int num : nums) {
        int x = abs(num);
        if (nums[x-1] > 0) nums[x-1] *= -1;
    }
    vector<int> ans;
    for (int i = 0; i < nums.size(); i++) {
        if (nums[i] > 0) ans.push_back(i+1);
    }
    return ans;
}

第一个缺失的正整数(数组有负数)

比如[1,2,0]返回3,[3,4,-1,1]返回2

int firstMissingPositive(vector<int>& nums) {
    // 期望变成数组[1..n]。位置i应放数i+1,即nums[i]==i+1
    // => nums[i]-1==i,nums[nums[i]-1]==nums[i]
    // 旋转置换:若nums[nums[i]-1]!=nums[i],交换这两处的值
    const int N = nums.size();
    for (int i = 0; i < N; i++) {
        while (1 <= nums[i] && nums[i] <= N && nums[nums[i]-1] != nums[i]) {
            swap(nums[nums[i]-1], nums[i]);
        }
    }
    // first missing
    for (int i = 0; i < N; i++) {
        if (nums[i] != i + 1) return i + 1;
    }
    return N + 1;
}

第k个缺失的正整数(有序正数组)

int findKthPositive(vector<int>& arr, int k) {
    // 题目:找第k小的缺失自然数
    // 假设arr[i]全部>k,所求就是k;从左往右遇到arr[i]<=k,就递增k
    for (int num : arr) { // arr[]是递增数组
        if (num <= k) k++;
        else break;
    }
    return k;
}    

也可用二分搜索解决

int findKthPositive(vector<int>& arr, int k) {
    // 题目:找第k小的缺失自然数
    // 数组arr和缺失数组mis可拼成自然数序列,
    // 值arr[m]是自然数序列的第arr[m]个元素,又是数组arr的第m+1个元素,
    // 所以<=arr[m]的缺失数count(m)=arr[m]-(m+1)
    // 因为arr[m]比m增加得快,count(m)是关于m的递增函数,
    // 条件式enough(m){count(m)>=k}满足二分搜索的条件形式[0..0 1..1]
    int l = -1, u = arr.size();
    while (l + 1 < u) {
        int m = l + (u - l) / 2;
        if (enough(m, arr, k)) {
            u = m;
        } else {
            l = m;
        }
    }
    // <=arr[u]的缺失数首次有>=k个。
    // <=arr[u-1]的缺失数有count(u-1)=arr[u-1]-u个,
    // 比起需要的k个,还差k-(arr[u-1]-u)个,
    // 第k个缺失数为 arr[u-1]+(k-(arr[u-1]-u)) = u+k
    return u + k;
}

bool enough(int m, vector<int>& arr, int k) {
    return arr[m] - (m + 1) >= k;
}

给数组补一些数,使[1..n]的数都能由数组数相加获得

贪心法

int minPatches(vector<int>& nums, int n) {
    // 设[1..missing)已能由数组数相加获得,尝试扩展上边界missing
    long missing = 1;
    int i = 0, ans = 0;
    while (missing <= n) { // 最终missing>n,[1..missing)包含[1..n]
        // nums[i]已能由相加获得(<missing)或在数组中(=missing)
        if (i < nums.size() && nums[i] <= missing) {
            missing += nums[i];
            i++;
        } else {
            missing += missing; // 补上数missing
            ans++;
        }
    }
    return ans;
}