求逃跑的猪猪们

通过巧妙地利用数组元素的符号变化,解决了一道寻找失踪编号的问题。该问题要求在特定的时间和空间复杂度限制下,找出从集合中消失的元素。

作者 | 涛歌依旧   责编 | 张文

来源 | 转载自涛歌依旧(ID:ai_taogeyijiu_2021)

在之前的文章中,我们讨论过一个简单的题目: 求丢失的猪。

当时,题目中已经表明,只有一头猪丢失了。这种情况下,用异或是最佳算法,而且很好懂。

但是,如果逃跑的猪是多头呢?如何才能知道逃跑的猪猪们的编号?且看具体题目场景叙述。

题目场景如下:

有 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 的每个元素。

  1. 针对 4,令 a[3]为负,即 a[3]=-7,所以,下次看到-7 时,就知道 4 是存在的。

  2. 针对 3,令 a[2]为负,即 a[2]=-2,所以,下次看到-2 时,就知道 3 是存在的。

  3. 针对 2,令 a[1]为负,即 a[1]=-3,所以,下次看到-3 时,就知道 2 是存在的。


  4. 针对-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:

实话说,如果完全没有见过这种思路,要在面试现场想出这种解法,几乎不太可能。所以还是要多刷题。

程序员如何避免陷入“内卷”、选择什么技术最有前景,中国开发者现状与技术趋势究竟是什么样?快来参与「2020 中国开发者大调查」,更有丰富奖品送不停!



☞腾讯、字节跳动展开拉锯战;网易云音乐称酷狗抄袭;谷歌不再开发云游戏 | 极客头条

☞估值飙至 280 亿美元,Databricks G 轮融资 10 亿美元,谁说开源不挣钱?
☞开源 = 打破商业垄断?
☞三年已投 1000 亿打造的达摩院,何以仗剑走天涯?
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值