44. 扑克牌顺子
题目描述
本题知识点: 字符串
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的运气如何, 如果牌能组成顺子就输出true,否则就输出false。为了方便起见,你可以认为大小王是0。
思路
分析可知,必须要满足三个条件:
- 数组长度为5
- 除零以外没有重复数
- 最大值减去最小值小于5
显然第一条和第三条非常容易实现,因此我们主要考虑第二条的实现。
思路一
由于只有14个可能的取值(0-13),我们可以设定一个长度为14的数组来记录这14个数出现的次数。同时我们设定一个最大值和最小值来标记相应的值,需要注意的是这两个值都不取0。由于使用了辅助计数数组,该方法的时间复杂度和空间复杂度都为 O ( n ) O(n) O(n)。
# -*- coding:utf-8 -*-
class Solution:
def IsContinuous(self, numbers):
# write code here
l = len(numbers)
if l != 5:
return False
minNum, maxNum = 14, -1
count = [0 for i in range(14)]
# 得到了两个最值并且确定了是否有除0外的重复数值
for i in range(l):
count[numbers[i]] += 1
if numbers[i] == 0:
continue
if count[numbers[i]] > 1:
return False
if numbers[i] > maxNum:
maxNum = numbers[i]
if numbers[i] < minNum:
minNum = numbers[i]
if maxNum - minNum < 5:
return True
else:
return False
思路二
该方法首先对数组进行排序,再对数组进行遍历。遍历的过程中记录0出现的个数并判断前后两个数是否相等,若循环可以执行完成,则说明一定不存在除0外的两个相同的数字,此时只需要判断最大值和最小值之差是否满足条件即可。该方法时间复杂度为 O ( n l o g n ) O(nlogn) O(nlogn),空间复杂度为 O ( 1 ) O(1) O(1)。
# -*- coding:utf-8 -*-
class Solution:
def IsContinuous(self, numbers):
# write code here
l = len(numbers)
if l != 5:
return False
numbers = sorted(numbers)
count0 = 0
# 统计了0的个数->得到最小值的位置;并判断了是否有除0外的重复数
for i in range(l):
if numbers[i] == 0:
count0 += 1
continue
if i < l-1 and numbers[i] == numbers[i+1]:
return False
# 函数运行到这一步说明一定没有除零外的重复数
if numbers[l-1] - numbers[count0] < 5:
return True
else:
return False
45. 圆圈中最后剩下的数
题目描述
本题知识点: 模拟
每年六一儿童节,牛客都会准备一些小礼物去看望孤儿院的小朋友,今年亦是如此。HF作为牛客的资深元老,自然也准备了一些小游戏。其中,有个游戏是这样的:首先,让小朋友们围成一个大圈。然后,他随机指定一个数m,让编号为0的小朋友开始报数。每次喊到m-1的那个小朋友要出列唱首歌,然后可以在礼品箱中任意的挑选礼物,并且不再回到圈中,从他的下一个小朋友开始,继续0…m-1报数…这样下去…直到剩下最后一个小朋友,可以不用表演,并且拿到牛客名贵的“名侦探柯南”典藏版(名额有限哦!!_)。请你试着想下,哪个小朋友会得到这份礼品呢?(注:小朋友的编号是从0到n-1)
思路
思路一:模拟法
该方法直接用数组模拟整个流程,时间复杂度为 O ( m n ) O(mn) O(mn),这个时间复杂度非常高,差点没通过。空间复杂度为 O ( n ) O(n) O(n)。
# -*- coding:utf-8 -*-
class Solution:
def LastRemaining_Solution(self, n, m):
# write code here
if n < 1 or m < 1:
return -1
array = list(range(n))
count, i, step = n, -1, 0
while count > 0:
i += 1
# 模拟环
if i >= n:
i = 0
# 跳过已经被删除的元素
if array[i] == -1:
continue
# 经过没有被删除的步数才算是有效的
step += 1
# 直接写step==m会超时
if step == (m%count) or step == m:
array[i] = -1
step = 0
count -= 1
return i
思路二:归纳法
假设孩子编号为
0
,
1
,
2...
n
0,1,2 ... n
0,1,2...n,那么第一次出局的孩子编号为
k
=
(
m
−
1
)
%
n
k=(m-1)\%n
k=(m−1)%n,我们假设根据这样的序列得到的最后一个孩子为
f
(
m
,
n
)
f(m,n)
f(m,n),显然函数f跟m,n相关。第二次问题变为
k
+
1
,
k
+
2...
n
,
1
,
2...
k
−
1
k+1,k+2...n,1,2...k-1
k+1,k+2...n,1,2...k−1,我们设最后一个孩子为
q
(
m
,
n
−
1
)
q(m, n-1)
q(m,n−1)。显然有
f
(
m
,
n
)
=
q
(
m
,
n
−
1
)
f(m,n)=q(m,n-1)
f(m,n)=q(m,n−1)。下面我们考虑:
k
+
1
,
k
+
2...
n
,
1
,
2...
k
−
1
=
>
q
(
m
,
n
−
1
)
k+1,k+2...n,1,2...k-1=>q(m,n-1)
k+1,k+2...n,1,2...k−1=>q(m,n−1)
1
,
2
,
3...
n
−
1
=
>
f
(
m
,
n
−
1
)
1,2,3...n-1=>f(m,n-1)
1,2,3...n−1=>f(m,n−1)
不难看出,上面的式子左右两边存在一样的变化规律。我们假设存在一个函数g可以把
k
+
1
,
k
+
2...
n
,
1
,
2...
k
−
1
k+1,k+2...n,1,2...k-1
k+1,k+2...n,1,2...k−1映射为
0
,
1
,
2...
n
0,1,2 ... n
0,1,2...n,不难求出
g
(
x
)
=
(
x
−
k
−
1
)
%
n
g(x)=(x-k-1)\%n
g(x)=(x−k−1)%n,那么显然g也可以把
q
(
m
,
n
−
1
)
q(m,n-1)
q(m,n−1)映射为
f
(
m
,
n
−
1
)
f(m,n-1)
f(m,n−1),即
g
(
q
(
m
,
n
−
1
)
)
=
f
(
m
,
n
−
1
)
g(q(m,n-1))=f(m,n-1)
g(q(m,n−1))=f(m,n−1)。当然也可以得出:
q
(
m
,
n
−
1
)
=
g
−
1
(
f
(
m
,
n
−
1
)
)
q(m, n-1)=g^{-1}(f(m,n-1))
q(m,n−1)=g−1(f(m,n−1))
又有
g
−
1
(
x
)
=
(
x
+
k
+
1
)
%
n
g^{-1}(x)=(x+k+1)\%n
g−1(x)=(x+k+1)%n,因此
q
(
m
,
n
−
1
)
=
g
−
1
(
f
(
m
,
n
−
1
)
)
=
(
f
(
m
,
n
−
1
)
+
k
+
1
)
%
n
=
(
f
(
m
,
n
−
1
)
+
m
)
%
n
q(m, n-1)=g^{-1}(f(m,n-1))=(f(m,n-1)+k+1)\%n=(f(m,n-1)+m)\%n
q(m,n−1)=g−1(f(m,n−1))=(f(m,n−1)+k+1)%n=(f(m,n−1)+m)%n
因此:
f
(
m
,
n
)
=
0
,
n
=
1
f(m,n)=0, n=1
f(m,n)=0,n=1
f
(
m
,
n
)
=
(
f
(
m
,
n
−
1
)
+
m
)
%
n
,
n
>
=
2
f(m,n)=(f(m,n-1)+m)\%n,n>=2
f(m,n)=(f(m,n−1)+m)%n,n>=2
该方法时间复杂度为
O
(
n
)
O(n)
O(n),空间复杂度为
O
(
1
)
O(1)
O(1)。
# -*- coding:utf-8 -*-
class Solution:
def LastRemaining_Solution(self, n, m):
# write code here
if n < 1 or m < 1:
return -1
res = 0
# 长度为2-n
for i in range(2, n+1):
res = (res + m) % i
return res
46. 求 1 + 2 + . . . + n 1+2+...+n 1+2+...+n
题目描述
求1+2+3+…+n,要求不能使用乘除法、for、while、if、else、switch、case等关键字及条件判断语句(A?B:C)。
思路
该题要求不能用循环的方法,因此想到递归的方法。递归的方法难点在于如何判断终止条件,因为题目还规定了不能使用条件判断语句。因此使用0 and others = 0
这个特性来当做终止条件。
# -*- coding:utf-8 -*-
class Solution:
def Sum_Solution(self, n):
# write code here
return n and (n + self.Sum_Solution(n-1))
下面这种方法只是练习一下内置函数,当然使用sum
函数会更简单,但是不属于题目考察知识点。
# -*- coding:utf-8 -*-
class Solution:
def Sum_Solution(self, n):
# write code here
def add(x, y):
return x + y
return reduce(add, range(1, n+1))
47. 不用加减乘除做加法
题目描述
写一个函数,求两个整数之和,要求在函数体内不得使用+、-、*、/四则运算符号。
思路
下面的方法来源于牛客网评论。
首先看十进制是如何做的:
5
+
7
=
12
5+7=12
5+7=12,三步走
- 第一步:相加各位的值,不算进位,得到 2 2 2。
- 第二步:计算进位值,得到 10 10 10. 如果这一步的进位值为 0 0 0,那么第一步得到的值就是最终结果。
- 第三步:重复上述两步,只是相加的值变成上述两步的得到的结果
2
2
2和
10
10
10,得到
12
12
12。
5 + 7 = 2 + 10 = 12 + 0 = 12 5+7=2+10=12+0=12 5+7=2+10=12+0=12
同样我们可以用三步走的方式计算二进制值相加: 5 − 101 5-101 5−101, 7 − 111 7-111 7−111
- 第一步:相加各位的值,不算进位,得到 010 010 010,二进制每位相加就相当于各位做异或操作, 101 ⨁ 111 101\bigoplus111 101⨁111。
- 第二步:计算进位值,得到 1010 1010 1010,相当于各位做与操作得到 101 101 101,再向左移一位得到 1010 1010 1010, ( 101 & 111 ) < < 1 (101\&111)<<1 (101&111)<<1。
- 第三步重复上述两步, 各位相加 010 ⨁ 1010 = 1000 010\bigoplus1010=1000 010⨁1010=1000,进位值为 100 = ( 010 & 1010 ) < < 1 100=(010\&1010)<<1 100=(010&1010)<<1。
- 继续重复上述两步: 1000 ⨁ 100 = 1100 1000\bigoplus100 = 1100 1000⨁100=1100,进位值为 0 0 0,跳出循环, 1100 1100 1100为最终结果。
python
没有无符号左移操作,所以需要越界检查.
# -*- coding:utf-8 -*-
class Solution:
def Add(self, num1, num2):
# write code here
while num2 != 0:
# 这两步不设置边界会出错
tmp = (num1 ^ num2) & 0xFFFFFFFF
num2 = ((num1 & num2) << 1) & 0xFFFFFFFF
num1 = tmp
return num1 if num1<=0x7FFFFFFF else ~(num1^0xFFFFFFFF)