算法的优雅(三)卡牌的秘密

现在大学男生宿舍和3年前有什么不一样?答案是:以前他们都玩Dota,而现在很多人在玩LOL。

作为一个职业宅男,LOL也成为笔者的一部分了,而笔者最喜欢的英雄除了暴力小萝莉安妮外,还有一个就是卡牌大师——一切尽在卡牌中。


问题来了:一副扑克牌除去大小王,在剩下52张牌中,随意抽出一张牌,用尽可能快尽可能少的空间求出这张牌的点数(不要求花色)。

本问题来自《编程之美》中的1.1.5:快速找出故障机器,大家觉得我写的不好,可以去看看原著。


解法一:

对于学了多年编程的读者,不难想出一个方法,用一个数组,初始化为0,之后53张牌从头到尾扫一遍,每次点数的数组位置+1,最后扫一遍数组,3次的就是被抽出牌的点数了。

这样,我们得到了答案,时间复杂度是O(n),空间复杂度是O(k),k是数的最大值。

那么,我们能不能让复杂度降低呢?


解法二:

首先,我们不难证出,求出抽出牌点数的算法至少要将牌组扫一遍,所以时间复杂度O(n)不可能降低了,那么让我们看看空间复杂度。

还记的数据结构课学的一种查找方法么?没错,即使哈希表。

建立哈希表,之后每次存入数据,如果同一位置出现两次,那么这个位置肯定不是抽出的排了,所以该位置可以释放给别的数据用了。

所以,在空间上,最好情况是O(1),最坏情况依旧是O(k)。


解法三:

有没有比较稳定的空间复杂度呢?

让我们想想我们还学过什么。

在C++,算法、数据结构、离散数学、组成原理、数电、微型计算机等课程中,我们都学过一个东西,但是老师们都没有强调过,那就是位运算。

位运算是一种很神奇的运算方式,要比普通的四则运算快很多,而在位运算中有个XOR/抑或。

A XOR A = 0, A XOR 0 = A,而且满足结合律和交换律;

而正常牌出现是偶数次,而只有被抽出的牌是奇数次,所以,将所有数XOR一遍,结果就出来,而且空间复杂度是O(1)


解法四:

让我们看看有没有别方法。

首先,这是一副牌,我们知道什么信息呢?

所有52张牌的点数我们是知道的,那么它们的和也就知道了,如果用总和减去53张牌的点数,结果就是抽出的牌咯。


追问环节:

如果我抽出两张牌怎么办呢?

显然前三种方法可以解决,但XOR方法明显出现的局限性,让我们看看第四种方法怎么办。


设这两张牌是x和y,那么根据第四种方法我们能得到x+y,显然这个解释不确定的,而解一个二元方程,我们还需要另一个方程。

那么,我们知道总和确定,那么总乘积也知道的,于是xy的值也就知道了。(如果乘积很大,我们可以用平方和来做。)

于是这个二元方程组就联立好了。


继续追问环节:

如果是抽出了K张牌怎么办。

这道题没有标准答案,虽然我们可以用第四种方法求解方程组(貌似有一种特殊的行列式是第一行是0次幂,第二行1次幂,第三行2次幂....我忘了叫什么了....),显然这个方法不是很好,这时候hash是最好的选择了,即容易实现,而且能节省空间。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值