这些题竟然还可以使用广度优先搜索解决

315 阅读1分钟

广度优先搜索(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;
    }
}

今天先到这里。