题目地址:
https://leetcode.com/problems/find-the-duplicate-number/
有个长为 n + 1 n+1 n+1的数组,设为 ( x 0 , x 1 , x 2 , . . . , x n ) (x_0,x_1,x_2,...,x_n) (x0,x1,x2,...,xn)其中的数字都属于 1 ∼ n 1\sim n 1∼n,已知只有一个数字发生了重复,问那个数字是几。
思路是静态链表。想象一个静态链表,它的值是 ( 0 , 1 , 2 , 3 , . . . , n ) (0,1,2,3,...,n) (0,1,2,3,...,n),对应的next数组是 ( x 0 , x 1 , x 2 , . . . , x n ) (x_0,x_1,x_2,...,x_n) (x0,x1,x2,...,xn),由于所有的 x i x_i xi都在 1 1 1和 n n n之间,所以这个next数组是定义合理的。假设对于某两个不同的数 i i i和 j j j,有 x i = x j x_i=x_j xi=xj,那么这个静态链表就存在环,所以直接用链表求环的方法找出环的入口即可。链表求环的算法可以参照https://blog.csdn.net/qq_46105170/article/details/104015181。详细的解释与证明在代码后面。代码如下:
class Solution {
public:
int findDuplicate(vector<int>& a) {
int slow = 0, fast = 0;
do {
slow = a[slow];
fast = a[a[fast]];
} while (slow != fast);
int cur = 0;
while (cur != slow) cur = a[cur], slow = a[slow];
return cur;
}
};
时间复杂度 O ( n ) O(n) O(n),空间 O ( 1 ) O(1) O(1)。
算法正确性证明:
沿袭前面的符号,设一个静态链表,它的值是
(
0
,
1
,
2
,
3
,
.
.
.
,
n
)
(0,1,2,3,...,n)
(0,1,2,3,...,n),对应的next数组是
(
x
0
,
x
1
,
x
2
,
.
.
.
,
x
n
)
(x_0,x_1,x_2,...,x_n)
(x0,x1,x2,...,xn),
0
0
0是这个链表的第一个节点。其实这个next数组就指的是nums这个数组。由于所有的
x
i
x_i
xi都在
1
1
1和
n
n
n之间,这个next数组是定义合理的。不妨设值
i
i
i的next为
f
(
i
)
f(i)
f(i),也就是
f
(
i
)
=
x
i
f(i)=x_i
f(i)=xi。那么这个链表链式表示实际上就是:
0
→
x
0
→
f
(
x
0
)
→
f
2
(
x
0
)
→
.
.
.
0\rightarrow x_0 \rightarrow f(x_0)\rightarrow f^2(x_0)\rightarrow ...
0→x0→f(x0)→f2(x0)→...首先,对于任意的
k
≥
0
k\ge0
k≥0,都有
f
k
(
x
0
)
!
=
0
f^k(x_0)!=0
fk(x0)!=0(定义
f
0
(
x
0
)
=
x
0
f^0(x_0)=x_0
f0(x0)=x0),这是因为
x
0
x_0
x0到
x
n
x_n
xn都属于
1
∼
n
1\sim n
1∼n,而这个序列的取值只能取有限个不同值,所以一定存在重复,由抽屉原理,存在某两个不同的数
i
i
i和
j
j
j,满足
1
≤
i
<
j
≤
n
+
1
1\le i<j\le n+1
1≤i<j≤n+1,并且
f
i
(
x
0
)
=
f
j
(
x
0
)
≠
0
f^i(x_0)=f^j(x_0)\ne0
fi(x0)=fj(x0)=0。不妨设
i
i
i和
j
j
j是最小的一对这样的数。所以有
f
(
f
i
−
1
(
x
0
)
)
=
f
(
f
j
−
1
(
x
0
)
)
f(f^{i-1}(x_0))=f(f^{j-1}(x_0))
f(fi−1(x0))=f(fj−1(x0))并且
f
i
−
1
(
x
0
)
≠
f
j
−
1
(
x
0
)
f^{i-1}(x_0)\ne f^{j-1}(x_0)
fi−1(x0)=fj−1(x0)也就是说,
f
i
−
1
(
x
0
)
f^{i-1}(x_0)
fi−1(x0)和
f
j
−
1
(
x
0
)
f^{j-1}(x_0)
fj−1(x0)这两个node对应的next是同一个值,这“同一个值”就是要找的那个重复的数。用链表的语言来说,就是环的入口。所以只需要找这个静态链表环的入口就行了。