【PHP解法==LeetCode队列-广度优先遍历】279. 完全平方数 && 127.单词接龙 && 126.单词接龙 II

目录

279. 完全平方数

127.单词接龙

126.单词接龙 II


广度优先遍历可以解决:无权图的最短路径等问题

以下的三道题,没有直接给一个图进行遍历,因此我们需要对题目进行分析,从而转化为图的思想去进行bfs或dfs

279. 完全平方数

给定正整数 n,找到若干个完全平方数(比如 1, 4, 9, 16, ...)使得它们的和等于 n。你需要让组成和的完全平方数的个数最少。

示例 1:

输入: n =12
输出: 3 
解释: 12 = 4 + 4 + 4.

示例 2:

输入: n =13
输出: 2
解释: 13 = 4 + 9.

对于这道题,可能第一直觉会是贪心算法,但是举个栗子。

(1)12=9+1+1+1       (2)12=4+4+4       明显2的解法更为准确

因此需要对该问题进行建模,将整个问题转化为一个图论的问题

(1)从n到0,每个数字表示一个节点

(2)如果两个数字x到y相差了一个完全平方数,则连接一条边

(3)则可以得到一个无权图,原问题转为求这个无权图中从n到0的最短路径

如下图:构建图的过程

(1)4到3相差1,3到2相差1,2到1相差1,1到1相差1         4到0相差4,为2的平方数

(2)依次类推,得到第二张图9到0相差3的平方数,9到5相差2的平方数,9到8相差1的平方数

class Solution {
    function numSquares($n) {
        //bfs,广度优先遍历
        $queue = [];            //初始化队列
        $queue[] = [$n,0];      //[数字,步数]
        $visited = [];          //记录已经访问的节点,防止下面多次访问
        $visited[$n] = true;
        while (!empty($queue)) {
            $node = array_shift($queue);        //依次出队进行分析
            $num = $node[0];                    //数字
            $step = $node[1];                   //步数
            for($i = 1; ;$i++){
                $res = $num-$i*$i;              //得出相差弹出元素某个平方数的结果
                if($res < 0){ break; }          //若该结果小于0,则已超出范围
                if($res == 0){ return $step+1; }//等于0,则得到最终答案
                if(empty($visited[$res])){      //如果该结果已经被访问到,则表示到达该结果可以有更短的路径
                    $queue[] = [$res,$step+1];  //如果该结果还没有被访问到,则进行入队
                    $visited[$res] = true;      //记录该结果被访问到
                }
            }
        }   
    }
}

127.单词接龙

给定两个单词(beginWord 和 endWord)和一个字典,找到从 beginWord 到 endWord 的最短转换序列的长度。转换需遵循如下规则:

  1. 每次转换只能改变一个字母。
  2. 转换过程中的中间单词必须是字典中的单词。

说明:

  • 如果不存在这样的转换序列,返回 0。
  • 所有单词具有相同的长度。
  • 所有单词只由小写字母组成。
  • 字典中不存在重复的单词。
  • 你可以假设 beginWord 和 endWord 是非空的,且二者不相同。

示例 1:

输入:
beginWord = "hit",
endWord = "cog",
wordList = ["hot","dot","dog","lot","log","cog"]
输出: 5
解释: 一个最短转换序列是 "hit" -> "hot" -> "dot" -> "dog" -> "cog",
     返回它的长度 5。

示例 2:

输入:
beginWord = "hit"
endWord = "cog"
wordList = ["hot","dot","dog","lot","log"]
输出: 0
解释: endWord "cog" 不在字典中,所以无法进行转换。

对该问题进行建模,将整个问题转化为一个图论的问题

        (1)每个单词表示一个节点

        (2)如果两个单词相差了一个字母,则连接一条边

        (3)则可以得到一个无权图,原问题转为求这个无权图中从beginWord到endWord的最短路径

解法:

        (1)初始化:初始化条件判断,初始化构建图,构建队列,构建距离结果数组

        (2)依次出队,进行广度遍历,将访问过的节点记录到距离结果数组中,并入队

        (3)循环进行,直到找到endWord,返回最终答案

class Solution {
    function ladderLength($beginWord, $endWord, $wordList) {
        if(!in_array($endWord,$wordList)) return 0;                     //如果字典中没有结束单词,则永远也找不到结果,返回0
        $graph = $this->constructGraph($beginWord,$wordList,$graph);    //构建图
        $distance = [];                                                 //记录到某个单词的最短距离,也可当做记录某节点是否被访问
        $distance[$beginWord] = 1;                                      //初始化开始单词的距离
        $queue= [$beginWord];                                           //初始化队列
        while (!empty($queue)) {
            $curWord = array_shift($queue);                         //依次出队进行分析
            foreach ($graph[$curWord] as $v) {                      //只分析与curWord相差1位的单词
                if($v == $endWord) return $distance[$curWord]+1;    //如果已经找到了endWord,则程序结束,返回结果
                if(!isset($distance[$v])){                          //判断是否已经记录了某单词,没有则进行后续操作
                    $distance[$v] = $distance[$curWord]+1;          //该单词是curWord下一步的操作
                    $queue[] = $v;                                  //入队
                }
            }
        }
        return 0;
    }
    /**
     * 初始化构建邻接矩阵,图,以便进行后续的广度遍历
     */
    public function constructGraph($beginWord,$wordList){
        if(!in_array($beginWord,$wordList))
            $wordList[] = $beginWord;
        $graph = [];
        $len = count($wordList);
        //依次取出进行比较,压入结果图中
        for($i=0;$i<$len;$i++){
            for($j=$i+1;$j<$len;$j++){
                if($this->compareWord($wordList[$i],$wordList[$j])){
                    $graph[$wordList[$i]][] = $wordList[$j];
                    $graph[$wordList[$j]][] = $wordList[$i];
                }
            }
        }
        return $graph;
    }
    /**
     * 比较两个单词是否相差一位
     */
    public function compareWord($a,$b){
        $len = strlen($a);          //题目规定单词长度一直,因此只需得一个单词的长度即可
        $different = 0;             //比较过程,每一个字母进行比较
        for($i = 0;$i<$len;++$i){
            if($a[$i] != $b[$i]){
                $different++;
            }
        }
        return $different == 1;     //返回是否相差1个字母
    }
}

126.单词接龙 II

给定两个单词(beginWord 和 endWord)和一个字典 wordList,找出所有从 beginWord 到 endWord 的最短转换序列。转换需遵循如下规则:

  1. 每次转换只能改变一个字母。
  2. 转换过程中的中间单词必须是字典中的单词。

说明:

  • 如果不存在这样的转换序列,返回一个空列表。
  • 所有单词具有相同的长度。
  • 所有单词只由小写字母组成。
  • 字典中不存在重复的单词。
  • 你可以假设 beginWord 和 endWord 是非空的,且二者不相同。

示例 1:

输入:
beginWord = "hit",
endWord = "cog",
wordList = ["hot","dot","dog","lot","log","cog"]
输出:
[
  ["hit","hot","dot","dog","cog"],
  ["hit","hot","lot","log","cog"]
]

示例 2:

输入:
beginWord = "hit"
endWord = "cog"
wordList = ["hot","dot","dog","lot","log"]
输出: []
解释: endWord "cog" 不在字典中,所以不存在符合要求的转换序列。

与127的区别,127只需得出最短的长度,126需要保存所有最短的序列,所以需要用其他方式或数据结构保存结果序列

因此,构建图的过程同126一样

解法:bfs+dfs

        (1)初始化条件判断,endWord是否在结果数组中

        (2)构建无权图-邻接矩阵

        (3)进行广度优先遍历bfs,得到距离记录数组

        (4)进行深度优先遍历dfs,得到最终结果数组

class Solution {
    function findLadders($beginWord, $endWord, $wordList) {
        if(!in_array($endWord,$wordList)) return [];                         //如果字典中没有结束单词,则永远也找不到结果,返回空数组
        $graph = $this->constructGraph($beginWord,$wordList);                //构建图
        $distance = $this->bfs($beginWord,$endWord,$graph);                  //进行广度优先遍历获得距离结果数组(从beginWord到endWord的距离记录数组)
        $res = [];                                                           //初始化最终返回结果数组
        $this->dfs($beginWord,$endWord,$graph,$distance,[$beginWord],$res);  //深度优先遍历
        return $res;
    }
    /**
     * 初始化构建邻接矩阵,图,以便进行后续的广度遍历
     */
    public function constructGraph($beginWord,$wordList){
        if(!in_array($beginWord,$wordList))
            $wordList[] = $beginWord;
        $graph = [];
        $len = count($wordList);
        //依次取出进行比较,压入结果图中
        for($i=0;$i<$len;$i++){
            for($j=$i+1;$j<$len;$j++){
                if($this->compareWord($wordList[$i],$wordList[$j])){
                    $graph[$wordList[$i]][] = $wordList[$j];
                    $graph[$wordList[$j]][] = $wordList[$i];
                }
            }
        }
        return $graph;
    }
    /**
     * 广度优先遍历获得距离记录数组
     */
    public function bfs($beginWord,$endWord,$graph){
        $queue = [$beginWord];
        $distance = [];                                     //记录到某个单词的最短距离,也可当做记录某节点是否被访问
        $distance[$beginWord] = 0;                          //初始化开始单词的距离
        while (!empty($queue)) {
            $curWord = array_shift($queue);                 //依次出队进行分析
            foreach ($graph[$curWord] as $v) {              //只分析与curWord相差1位的单词
                if(!isset($distance[$v])){                  //判断是否已经记录了某单词,没有则进行后续操作
                    $distance[$v] = $distance[$curWord]+1;  //该单词是curWord下一步的操作
                    if($v == $endWord) return $distance;    //如果已经访问到endWord,则可直接返回距离记录数组
                    $queue[] = $v;                          //入队
                }
            }
        }
        return $distance;
    }
    /**
     * 深度优先遍历获得最终返回数组
     * $res采用引用调用,方便进行赋值,同时也优化代码,无需返回数组
     */
    public function dfs($curWord,$endWord,$graph,$distance,$path,&$res){
        if($curWord == $endWord){
            $res[] = $path;     //当找到endWord时,即可以将整个path压入结果数组中
        }else{
            foreach ($graph[$curWord] as $v) {                           //只分析与curWord相差1位的单词
                if($distance[$v] == $distance[$curWord] + 1){            //判断该单词是否是curWord的下一步单词
                    $path[] = $v;                                        //是则将该单词压入path中
                    $this->dfs($v,$endWord,$graph,$distance,$path,$res); //继续深度遍历
                    array_pop($path);                                    //弹出压入的单词,以便后序压入新的path
                }
            }
        }
    }
    /**
     * 比较两个单词是否相差一位
     */
    public function compareWord($a,$b){
        $len = strlen($a);          //题目规定单词长度一直,因此只需得一个单词的长度即可
        $different = 0;             //比较过程,每一个字母进行比较
        for($i = 0;$i<$len;++$i){
            if($a[$i] != $b[$i]){
                $different++;
            }
        }
        return $different == 1;     //返回是否相差1个字母
    }
}

 

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值