题目描述
- LL今天心情特别好,因为他去买了一副扑克牌,发现里面居然有2个大王,2个小王(一副牌原本是54张^_^)…他随机从中抽出了5张牌,想测测自己的手气,看看能不能抽到顺子,如果抽到的话,他决定去买体育彩票,嘿嘿!!“红心A,黑桃3,小王,大王,方片5”,“Oh My God!”不是顺子…..LL不高兴了,他想了想,决定大\小 王可以看成任何数字,并且A看作1,J为11,Q为12,K为13。上面的5张牌就可以变成“1,2,3,4,5”(大小王分别看作2和4),“So Lucky!”。LL决定去买体育彩票啦。 现在,要求你使用这幅牌模拟上面的过程,然后告诉我们LL的运气如何。为了方便起见,你可以认为大小王是0。
- 地址: 牛客链接
问题分析
- 排序法:先将数组排序,那么0肯定在前,其他元素在后。然后遍历数组,累加记录 0的数目,看 0 的数目能够弥补当前两个元素的差距(如4,6之间的差距便是 1,需要 一个 0作为 5 去弥补)。只有当遍历的过程中,0的数目一直能弥补非0元素之间的差距,并且非0元素不会出现重复时,才能形成顺子。
时间复杂度:O(NlogN) 宏观法:不拘泥与小的细节,不去考虑具体 0的数目,从宏观上考虑问题,如果这个数组的 max 与min 相差大于4,那么肯定不会成为顺子。如果出现了非0的重复元素,那么肯定不是顺子。
所以,如果要是顺子必须满足:- max - min < 5
- 非0元素不为重复
所以,遍历数组一次便可以得到答案
那么,根据检验重复的方法,又可以有以下三种做法:
- set检验重复
- count数组检验重复(count[2] = 1 便表示 2 出现了 1 次)
- bitmap 的思想,用一位来检验重复,用一个flag,二进制表现形式为32位,第 i 位便可以表示 i 是否出现过。若没有,则为1,若出现了,则置1。这样比以上更加节省空间。
时间复杂度:O(N)
经验教训
- bitmap 检验重复的方法
- 检验第 i 位 是否为 1:用 & 与 >>
((flag >> i) & 1) == 1
- 将第 i 位设置为 1,其他位不变:用 | 与 <<
flag |= (1 << i);
- 检验第 i 位 是否为 1:用 & 与 >>
- 宏观考虑问题
代码实现
- 排序法:
public boolean isContinuous(int [] numbers) {
if (numbers == null || numbers.length != 5) {
return false;
}
Arrays.sort(numbers);
int numOfZero = 0;
for (int i = 0; i < numbers.length; i++) {
if (numbers[i] == 0) {
numOfZero++;
continue;
}else {
//出现重复
if (i - 1 >= 0 && numbers[i - 1] == numbers[i]) {
return false;
}
if (i - 1 >= 0 && numbers[i - 1] != 0) {
//计算差距
int numOfInterval = numbers[i] - numbers[i - 1] - 1;
//当前0是否能弥补
if (numOfInterval > numOfZero) {
//不能
return false;
}
//弥补后更新0的数量
numOfZero -= numOfInterval;
}
}
}
return true;
}
- count数组检验重复
public boolean isContinuous(int [] numbers) {
if (numbers == null || numbers.length != 5) {
return false;
}
int max = -1;
int min = 14;
int[] count = new int[14];
for (int i = 0; i < 5; i++) {
if (numbers[i] == 0) {
continue;
}
if (count[numbers[i]] == 0) {
//未出现重复,更新count,max,min
count[numbers[i]]++;
max = max >= numbers[i] ? max : numbers[i];
min = min <= numbers[i] ? min : numbers[i];
if (max - min > 4) {
return false;
}
}else {
//出现重复
return false;
}
}
return true;
}
- bitmap检验重复
public boolean isContinuous(int [] numbers) {
if (numbers == null || numbers.length != 5) {
return false;
}
int max = -1;
int min = 14;
int flag = 0;
for (int i = 0; i < 5; i++) {
if (numbers[i] == 0) {
continue;
}
//出现重复
if (((flag >> numbers[i]) & 1) == 1) {
return false;
}else {
//未出现重复,将相应位置1
flag |= (1 << numbers[i]);
//更新 max,min
max = max >= numbers[i] ? max : numbers[i];
min = min <= numbers[i] ? min : numbers[i];
if (max - min > 4) {
return false;
}
}
}
return true;
}