技术提高是一个循序渐进的过程,所以我讲的leetcode算法题从最简单的level开始写的,然后到中级难度,最后到hard难度全部完。目前我选择C语言,Python和Java作为实现语言,因为这三种语言还是比较典型的。由于篇幅和精力有限,其他语言的实现有兴趣的朋友请自己尝试。
如果有任何问题可以在文章后评论或者私信给我。
如果有朋友希望我讲些其他话题,请在评论区留言或者私信给我。
持续分享,敬请关注。
LeetCode 1207. 唯一的数值次数(Unique Number of Occurrences)
问题描述:
给定一个整数数组arr,编写一个函数,当且仅当该数组中每个值的出现次数是唯一的时才返回true。
注:
- 1 <= arr.length <= 1000;
- -1000 <= arr[i] <= 1000;
示例:
C语言实现:
一般思路是,创建一个长度为2001的数组count,用来统计arr中不同值出现的数量。再创建一个长度是arr.length+1的数组 bits。遍历count,针对每一个元素e,如果bits[e] > 0, 返回false。否则 bits[e] += e;遍历结束,没问题就返回true。
这个思路实现简单,但是有一个问题:不管arr的长度是多少,如果arr中的数值次数都是唯一的,就需要遍历count 2001次。
我们期望能找到一个算法,在计算的时候是要依赖arr的长度,毕竟arr的长度是小于等于2001的。
我们可以对上面的算法做一些改造,我们将bits定义为长度为63的无符号整型,这么bits可以作为一个长度为2016的bitmap(63*32=2016),则它就可以用bit位标记arr中是否存在某个数。
这种方法我们在前面的题目中多次用的,它的好处是节省空间,但是有时候也会对时间复杂度有优化作用,一会就能看到。
接下来,我们遍历arr,开始在bits完成这个bit标注。由于arr中的数的数值范围是[-1000,1000],因此我们将它们都增加1000,变成[0,2000],以使得bits可以表示它们。这就是为什么bits的长度是63。
在遍历的同时,统计不同数值出现的次数。
我们回头看,会发现,其实bits就类似map中的key,而count就是map中的value。
然后我们新建一个长度为1+arr.length的整型数组 count_map,这个依然是一个bitmap,它用来鉴别是否存在重复数量。
然后我们遍历bits,这是和上一个算法最不同的地方。遍历bits就类似遍历map中的key。这里我们通过位操作,获取arr中每一个不同的值(代码中的j),然后依次来查询count中该值出现的次数。
最后,我们依然通过位操作来判断count_map中该次数是否已经出现,如果出现就返回false,否则设置count_map。
我们会发现这个算法虽然要复杂一点,但是没有对count遍历,对bits的遍历其实也是线性的,遍历的时间长短由arr中存在多少个不同值决定,所以时间复杂度是O(arr.length)。
详细代码如下:
需要提醒的是,bits需要定义成无符号整型,因为普通int,最前面一位是符号位,并不能表示具体值,当遇到1<<31操作时会出错,且移位操作时,数值1也需要强制转换成无符号整型。
Java语言实现:
Java 的实现和C语言的实现一致,需要提醒的是,java中没有无符号整型,所以一个整型我们只能用后31位做bitmap,所以bits长度定义位65,后续的位操作也是用31而不是32。
代码如下:
Python语言实现:
Python 的实现,我觉得的简洁才是王道。我们可以用Counter直接完成计数,最后比较Counter对象的key和value的长度是否相等即可。
代码如下: