|
| 1 | +--- |
| 2 | +description: 剑指 Offer 03. 数组中重复的数字 |
| 3 | +--- |
| 4 | + |
| 5 | +# OF3.数组中重复的数字 |
| 6 | + |
| 7 | +## 题目描述 |
| 8 | + |
| 9 | +[题目地址](https://leetcode-cn.com/problems/shu-zu-zhong-zhong-fu-de-shu-zi-lcof/) |
| 10 | + |
| 11 | +找出数组中重复的数字。 |
| 12 | + |
| 13 | +在一个长度为 n 的数组 _`nums`_ 里的所有数字都在 0~n-1 的范围内。数组中某些数字是重复的,但不知道有几个数字重复了,也不知道每个数字重复了几次。请找出数组中任意一个重复的数字。 |
| 14 | + |
| 15 | +### **示例 1:** |
| 16 | + |
| 17 | +```go |
| 18 | +输入: |
| 19 | +[2, 3, 1, 0, 2, 5, 3] |
| 20 | +输出:2 或 3 |
| 21 | +``` |
| 22 | + |
| 23 | +### **限制:** |
| 24 | + |
| 25 | +```go |
| 26 | +2 <= n <= 100000 |
| 27 | +``` |
| 28 | + |
| 29 | +## 题解 |
| 30 | + |
| 31 | +### 思路1 : **哈希表 / Set** |
| 32 | + |
| 33 | +**算法流程:** |
| 34 | + |
| 35 | +1. 初始化: 新建 HashSet ,记为 dicdic |
| 36 | +2. 遍历数组 numsnums 中的每个数字 numnum |
| 37 | + 1. 当 num 在 dic 中,说明重复,直接返回 num |
| 38 | + 2. 将 numnum 添加至 dicdic 中 |
| 39 | +3. 返回 -1−1 。本题中一定有重复数字,因此这里返回多少都可以 |
| 40 | + |
| 41 | +**复杂度分析:** |
| 42 | + |
| 43 | +* **时间复杂度**$$O(N)$$**:**遍历数组使用 O\(N\) ,HashSet 添加与查找元素皆为 O\(1\)O\(1\) |
| 44 | +* **空间复杂度**$$O(N)$$**:** HashSet 占用 O\(N\) 大小的额外空间 |
| 45 | + |
| 46 | +#### 代码 |
| 47 | + |
| 48 | +{% tabs %} |
| 49 | +{% tab title="Go" %} |
| 50 | +```go |
| 51 | +func findRepeatNumber2(nums []int) int { |
| 52 | + mapTmp := make(map[int]bool) |
| 53 | + for _, val := range nums { |
| 54 | + if mapTmp[val] { |
| 55 | + return val |
| 56 | + } else { |
| 57 | + mapTmp[val] = true |
| 58 | + } |
| 59 | + } |
| 60 | + return 0 |
| 61 | +} |
| 62 | +``` |
| 63 | +{% endtab %} |
| 64 | + |
| 65 | +{% tab title="Python" %} |
| 66 | +```python |
| 67 | +class Solution: |
| 68 | + def findRepeatNumber(self, nums: [int]) -> int: |
| 69 | + dic = set() |
| 70 | + for num in nums: |
| 71 | + if num in dic: return num |
| 72 | + dic.add(num) |
| 73 | + return -1 |
| 74 | +``` |
| 75 | +{% endtab %} |
| 76 | + |
| 77 | +{% tab title="C++" %} |
| 78 | +```cpp |
| 79 | +class Solution { |
| 80 | +public: |
| 81 | + int findRepeatNumber(vector<int>& nums) { |
| 82 | + unordered_map<int, bool> map; |
| 83 | + for(int num : nums) { |
| 84 | + if(map[num]) return num; |
| 85 | + map[num] = true; |
| 86 | + } |
| 87 | + return -1; |
| 88 | + } |
| 89 | +}; |
| 90 | +``` |
| 91 | +{% endtab %} |
| 92 | +
|
| 93 | +{% tab title="Java" %} |
| 94 | +```java |
| 95 | +class Solution { |
| 96 | + public int findRepeatNumber(int[] nums) { |
| 97 | + Set<Integer> dic = new HashSet<>(); |
| 98 | + for(int num : nums) { |
| 99 | + if(dic.contains(num)) return num; |
| 100 | + dic.add(num); |
| 101 | + } |
| 102 | + return -1; |
| 103 | + } |
| 104 | +} |
| 105 | +``` |
| 106 | +{% endtab %} |
| 107 | +{% endtabs %} |
| 108 | + |
| 109 | +### 思路2:**原地交换** |
| 110 | + |
| 111 | +#### 算法流程 |
| 112 | + |
| 113 | +1. 遍历数组 numsnums ,设索引初始值为 i = 0i=0 : |
| 114 | + 1. 若 nums\[i\] = inums\[i\]=i : 说明此数字已在对应索引位置,无需交换,因此跳过 |
| 115 | + 2. 若 nums\[nums\[i\]\] = nums\[i\]nums\[nums\[i\]\]=nums\[i\] : 代表索引 nums\[i\]nums\[i\] 处和索引 ii 处的元素值都为 nums\[i\]nums\[i\] ,即找到一组重复值,返回此值 nums\[i\]nums\[i\] |
| 116 | + 3. 否则: 交换索引为 ii 和 nums\[i\]nums\[i\] 的元素值,将此数字交换至对应索引位置 |
| 117 | +2. 若遍历完毕尚未返回,则返回 -1 |
| 118 | + |
| 119 | +#### 复杂度分析 |
| 120 | + |
| 121 | +* **时间复杂度**$$O(N)$$**:**遍历数组使用 $$O(N)$$ ,每轮遍历的判断和交换操作使用 $$O(N)$$ |
| 122 | +* **空间复杂度**$$O(1)$$**:** 使用常数复杂度的额外空间。 |
| 123 | + |
| 124 | +{% tabs %} |
| 125 | +{% tab title="Go" %} |
| 126 | +```go |
| 127 | +func findRepeatNumber3(nums []int) int { |
| 128 | + for idx, val := range nums { |
| 129 | + if idx == val { |
| 130 | + continue |
| 131 | + } |
| 132 | + if nums[val] == val { |
| 133 | + return val |
| 134 | + } |
| 135 | + nums[val], nums[idx] = nums[idx], nums[val] |
| 136 | + } |
| 137 | + return 0 |
| 138 | +} |
| 139 | +``` |
| 140 | +{% endtab %} |
| 141 | + |
| 142 | +{% tab title="Python" %} |
| 143 | +```python |
| 144 | +class Solution: |
| 145 | + def findRepeatNumber(self, nums: [int]) -> int: |
| 146 | + i = 0 |
| 147 | + while i < len(nums): |
| 148 | + if nums[i] == i: |
| 149 | + i += 1 |
| 150 | + continue |
| 151 | + if nums[nums[i]] == nums[i]: return nums[i] |
| 152 | + nums[nums[i]], nums[i] = nums[i], nums[nums[i]] |
| 153 | + return -1 |
| 154 | +``` |
| 155 | +{% endtab %} |
| 156 | + |
| 157 | +{% tab title="Java" %} |
| 158 | +```java |
| 159 | +class Solution { |
| 160 | + public int findRepeatNumber(int[] nums) { |
| 161 | + int i = 0; |
| 162 | + while(i < nums.length) { |
| 163 | + if(nums[i] == i) { |
| 164 | + i++; |
| 165 | + continue; |
| 166 | + } |
| 167 | + if(nums[nums[i]] == nums[i]) return nums[i]; |
| 168 | + int tmp = nums[i]; |
| 169 | + nums[i] = nums[tmp]; |
| 170 | + nums[tmp] = tmp; |
| 171 | + } |
| 172 | + return -1; |
| 173 | + } |
| 174 | +} |
| 175 | +``` |
| 176 | +{% endtab %} |
| 177 | + |
| 178 | +{% tab title="C++" %} |
| 179 | +```cpp |
| 180 | +class Solution { |
| 181 | +public: |
| 182 | + int findRepeatNumber(vector<int>& nums) { |
| 183 | + int i = 0; |
| 184 | + while(i < nums.size()) { |
| 185 | + if(nums[i] == i) { |
| 186 | + i++; |
| 187 | + continue; |
| 188 | + } |
| 189 | + if(nums[nums[i]] == nums[i]) |
| 190 | + return nums[i]; |
| 191 | + swap(nums[i],nums[nums[i]]); |
| 192 | + } |
| 193 | + return -1; |
| 194 | + } |
| 195 | +}; |
| 196 | +``` |
| 197 | +{% endtab %} |
| 198 | +{% endtabs %} |
| 199 | +
|
| 200 | +## 总结 |
| 201 | +
|
| 202 | +如果你同我一样热爱数据结构、算法、LeetCode,可以关注我 GitHub 上的 算法 题解:[awesome-golang-algorithm](https://github.com/kylesliu/awesome-golang-algorithm) |
| 203 | +
|
0 commit comments