循环排序总结

可以用来处理数组中的数值限定在一定的区间的问题。

例题:

1.找到所有数组中消失的数字

(https://leetcode-cn.com/problems/find-all-numbers-disappeared-in-an-array/)

给定一个范围在  1 ≤ a[i] ≤ n ( n = 数组大小 ) 的 整型数组,数组中的元素一些出现了两次,另一些只出现一次。

找到所有在 [1, n] 范围之间没有出现在数组中的数字。

您能在不使用额外空间且时间复杂度为O(n)的情况下完成这个任务吗? 你可以假定返回的数组不算在额外空间内。

题本身不难,使用哈希表很容易通过,但如果不使用额外空间和时间复杂度为O(n)时,就比较难了

思路:我们可以将数组中的每个数根据索引的大小,将对应索引的值都加上n,那么到最后,只有缺少的值对应的索引位置上的值没有加上n,再遍历一遍数组就可以得到缺少的值

class Solution {
public:
    vector<int> findDisappearedNumbers(vector<int>& nums) {
        vector<int> ans;
        int n = nums.size();
        for(int i=0;i<n;i++){
            int pos = (nums[i]-1)%n;
            nums[pos] += n; 
        }
        for(int i=0;i<n;i++){
            if(nums[i]<=n){
                ans.push_back(i+1);
            }
        }
        return ans;
    }
};

2.寻找重复数

(https://leetcode-cn.com/problems/find-the-duplicate-number/)

给定一个包含 n + 1 个整数的数组 nums ,其数字都在 1 到 n 之间(包括 1 和 n),可知至少存在一个重复的整数。

假设 nums 只有 一个重复的整数 ,找出 这个重复的数 。

同样也可以使用哈希表解决,但有几种巧妙的方法

(1)二分

二分法的思路是取[left,right]的中间值mid,然后统计于是数组中 小于等于 mid的个数cnt,如果cnt的个数严格大于mid,那么答案就在[left,mid]中

class Solution {
public:
    int findDuplicate(vector<int>& nums) {
        int n = nums.size();
        int l = 1,r = n-1;
        while(l<r){
            int mid = (l+r)/2;
            int cnt = 0;
            for(int i=0;i<n;i++){
                cnt += (nums[i] <= mid);
            }
            //抽屉原理,如果小于等于mid的个数大于4,那么重复元素一定存在于[1,4]
            if(cnt > mid) r = mid;
            else l = mid + 1;
        }
        return l;
    }
};

(2)位运算

位运算的思路是,先统计数组中每个元素在每一位上的个数x,然后统计[1,n]中每个元素在每一位上的个数y,如果x大于y,那么重复元素在该位是有值的。

class Solution {
public:
    int findDuplicate(vector<int>& nums) {
        int n = nums.size();
        int bit_max = 31;
        while(!((n-1) >> bit_max)){ //得到数组中数的最高位
            bit_max--;
        }
        int ans = 0;
        for(int i = 0 ;i <= bit_max;i++){
            int x = 0,y = 0;
            for(int j = 0;j < n;j++){
                if(nums[j] & (1 << i)){
                    x++;
                }
                if(j && (j & (1 << i))){
                    y++;
                }
            }
            //对存在的位进行或运算
            if(x > y){
                ans |= (1<<i);
            }
        }
        return ans;
    }
};

(3)快慢指针

如果数组中存在重复的元素,那么指针根据nums[]对应的索引来移动,那么最后一定会进入环中。不懂快慢指针的可以看前几天的题解。

快慢指针时间复杂度O(n),空间复杂度O(1).

class Solution {
public:
    int findDuplicate(vector<int>& nums) {
        int fast = 0,slow = 0;
        do{
            slow = nums[slow];
            fast = nums[nums[fast]];
        }while(fast != slow);
        slow = 0;
        while(slow != fast){
            slow = nums[slow];
            fast = nums[fast];
        }
        return slow;
    }
};
posted @ 2021-02-04 13:41  voids5  阅读(270)  评论(0编辑  收藏  举报