mirror of
https://github.com/ShusenTang/LeetCode.git
synced 2024-09-02 14:20:01 +00:00
update 287
This commit is contained in:
parent
a101162a6d
commit
5f8da300d2
@ -19,6 +19,8 @@
|
|||||||
|
|
||||||
时间复杂度为O(n)
|
时间复杂度为O(n)
|
||||||
|
|
||||||
|
**注意此思路work的前提是数组中重复元素只有一个!**
|
||||||
|
|
||||||
## 思路三
|
## 思路三
|
||||||
此题还有一个十分巧妙的方法[可参考[此处](https://leetcode.com/problems/find-the-duplicate-number/discuss/72846/My-easy-understood-solution-with-O(n)-time-and-O(1)-space-without-modifying-the-array.-With-clear-explanation.)]。此时要把nums数组看成一个链表:`nums[i] = j`表示链表中结点i的next结点为j,例如数组
|
此题还有一个十分巧妙的方法[可参考[此处](https://leetcode.com/problems/find-the-duplicate-number/discuss/72846/My-easy-understood-solution-with-O(n)-time-and-O(1)-space-without-modifying-the-array.-With-clear-explanation.)]。此时要把nums数组看成一个链表:`nums[i] = j`表示链表中结点i的next结点为j,例如数组
|
||||||
```
|
```
|
||||||
@ -34,9 +36,12 @@ nums[i]: 3 1 3 4 2
|
|||||||
可以看到这个链表是有环的,为什么有环呢,就是因为数组nums中存在重复元素3。仔细分析我们可以发现,链表中环的开始结点即为重复元素,
|
可以看到这个链表是有环的,为什么有环呢,就是因为数组nums中存在重复元素3。仔细分析我们可以发现,链表中环的开始结点即为重复元素,
|
||||||
即题目转变为求一个带环链表中环的开始结点,这其实就是[142. Linked List Cycle II](https://leetcode.com/problems/linked-list-cycle-ii/),可参见这题我的[题解](https://github.com/ShusenTang/LeetCode/blob/master/solutions/142.%20Linked%20List%20Cycle%20II.md),这里直接给出代码。
|
即题目转变为求一个带环链表中环的开始结点,这其实就是[142. Linked List Cycle II](https://leetcode.com/problems/linked-list-cycle-ii/),可参见这题我的[题解](https://github.com/ShusenTang/LeetCode/blob/master/solutions/142.%20Linked%20List%20Cycle%20II.md),这里直接给出代码。
|
||||||
|
|
||||||
## 若可改变数组
|
**注意此思路work的前提是数组中元素不可能为0,即0一定不在环内!**
|
||||||
本题要求数组nums是只读的,即不能改变nums,如果可改变nums,那么此题还有一个时间复杂度O(n)空间复杂度O(1)的解法(剑指offer第二版面试题3)。
|
|
||||||
|
|
||||||
|
## 若可改变数组
|
||||||
|
本题要求数组nums是只读的,即不能改变nums,如果可改变nums,那么此题还有其他时间复杂度O(n)空间复杂度O(1)的解法(剑指offer第二版面试题3)。
|
||||||
|
|
||||||
|
### 思路四
|
||||||
由于所有元素都在区间[1, n]之内,所以如果没有重复的数字,那当数组排序后数字m会排在第m位(即下标是m-1)。
|
由于所有元素都在区间[1, n]之内,所以如果没有重复的数字,那当数组排序后数字m会排在第m位(即下标是m-1)。
|
||||||
所以我们可以重排这个数组。用while循环从头到尾扫描这个数组,当扫描到nums[i]时,
|
所以我们可以重排这个数组。用while循环从头到尾扫描这个数组,当扫描到nums[i]时,
|
||||||
1. 如果`i == nums[i] - 1`,则这个数字已经在它该在的位置,i加1然后进入下一循环即可;
|
1. 如果`i == nums[i] - 1`,则这个数字已经在它该在的位置,i加1然后进入下一循环即可;
|
||||||
@ -45,6 +50,14 @@ nums[i]: 3 1 3 4 2
|
|||||||
|
|
||||||
因为调用swap的次数是线性的,所以时间复杂度还是O(n),而没有引入额外的数组,所以空间复杂度是O(1)。
|
因为调用swap的次数是线性的,所以时间复杂度还是O(n),而没有引入额外的数组,所以空间复杂度是O(1)。
|
||||||
|
|
||||||
|
### 思路五
|
||||||
|
由于不能有额外的数组或者数据结构如hashmap来标记某个数字之前是否出现过,但是注意数组里数字的范围保证在[1, n]之间,所以可以利用现有数组设置标志,当一个数字被访问过后,可以将它索引到的数减n(也可加n,只是溢出风险大一些),之后如果发现当前元素索引到的数小于等于0就说明当前元素之前出现过一次了。此方法相对于思路四的优点是算法结束前可以将数组恢复原样。
|
||||||
|
|
||||||
|
|
||||||
|
## 需要注意此题的几个变种
|
||||||
|
1. 重复数字可能有多个(如剑指offer面试题3),此时思路二不再适用;
|
||||||
|
2. 元素范围为[0, n-1](如剑指offer面试题3_1),此时思路三不再适用。
|
||||||
|
|
||||||
# C++
|
# C++
|
||||||
|
|
||||||
## 思路一
|
## 思路一
|
||||||
@ -115,6 +128,7 @@ public:
|
|||||||
```
|
```
|
||||||
|
|
||||||
## 若可改变数组
|
## 若可改变数组
|
||||||
|
### 思路四
|
||||||
``` C++
|
``` C++
|
||||||
class Solution {
|
class Solution {
|
||||||
public:
|
public:
|
||||||
@ -130,3 +144,21 @@ public:
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
```
|
```
|
||||||
|
### 思路五
|
||||||
|
``` C++
|
||||||
|
class Solution {
|
||||||
|
public:
|
||||||
|
int findDuplicate(vector<int>& nums) {
|
||||||
|
int n = nums.size() - 1;
|
||||||
|
for(int i = 0; i <= n; i++){
|
||||||
|
int tmp = nums[i];
|
||||||
|
if(tmp <= 0) tmp += n; // 当前元素实际值
|
||||||
|
// tmp索引到的数小于等于0说明tmp之前出现过
|
||||||
|
if(nums[tmp] <= 0) return tmp;
|
||||||
|
nums[tmp] -= n;
|
||||||
|
}
|
||||||
|
// 算法结束前可以再遍历一遍数组将其恢复原样
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
```
|
||||||
|
Loading…
Reference in New Issue
Block a user