广度优先搜索(BFS)竟然还可以这样用????
之前对广度优先搜索的理解一直停留在层次遍历,最近发现竟然还可以做一些求最短路径的事,下面就拿两道题来探探吧。
第一道题是求组成和的完全平方数的个数最少。 讲道理按照我以前的理解,这道题应该是DP的套路,对,是可以用DP搞的。 但是现在BFS也可以搞,BFS适合搞这种最短路径的,因为他是一层一层往外遍历的。 参考这里的总结
BFS的套路就是一个队列,每次遍历一层。然后有一个记录访问过的节点的$uesd数组。
所以这道题就可以看成是从数字n开始,每次可以走平方数步,问最后最少走多少步就可以得到0.
借图一张方便理解:
灰色的节点就是可以跳过的节点了,因为假设前面的某个节点到达了0,那么后面的节点就无需再算了。
class Solution {
/**
* @param Integer $n
* @return Integer
*/
function numSquares($n) {
$step = 0;
$queue = new SplQueue();
$queue->enqueue($n);
$used = array_fill(0, $n + 1, 0);
$used[$n] = 1;
while (!$queue->isEmpty()) {
$size = count($queue);
$step++;
for ($i = 0; $i < $size; $i++) {
$top = $queue->dequeue();
for ($j = 1; $top - $j * $j >= 0; $j++) {
$cur = $j * $j;
$next = $top - $cur;
if ($next == 0) {
return $step;
}
if ($next < 0) {
continue;
}
if ($used[$next] == 0) {
$used[$next] = 1;
$queue->enqueue($next);
}
}
}
}
}
}
其实理解了上面的图和代码后,再来看下面的这道题就完全OK了,一样的思路。
经典的零钱兑换,搁以前的话,只能想到用DP做,现在,想一下,是不是和上面的题一样。
给定一个数amount,每次给定一些选择,问至少需要多少硬币可以凑齐amount。
跟上面的代码一样,不一样的地方就是每次可供选择的数字不一样而已。
class Solution {
/**
* @param Integer[] $coins
* @param Integer $amount
* @return Integer
*/
//试试BFS
function coinChange($coins, $amount) {
if ($amount == 0) {
return 0;
}
$queue = new Splqueue();
$queue->enqueue(0);
$used = array_fill(0, $amount + 1, 0);
$step = 0;
while (!$queue->isEmpty()) {
$step++;
$size = count($queue);
for ($i = 0; $i < $size; $i++) {
$cur = $queue->dequeue();
foreach ($coins as $coin) {
if ($cur + $coin == $amount) {
return $step;
}
if ($cur + $coin < $amount && !$used[$coin + $cur]) {
$used[$coin + $cur] = 1;
$queue->enqueue($cur + $coin);
}
}
}
}
return -1;
}
}
今天先到这里。