本文,我们还是来聊面试问题。在之前的文章中,我们讨论过一个简单的题目: 求丢失的猪。
当时,题目中已经表明,只有一头猪丢失了。这种情况下,用异或是最佳算法,而且很好懂。
但是,如果逃跑的猪是多头呢?如何才能知道逃跑的猪猪们的编号?且看具体题目场景叙述。
题目场景如下:
有n只公猪,用车拉到菜市场去卖,猪身上分别贴了1~n的编号。发车之前,有些公猪的老婆流泪了,希望和猪老公一起上车,并和猪老公贴了相同编号。
车开到半路,突然,有些猪开始打歪主意了,想跳车逃跑。逃跑的规律是:单身公猪可以单独跳车逃跑,夫妻猪要么一起留在车上,要么同时跳车逃跑。
到了菜市场后,主人发现,有些猪逃跑了,震惊不已,想找出所有逃跑的公猪的编号。要求:时间复杂度为O(n), 空间复杂度为O(1),试编写代码求解。
天网恢恢,疏而不漏,这猪还是挺可怜的,逃跑了也要被查。
我们来具体看看是什么意思。假设到菜市场后,剩下的猪猪的编号是:
a = [4, 3, 2, 7, 8, 2, 3, 1]
那么很显然,溜走的公猪的编码是5和6, 至于5和6是否有老婆,不得而知,也不必知。
显然,当猪猪很少时,用肉眼便知逃跑的猪猪。但是,当猪猪很多时,肉眼肯定看不出来。
由于车上就那么大点空间,所以要在空间复杂度为O(1)的要求下解决问题,而且时间复杂度要求为O(n).
有的朋友说,直接哈希表搞定,这显然不行,因为空间复杂度不符合的要求。
有的朋友说,排序一下就搞定,这显然也不行啊,因为时空复杂度不符合要求。
那怎么办呢?确实挺难的,但也不是不可能,采取的思路是:用负号标识存在性。即用a[x-1]的负号,来标识x的存在性。如下表:
4 | 3 | 2 | 7 | 8 | 2 | 3 | 1 |
4 | 3 | 2 | -7 | 8 | 2 | 3 | 1 |
4 | 3 | -2 | -7 | 8 | 2 | 3 | 1 |
4 | -3 | -2 | -7 | 8 | 2 | 3 | 1 |
4 | -3 | -2 | -7 | 8 | 2 | -3 | 1 |
4 | -3 | -2 | -7 | 8 | 2 | -3 | -1 |
4 | -3 | -2 | -7 | 8 | 2 | -3 | -1 |
4 | -3 | -2 | -7 | 8 | 2 | -3 | -1 |
-4 | -3 | -2 | -7 | 8 | 2 | -3 | -1 |
有的朋友看到这里,依然很懵圈,不用着急,且听我解释,咱们来遍历数组a的每个元素。
-
针对4,令a[3]为负,即a[3]=-7,所以,下次看到-7时,就知道4是存在的。
-
针对3,令a[2]为负,即a[2]=-2,所以,下次看到-2时,就知道3是存在的。
-
针对2,令a[1]为负,即a[1]=-3,所以,下次看到-3时,就知道2是存在的。
-
略
-
略
-
略
-
略
-
针对-1, 知其原身必然是1,故令a[0]为负,即a[0]=-4,所以,下次看到-4时候,就知道1是存在的。
可以看到,a数组的最后值是:
a = [-4, -3, -2, -7, 8, 2, -3, -1]
最后只有8和2是正数,而8和2的下标位置分是4和5, 也就是说x-1的值是4和5,所以x的值就是5和6, 即5和6没有出现过,所以逃跑的公猪猪是5和6.
确实挺巧妙,本质就是:用a[x-1]的负号,来标识x的存在性。
话不多说,来看代码:
func findDisappearedNumbers(a []int) []int {
len := len(a)
var results []int
for i := 0; i < len; i++ {
if (a[taogeAbs(a[i]) - 1] > 0) {
a[taogeAbs(a[i]) - 1] = - a[taogeAbs(a[i]) - 1];
}
}
for i := 0; i < len; i++ {
if (a[i] > 0) {
results = append(results, i + 1)
}
}
return results
}
func taogeAbs(x int) int {
if x < 0 {
return -x
}
return x
}
在leetcode上进行了自测验证,结果OK,能找出逃掉的公猪,以本文中的a数组为例,得到逃跑的公猪是5和6:
实话说,如果完全没有见过这种思路,要在面试现场想出这种解法,几乎不太可能。但如果看过我前面文章,是能发现这种巧妙方法的。
腾讯、阿里、字节跳动的面试题,很难当场想出解法。所以,题还是要刷。刷题比刷抖音有趣多了,能培养和拓展思维,预防脑袋生锈。
最后,希望大家在刷题中,找到乐趣,你会感叹原来还能这样啊,顺便地,能拿到更好的offer,工资翻倍,福利多多,祝大家面试顺利。