广度优先搜索(Breadth First Search) 学习

本文介绍了多种数据结构和算法在实际问题中的应用,包括员工重要性的计算、N叉树的层序遍历、腐烂橘子的扩散模拟、单词接龙的最短转换路径以及最小基因变化。这些问题通过队列实现宽度优先搜索,解决过程中涉及到哈希表、二维数组、状态标记等技巧,展示了算法在解决复杂问题中的高效性和实用性。
摘要由CSDN通过智能技术生成


借助队列其先进先出的特点,将要搜索的放在队尾,他是一种地毯式搜索,所以第一次找到目标,一定是最短路径

员工重要性

在这里插入图片描述

/*
// Definition for Employee.
class Employee {
public:
    int id;
    int importance;
    vector<int> subordinates;
};
*/

class Solution {
public:
    int getImportance(vector<Employee*> employees, int id) {
        unordered_map<int,Employee*> map;
        for(auto& e:employees)
        {
          map[e->id]=e;
        }
        queue<int> q;
        q.push(id);
        int ret=0;
        while(!q.empty())
        {
          int top=q.front();
          q.pop();
          ret+=map[top]->importance;
          for(auto& e:map[top]->subordinates)
          {
              q.push(e);
          }
        }
        return ret;

    }
};

N叉树的层序遍历

和二叉树层序遍历思路一样,只不过他这里的结构体里用数组存储了他的孩子。将孩子一次入队即可。

/*
// Definition for a Node.
class Node {
public:
    int val;
    vector<Node*> children;

    Node() {}

    Node(int _val) {
        val = _val;
    }

    Node(int _val, vector<Node*> _children) {
        val = _val;
        children = _children;
    }
};
*/

class Solution {
public:
    vector<vector<int>> levelOrder(Node* root) {
        vector<vector<int>> vv;
        if(root==NULL)
           return vv;
        queue<Node*> q;
        q.push(root);

        while(!q.empty())
        {
            int size=q.size();
            vector<int> temp;
            while(size--)
            {
            Node* Tnode=q.front();
            q.pop();
            temp.push_back(Tnode->val);
            for(auto& e:Tnode->children)
            {
                if(e)
                  q.push(e);
            }
            }
            vv.push_back(temp);
        }
        return vv;
    }
};

腐烂的橘子

在这里插入图片描述

在这里插入图片描述

这里需要对四周同时污染,所以定义一个二维数组,表示其搜索范围。

class Solution {
public:
    int orangesRotting(vector<vector<int>>& grid) {
        int next[4][2]={{1,0},
                        {-1,0},
                        {0,1},
                        {0,-1}};
        int m=grid.size();
        if(m==0)
          return 0;
        int n=grid[0].size();
        queue<pair<int,int>> q;

        //找到腐烂的橘子入队
          for(int i=0;i<m;i++)
          {
              for(int j=0;j<n;j++)
              {
                  if(grid[i][j]==2)
                  {
                      q.push(make_pair(i,j));
                  }
              }
          }
          int count=0;
          
          while(!q.empty())
          {
             int flag=0;
              //处理当前队列中腐烂的橘子,因为要算出分钟,所以必须用size来保存每一次队列中腐烂的橘子
              //因为这一批腐烂的橘子是同时污染的。
             int size=q.size();
             while(size--)
             {
                pair<int,int> top=q.front();
                q.pop();
                //把它的周围变成腐烂的橘子
                for(int i=0;i<4;i++)
                {
                   int nx=top.first+next[i][0];
                   int ny=top.second+next[i][1];
                   if(nx<0||ny<0||nx>=m||ny>=n||grid[nx][ny]!=1)
                   {
                        continue;
                   }
                   flag=1;
                   grid[nx][ny]=2;
                   //变成腐烂橘子之后入队,腐烂他周围新鲜橘子
                   q.push(make_pair(nx,ny));
                }         
             }
             //队列中当前一批腐烂橘子处理完才++一次
             if(flag)
               count++;
          }
         for(int i=0;i<m;i++)
          {
              for(int j=0;j<n;j++)
              {
                  if(grid[i][j]==1)
                  {
                      return -1;
                  }
              }
          }
          
          return count;
         

    }
};

单词接龙

将所给的单词放入哈希表中作为字典。
需要用标记字典,单词有可能重复。
他们两都为哈希set,查询效率高。

  1. 先入单词到队列中,同时对其做标记
  2. 取出这个单词,用26个字母对齐拼接
  3. 必须是,在字典中且没有被标记过才能入队列,用26个字母试探
  4. 处理完一批在++,因为有可能有一批单词转换

代表样例便是hit->hot。hot可以转换为dot和lot,但是dot转换步数最小。

class Solution {
public:
    int ladderLength(string beginWord, string endWord, vector<string>& wordList) {
        //字典
        unordered_set<string> dict;
        for(auto& e:wordList)
        {
             dict.insert(e);
        }
        //标记
        unordered_set<string> vist;
        vist.insert(beginWord);
        queue<string> q;
        q.push(beginWord);
        int count=1;
        while(!q.empty())
        {
            int size=q.size();
            //对队列中的一批单词进行转换
            while(size--)
            {
            string Tstr=q.front();
            q.pop();
            if(Tstr==endWord)
               return count;
            for(int i=0;i<Tstr.size();i++)
            {
                //写到这里是因为每次对第一位26次试探结束,重新变回原样对第二位进行试探
                //例如第一次 hit从 ait,bit,cit...一直试探,
                string Nstr=Tstr;
                for(char j='a';j<='z';j++)
                {
                    Nstr[i]=j;

                    //存在字典中,且没有被遍历过
                    if(dict.count(Nstr)&&!vist.count(Nstr))
                    {
                      //入队
                      q.push(Nstr);
                      //然后打上标记
                      vist.insert(Nstr); 
                    }            
                }
            }
            }
            count++;
        }
    return 0;

    }
};

最小基因变化

和单词接龙类似,bank为词典,vis我们定义的标记词典。拼接序列是它给定的ACGT

class Solution {
public:
    int minMutation(string start, string end, vector<string>& bank) {
    //词典
    unordered_set<string> dict;
    for(auto& e:bank)
    {
        dict.insert(e);
    }
    //标记
    unordered_set<string> vist;
     vist.insert(start);
     //队列
     queue<string> q;
     q.push(start);
     
     int count=0;
     string str="ACGT";
     while(!q.empty())
     {
         int size=q.size();
         while(size--)
         {
           string Tstr=q.front();
           q.pop();
           if(Tstr==end)
             return count;
           for(int i=0;i<start.size();i++)
           {
               string Nstr=Tstr;
               for(char j=0;j<str.size();j++)
               {
                   Nstr[i]=str[j];
                   //词典包含基因序列,且没被遍历过过才入队
                   if(dict.count(Nstr)&&!vist.count(Nstr))
                   {
                       q.push(Nstr);
                       vist.insert(Nstr);
                   }
               }
           }
         }
         count++;
     }
     return -1;
    }
};

打开转盘锁

字典为死亡字典,之前的逻辑是在字典中且未被标记过才能入队,根据这里的题干要求,等会的逻辑需要变成不在死亡字典,且未被标记过才能入队。
之前的单词接龙和基因变化,需要拼接单词,但是这里的转盘锁,直接在锁的基础上对每一位进行变化,而且还有两种方式,所以需要定义两个临时字符串,一个+,一个-。当9继续++时需要变成0,当0再继续–时需要变成9。

class Solution {
public:
    int openLock(vector<string>& deadends, string target) {
        
        //字典
        unordered_set<string> dict(deadends.begin(),deadends.end());
        //丧心病狂的用例,起始位置直接死亡
        if(dict.count("0000"))
           return -1;
        queue<string> q;
        q.push("0000");
        //标记字典以防重复
        unordered_set<string> vis;
        vis.insert("0000");
        int step=0;
        while(!q.empty())
        {
            int size=q.size();
            while(size--)
            {
                string Tstr=q.front();
                q.pop();
                if(Tstr==target)
                {
                    return step;
                }
                for(int i=0;i<Tstr.size();i++)
                {
                    //因为它有两种变化方式
                    string str1=Tstr;
                    string str2=Tstr;
                    //等于0,--变成9,否则每次减1
                    str1[i]=str1[i]=='0'?'9':str1[i]-1;
                    //等于9,++变成0,否则每次加1
                    str2[i]=str2[i]=='9'?'0':str2[i]+1;

                    //不在死亡字典,且没有被遍历过
                    if(!dict.count(str1) && !vis.count(str1))
                    {
                        q.push(str1);
                        vis.insert(str1);
                    }
                    if(!dict.count(str2) && !vis.count(str2))
                    {
                        q.push(str2);
                        vis.insert(str2);                        
                    }
                }
            }
            step++;
        }
        return -1;

    }
};
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

楠c

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值