【LeetCode & 剑指offer刷题】数组题5:3 数组中重复的数字(287. Find the Duplicate Number)
【LeetCode & 剑指offer 刷题笔记】目录(持续更新中...)
287
.
Find the Duplicate Number
Given an array
nums
containing
n
+ 1 integers where each integer is between 1 and
n
(inclusive), prove that at least one duplicate number must exist. Assume that there is only one duplicate number, find the duplicate one.
Example 1:
Input:
[1,3,4,2,2]
Output:
2
Example 2:
Input:
[3,1,3,4,2]
Output:
3
Note:
-
You must not modify the array (assume the array is read only).
-
You must use only constant, O (1) extra space.
-
Your runtime complexity should be less than O ( n 2 ).
-
There is only one duplicate number in the array, but it could be repeated more than once.
//问题:查找数组中的重复数字(
长度为n+1,所有数字在1~n范围内,找出数组中任意一个重复数字,不能修改数组
)
/*
方法一:sort
若可以改变数组,可以直接sort,O(nlogn),O(1)
若不能改变数组,可以复制vector,再sort,O(nlogn), O(n)
*/
class
Solution
{
public
:
int
findDuplicate
(
vector
<
int
>&
nums
)
{
if
(
nums
.
empty
())
return
-
1
;
//表示没有重复数字
sort
(
nums
.
begin
(),
nums
.
end
());
for
(
int
i
=
1
;
i
<
nums
.
size
();
i
++)
{
if
(
nums
[
i
]
==
nums
[
i
-
1
])
return
nums
[
i
];
}
return
-
1
;
}
};
/*
方法二:哈希表
将元素当做key值,如果之前出现过了,则返回重复数字
O(n), O(n)
*/
class
Solution
{
public
:
int
findDuplicate
(
vector
<
int
>&
nums
)
{
if
(
nums
.
empty
())
return
-
1
;
//表示没有重复数字
unordered_set
<
int
>
seen
;
for
(
int
num
:
nums
)
{
if
(
seen
.
find
(
num
)
!=
seen
.
end
())
return
num
;
//如果在set中找到了该值,说明重复了
else
seen
.
insert
(
num
);
//否则插入key值
}
return
-
1
;
}
};
/*
方法三:也可以参见《剑指offer》P39中的解法(下标比较交换法) O(n),O(1)但会改变数组
问题:长度为n的数组中所有数字在0~n-1范围内,找出重复的数字
例:1 2 0 3 2 4
i=0,
2 1 0
0 1 2
i=1...
*/
#include
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
bool
duplicate
(
int
a
[],
int
length
,
int
*
duplication
)
{
//1.数组异常情况处理
if
(
a
==
nullptr
||
length
<
0
)
return
false
;
//2.数组值不符合条件时的处理
for
(
int
i
=
0
;
i
<
length
;
i
++)
{
if
(
a
[
i
]<
0
||
a
[
i
]>
length
-
1
)
return
false
;
}
/*
3. 比较a[i]与i
如果相等,i++
如果不相等,比较a[i]与a[a[i]],若相等,为重复数,退出;若不相等就交换。
*/
for
(
int
i
=
0
;
i
<
length
;
i
++)
{
while
(
a
[
i
]
!=
i
)
{
if
(
a
[
i
]
==
a
[
a
[
i
]])
{
*
duplication
=
a
[
i
];
return
true
;
//这里也可以返回重复的数字
}
else
swap
(
a
[
i
],
a
[
a
[
i
]]);
//每个数字最多交换2次(第一次为被交换方,第二次为交换方,到应处位置),故整个程序复杂度为O(n),o(1)
//a[i]被换到它应处的位置
}
}
return
false
;
}
};
/*
方法三:快慢指针法
O(n),O(1) 且不用改变数组
思路参考问题Linked List Cycle II(找有环链表的环入口)
将索引看做当前结点地址,将存储数看做指向下一个结点的指针,则重复数字即为环入口(索引或结点地址)
把第一个结点当做头结点
例子:
1 4 3 5 2 2
索引分别为0、1、2、3、4、5
0 1 4 2 3 5
1 -> 4 ->
2
-> 3 -> 5 -> 2
↑←-------←↓
注:
(1)如果在多一个重复数字2,则会多一个结点指向2,但是该结点永远无法被访问到,因为没有结点指向它
(2)如果数组中不存在重复数字,则为循环链表,相当于循环链表,这个时候需返回-1.
(3)如果多个数字重复,只有最前面的重复数字构成环,其他重复数字不会在链表中,所以
只能检测一个重复数字
(4)如果数组中有数字0,则该结点会指向头结点,从而形成循环链表,而其他结点会被丢失(所以
题目限定不包含0
,如果要包含0或负数的话,可以把整个数组预处理一遍,为负数时说明输入非法,为0时,可以将各数加1)
*/
class
Solution
{
public
:
int
findDuplicate
(
vector
<
int
>&
nums
)
{
if
(
nums
.
empty
())
return
-
1
;
//表示没有重复数字
int
slow
=
nums
[
0
];
int
fast
=
nums
[
nums
[
0
]];
while
(
slow
!=
fast
)
//假设存在重复数字,则会在环内相遇,假设不存在重复数字,形成循环链表,在头结点相遇
{
slow = nums[slow];
fast = nums[nums[fast]];
}
int
entry
=
0
;
if
(
entry
==
slow
)
return
-
1
;
//如果不存在重复数字,为循环链表(环入口在起始位置),则返回-1
while
(
entry
!=
slow
)
{
entry
=
nums
[
entry
];
slow
=
nums
[
slow
];
}
return
entry
;
}
};
posted @
2019-01-05 14:02 wikiwen 阅读(
...) 评论(
...) 编辑 收藏