剑指 Offer -- 数组中重复的数字(五十)

本文介绍了一种在未排序数组中查找重复数字的高效算法。该算法利用数组元素值范围的特点,通过交换元素来实现原地排序,一旦发现某个位置上的元素与其索引不匹配,则表明找到了重复项。

数组中重复的数字(五十)

题目描述:
在一个长度为 n 的数组里的所有数字都在 0n - 1 的范围内。 数组中某些数字是重复的,但不知道有几个数字是重复的。也不知道每个数字重复几次。请找出数组中任意一个重复的数字。 例如,如果输入长度为 7 的数组{2,3,1,0,2,5,3},那么对应的输出是第一个重复的数字 2

代码(已在牛客上 AC)

使用排序或者哈希表的思路就不多谈了, 这里参考 http://cuijiahua.com/blog/2018/01/basis_50.html 了解到第三种思路. 一般像数组中元素值的大小在 0 ~ n - 1 之间(或者 1 ~ n 之间) 啥的, 都可以考虑将元素值作为索引…

这道题是基于这样的考虑: 如果数组中没有重复元素, 那么在排序的情况下, iA[i] 是要一一对应的, 比如:

A[i] : 0  3  2  1
  i  : 0  1  2  3

i 的角度来看, 由于 A[1] != 1 却等于 3, 而从 A[i] 的角度看, 有 A[A[1]] = A[3] == 1 却不等于 3, 因此, 只要 i 的位置上的数据 A[i] 与数值 i 不相等, 那么相应的有 A[i] 位置上的数据 A[A[i]] 不等于 A[i](为了不烧脑, 绕口, 这句话可以从 A[i] 的角度看, 即把上面的 A[i] 那一行当索引, i 那一行当数组中的数值). 这种情况下, 可以考虑将位于索引 i 上的元素 A[i]A[A[i]] 交换, 看看交换后索引和值是否匹配, 如果仍然不相等, 那么继续交换这个过程.

上面的思想反映到下面的代码, 主要是:

 while (i != numbers[i]) {
    if (numbers[i] == numbers[numbers[i]]) {
        *duplication = numbers[i];
        return true;
    }
    swap(numbers[i], numbers[numbers[i]]);
}

即对于当前的索引 i, 判断 i 与其上的值 numbers[i] 是否相等, 如果不相等, 就不断将 numbers[i]numbers[numbers[i]] 中的数据交换. 但由于数组中存在重复值, 所以交换过程中, 如果遇到 numbers[i]numbers[numbers[i]] 相等, 那么直接返回 true.

class Solution {
public:
    // Parameters:
    //        numbers:     an array of integers
    //        length:      the length of array numbers
    //        duplication: (Output) the duplicated number in the array number
    // Return value:       true if the input is valid, and there are some duplications in the array number
    //                     otherwise false

    // 这道题还是有难度的, 不在于编码, 而在于思路, 详情参考:
    // http://cuijiahua.com/blog/2018/01/basis_50.html
    // 下面的方式是 O(N) 的, 假设数组中没有元素是重复的, 那么就希望索引和对应的元素相等,
    // 即 i == A[i]. 基于这一考虑, 遍历数组 A, 如果存在 i != A[i], 那么就判断 A[A[i]]
    // 与 A[i] 的关系, 如果仍然不相等, 那么就交换 A[A[i]] 与 A[i] 这个过程要持续下去,
    // 直到 i == A[i]. 如果在交换过程中发现某次 A[i] 与 A[A[i]] 相等了, 说明就找到了重复的
    // 元素, 返回 duplication 即可.
    bool duplicate(int numbers[], int length, int* duplication) {
        // 防止非法输入
        if (numbers == NULL || length <= 0) return false;
        for (int i = 0; i < length; ++i)
            if (numbers[i] < 0 || numbers[i] > length - 1) return false;

        for (int i = 0; i < length; ++i) {
            while (i != numbers[i]) {
                if (numbers[i] == numbers[numbers[i]]) {
                    *duplication = numbers[i];
                    return true;
                }
                swap(numbers[i], numbers[numbers[i]]);
            }
        }
        return false;
    }
};
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值