本文作者 TomorrowWu,原创文章,转载注明出处,博客地址 https://segmentfault.com/u/to... 第一时间看后续精彩文章。觉得好的话,顺手分享到朋友圈吧,感谢支持。
最近看了一道关于三色球的面试题,虽然很简单,但是还是考研编码能力的,下面做个对比
题目
有红、黄、绿三种颜色的球,其中红球3个,黄球3个,绿球6个。现将这12个球混放在一个盒子里,从中任意摸出8个球,编程计算摸出球的各种颜色搭配。
看到题目,显然是排列组合问题,下面看代码
优化前
func computeMatch1(redCount, yellowCount, greenCount, total int) int {
if redCount < 0 || yellowCount < 0 || greenCount < 0 || total <= 0 || total > redCount+yellowCount+greenCount {
return 0
}
num := 0
for r := 0; r <= redCount; r++ {
for y := 0; y <= yellowCount; y++ {
for g := 0; g <= greenCount; g++ {
if r+y+g == total {
//log.Printf("red:%v yellow:%v green:%v", r, y, g)
num++
}
}
}
}
return num
}
测试结果
为精确测出算法计算的时间,注释掉了log.Printf()代码
func Test_computeMatch1(t *testing.T) {
t1 := time.Now()
n := computeMatch1(3000,3000,6001,12000)
duration := time.Since(t1)
t.Logf("排列组合数量:%v 时长:%v",n,duration)
}
//结果
排列组合数量:3 时长:32.450475506s
代码未优化,三层for循环,且不管要求摸出多少球,遍历次数都一样,时间复杂度是O(n)=a*b*c
,在球数比较大时,运算时间惊人
优化后
func computeMatch(redCount, yellowCount, greenCount, total int) int {
if redCount < 0 || yellowCount < 0 || greenCount < 0 || total <= 0 || total > redCount+yellowCount+greenCount {
return 0
}
num := 0
for r := 0; r <= redCount; r++ {
if yellowCount+greenCount < total-r {
continue
}
if r == total {
num++
//log.Printf("red:%v yellow:%v green:%v", r, 0, 0)
break
} else if r < total {
for y := 0; y <= yellowCount; y++ {
if r+y == total {
num++
//log.Printf("red:%v yellow:%v green:%v", r, y, 0)
break
} else if r+y < total {
needGreen := total - r - y
if needGreen > 0 && greenCount >= needGreen {
//log.Printf("red:%v yellow:%v green:%v", r, y, total-r-y)
num++
}
}
}
}
}
return num
}
测试结果
func Test_computeMatch(t *testing.T) {
t1 := time.Now()
n := computeMatch(3000,3000,6001,12000)
duration := time.Since(t1)
t.Logf("排列组合数量:%v 时长:%v",n,duration)
}
//结果
排列组合数量:3 时长:106.502µs
总结
- 第一点主要是解题思路,能迅速将问题对应到数学上的排列组合问题,初步思路就是三层for循环
- 第二点就是代码质量问题,因为三层for循环,循环次数累积相乘,当球数很大时,计算所消耗的时间很可怕,虽然只有3种组合方式,所以就针对题目特点,对第二层循环做了优化,第三层循环可以去掉,具体优化看代码,并不复杂
- 第三点就是对函数参数的边界检查,细心