LeetCode-剑指 Offer II 114. 外星文字典,BFS 搜索算法及图的表示

剑指 Offer II 114. 外星文字典
现有一种使用英语字母的外星文语言,这门语言的字母顺序与英语顺序不同。
给定一个字符串列表 words ,作为这门语言的词典,words 中的字符串已经 按这门新语言的字母顺序进行了排序 。
请你根据该词典还原出此语言中已知的字母顺序,并 按字母递增顺序 排列。若不存在合法字母顺序,返回 “” 。若存在多种可能的合法字母顺序,返回其中 任意一种 顺序即可。
字符串 s 字典顺序小于 字符串 t 有两种情况:
在第一个不同字母处,如果 s 中的字母在这门外星语言的字母顺序中位于 t 中字母之前,那么 s 的字典顺序小于 t 。
如果前面 min(s.length, t.length) 字母都相同,那么 s.length < t.length 时,s 的字典顺序也小于 t 。

背景知识

图的概念

图是由节点以及连接这些节点边组成.

图的表示方法

①.邻接矩阵
邻接矩阵是表示图中顶点之间相邻关系的矩阵,对于n个顶点的图而言,矩阵的row和col表示的是0-n-1的n个点。
对包含n个节点的图,可以用二维数组(矩阵)来表示节点之间两两的连接关系

vector<vector<int>> edges;//用二纬数组表示各点之间两两的连通关系
int val = edges[i][j];//表示从节点i到节点j的边,0则表示非连通,有权重时也可以表示权重值

②.邻接列表

邻接矩阵可以方便的得到一个节点到另个节点的关联关系,但是邻接矩阵给每个节点都分配n个边的空间,但实际有许多变是不存在的,会造成空间的浪费。

vector<vector<vector<int>>>edges(n);//无权图时,用n个二维数组构成的vector来存储邻接列表
edges[i][j][0];//表示节点i的第j个连通节点编号
edges[i][j][1];//表示节点i到第j个连通节点边的权重

③.边缘列表
边缘列表是具有连接顶点及其权重的边的集合。

vector<vector<int>> edges;//带权图时,用一系列长度为3的vector构成vector来存储边缘列表
int u = edges[i][0];//第i条边的起点
int v = edges[i][1];//第i条边的终点
int val = edges[i][2];//边 u -> v 的权重

BFS 搜索算法

①.概念
广度优先搜索(BFS)的本质就是从源点出发,按层顺序进行遍历,把每一层的所有节点访问完后再转到下一层;使用队列(queue)来记录将要访问的点,访问完每个点就出队,然后把它的邻近点入队;同时要记录哪些点被访问过以避免重复访问。

②.BFS 搜索算法模板

int depth = 0 // 记录遍历到第几层,当前在第几层则和源点的距离为几
while( !queue.empty())//队列非空
{
    depth++;//
    int queue_size = queue.size();// 当前层的元素个数
    for(int i = 0;i < queue_size;i++)
    {
        cur_node = queue.front();queue.pop();//取出当前节点并从队列中删除
        for node 的所有相邻结点 m:
            if m 未访问过:
                queue.push(m);
                //标记访问状态,一定要在加入队列时就要标记其访问状态
                /*****/
    }
}

题目分析

题目分析

①.两个相邻字符的第一个不相同的字符,构成了一个先后顺序,可视为有向图的一条边。

②.依次遍历和比较列表中的所有相邻字符串,构建图的邻接列表,并记录节点的入度。

③.采用 BFS 算法,入度为 0 的节点先入队。

④.根据题目描述,若前面字符串是后面字符串的前缀,则直接返回空。

代码示例

class Solution {
public:
    string alienOrder(vector<string>& words) {
        
        unordered_map<char,unordered_set<char> > edges;//邻接列表
        unordered_map<char,int> inedg;//节点的入度
        //初始化图的节点
        for( auto & word : words )
        {
            for( auto & c : word)
            {
                if( edges.find(c) == edges.end() )
                {
                    edges[c] = {};
                    inedg[c] = 0;
                }
            }
        }
        //构建邻接列表
        int size = words.size();
        for( int i = 0;i+1 < size;++i)
        {
            auto word_1 = words[i];
            auto word_2 = words[i+1];

            int size_1 = word_1.size();
            int size_2 = word_2.size();

            int j = 0;
            while( j < size_1 && j < size_2 )
            {
                if( word_1[j] != word_2[j] )//第一个不相同的字母
                {
                    if( edges[ word_1[j] ].find(word_2[j]) == edges[ word_1[j] ].end() )
                    {
                        edges[ word_1[j] ].insert( word_2[j] );//可访问节点增加
                        inedg[word_2[j]] ++;//节点入度增加
                        
                    }
                    break;
                }   
                ++j;
            }
            if( j == size_2 && j < size_1) return "";// 前面字符串长度大于后面字符串
        }

        queue<char> m_queue;
        for( auto & edg : inedg )
        {
            if( edg.second == 0 ) m_queue.push( edg.first );
        }

        string res = "";
        while( !m_queue.empty())
        {
            auto u = m_queue.front();m_queue.pop();
            res.push_back(u);
            for( auto v : edges[u])
            {
                if( --inedg[v] == 0)
                {
                    m_queue.push(v);
                }
            }
        }
        if( res.size() != edges.size() ) return "";
        return res;

    }
};

在这里插入图片描述

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值