AcWing 14. 不修改数组找出重复的数字(分治法)

如果不将思考题考虑在内的话,那么AcWing 13.找出数组中重复的数字中的方法1的代码是可以完全移植过来的。这里不再赘述。不过,由于这道题的样例里面不存在含有超出范围的数据以及全部数据均出现一次的情况,因此代码还可以进一步简化。

简化后的具体代码如下:

class Solution {
public:
    int duplicateInArray(vector<int>& nums) {
        int n=nums.size();
        vector<int> v(n, 0);
        //统计出现次数
        for(auto &e: nums){
            v[e]++;
        }
        //遍历统计结果,发现有满足条件的则返回
        for(int i = 0; i < n; i++){
            if(v[i] > 1) return i;
        }
        return 0;
    }
};

现在,我们来解决思考题。由于要求空间复杂度为O(1),因此不可以再开辟等量空间统计出现次数。

为了解决问题,我们先按照元素所在的区间划分为等长的子区间(注意,这里的区间并不是数组的区间,而是数组中元素取值范围的区间)。然后再遍历数组统计落在这两个子区间里面元素的个数。假设所有的元素均是互异的,那么落在两个子区间里面元素的个数应该恰好等于区间长度。当存在重复元素时,两个子区间中应至少有一个是统计个数大于区间长度的。此时,只要在出现这种情况的子区间中继续如上的操作,最终就能得到重复的元素。该算法原理类似二分查找,不过要注意二分查找的子区间是按照数组划分的。

具体代码如下:

class Solution {
public:
    int duplicateInArray(vector<int>& nums) {
        int lo = 1, hi = nums.size() - 1;
        while(lo <= hi){
            int sum = 0; //统计数组元素在左子区间范围中元素个数
            int mid = lo + (hi - lo) / 2; //将区间划分为[lo, mid]和[mid + 1, hi]
            for(auto e: nums){
                if(lo <= e && e <= mid) sum++;
            }
            if(sum <= (mid - lo + 1)) lo = mid + 1;
            else hi = mid - 1;
        }
        return lo;
    }
};

 

使用分治法找出数组中第k大整数可以仿照快排的递归分治方式来实现。以下是具体思路和代码实现: ### 思路 1. 选取数组的第一个元素作为基准元素。 2. 遍历原数组,构造两个数组:一个存放小于等于基准的所有元素,另一个存放大于基准的所有元素。 3. 根据以下条件进行递归: - 若小数组元素数 + 1 等于 k,表明基准元素为第 k 大的数,返回该值。 - 若 k 小于小数组的个数,说明第 k 大的数在小数组内部,递归调用函数在小数组中继续查找。 - 若 k 大于小数组的个数,说明第 k 大的数在大数组内部,递归调用函数在大数组中继续查找,此时 k 的值需要更新为 `k - 1 - 小数组的个数`。 ### 代码实现 ```cpp #include <iostream> #include <vector> using namespace std; // 返回vector中第K大的数 k<container.size() int findKth(vector<int> vec, int k) { vector<int> smallVec; vector<int> bigVec; int baseEle = vec.at(0); for (size_t i = 1; i < vec.size(); i++) { if (vec.at(i) <= baseEle) smallVec.push_back(vec.at(i)); else bigVec.push_back(vec.at(i)); } // 递归的边界终点 小数组元素数+1==k 表明baseEle为第k大的数 返回该值 if (smallVec.size() + 1 == k) return baseEle; // k小于小数组的个数 说明第k大的数在小数组内部 if (smallVec.size() >= k) return findKth(smallVec, k); // k大于小数组的个数 说明第k大的数在大数组内部 else return findKth(bigVec, k - 1 - smallVec.size()); } int main() { int n, k, temp; cin >> n >> k; vector<int> inputVec; for (int i = 0; i < n; i++) { scanf("%d", &temp); inputVec.push_back(temp); } cout << findKth(inputVec, k) << endl; return 0; } ``` ### 复杂度分析 - **时间复杂度**:平均情况下为 $O(n)$,最坏情况下为 $O(n^2)$。 - **空间复杂度**:主要是递归调用栈的空间,平均情况下为 $O(log n)$,最坏情况下为 $O(n)$。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值