【Py/Java/C++三种语言OD2023C卷真题】20天拿下华为OD笔试之【BFS】2023C-查找一个有向网络的头节点和尾节点【欧弟算法】全网注释最详细分类最全的华为OD真题题解

文章目录

  • 题目描述与示例
    • 题目描述
    • 输入描述
    • 输出描述
    • 备注
    • 示例一
      • 输入
      • 输出
      • 说明
  • 解题思路
    • 头节点的判断
    • 尾节点的判断
    • 环的判断
  • 代码
    • Python
    • Java
    • C++
    • 时空复杂度
  • 华为OD算法/大厂面试高频题算法练习冲刺训练

题目描述与示例

题目描述

给定一个有向图,图中可能包含有环,有向边用两个节点表示。第一个整数表示起始节点,第二个整数表示终止节点,如0 1表示存在从01的路径。每个节点用正整数表示,求这个数据的头节点与尾节点,题目给的用例会是一个头节点,但可能存在多个尾节点。同时,图中可能含有环,如果图中含有环,返回-1

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

说明:入度为0是头节点,出度为0是尾节点

输入描述

第一行为后续输入的键值对数量N >= 0,第二行为2N个数字。每两个为一个起点,一个终点。

输出描述

输出一行头节点和尾节点。如果有多个尾节点,按从大到小的顺序输出。

备注

如果图有环,输出为-1

所有输入均合法,不会出现不配对的数据

示例一

输入

4
1 2 1 3 2 4 3 4

输出

1 4

说明

该例子表示以下有向图。头节点为1,尾节点为4

【Py/Java/C++三种语言OD2023C卷真题】20天拿下华为OD笔试之【BFS】2023C-查找一个有向网络的头节点和尾节点【欧弟算法】全网注释最详细分类最全的华为OD真题题解_第1张图片

解题思路

本题就是拓扑排序模板题。

假设输入确认没有环,那么题目的提示已经告诉了我们应该如何找到头节点和尾节点。

说明:入度为0是头节点,出度为0是尾节点

由于进行拓扑排序之前需要同时构建邻接表和入度****哈希表,即

neighbor_dic = defaultdict(list)
indegree_dic = defaultdict(int)

for i in range(0, 2*n, n):
    a, b = lst[i], lst[i+1]
    indegree_dic[b] += 1
    neighbor_dic[a].append(b)

则无需再额外构建出度数组。邻接表可以起到出度数组的作用。

头节点的判断

如果某一个节点k位于邻接表的key中(说明存在以k为起始节点的边),但其入度为0(说明不存在以k为终止节点的边),则说明k是一个头节点。即

head_node = -1
for k in neighbor_dic.keys():
    if k not in indegree_dic:
        head_node = k
        break

尾节点的判断

如果某一个节点k入度不为0(说明存在以k为终止节点的边),但其不位于邻接表的key中(说明不存在以k为起始节点的边),则说明k是一个尾节点。即

tail_nodes = list()
for k in indegree_dic.keys():
    if k not in neighbor_dic:
        tail_nodes.append(k)

环的判断

头节点和尾节点非常容易直接通过邻接表和入度哈希表计算得到。但本题的核心在于判断该有向图是否存在环。环的判断可以直接使用拓扑排序来进行。如果

  • 该有向图可以完成拓扑排序,则说明不存在环,输出相应的头节点和尾节点。
  • 该有向图不能完成拓扑排序,则说明存在环,输出-1

故设计函数check()来检查所给定的有向图是否能够成功完成拓扑排序

def check(head_node, neighbor_dic, indegree_dic):
    q = deque()
    q.append(head_node)
    while q:
        cur_node = q.popleft()
        for nxt_node in neighbor_dic[cur_node]:
            indegree_dic[nxt_node] -= 1
            if indegree_dic[nxt_node] == 0:
                q.append(nxt_node)
    return all(v == 0 for v in indegree_dic.values())

其中,完成拓扑排序的标准是:BFS结束之后,所有节点的入度均降为0

代码

Python

# 题目:【BFS】2023C-查找一个有向网络的头节点和尾节点
# 分值:200
# 作者:闭着眼睛学数理化
# 算法:BFS/拓扑排序
# 代码看不懂的地方,请直接在群上提问


from collections import deque, defaultdict


# 检查给定数据能够完成拓扑排序(是否存在环)的函数
def check(head_node, neighbor_dic, indegree_dic):
    # 构建队列
    q = deque()
    q.append(head_node)
    # 进行BFS
    while q:
        # 弹出当前队头节点cur_node
        cur_node = q.popleft()
        # 考虑cur_node的所有近邻节点nxt_node
        for nxt_node in neighbor_dic[cur_node]:
            # 近邻节点的入度-1
            indegree_dic[nxt_node] -= 1
            # 如果近邻节点的入度降为0,将其加入队列中
            if indegree_dic[nxt_node] == 0:
                q.append(nxt_node)

    # 如果最终所有节点的入度均为0,即indegree_dic.values()均为0
    # 则说明成功完成拓扑排序,有向图中不存在环
    return all(v == 0 for v in indegree_dic.values())


# 输入边数n
n = int(input())
# 输入2n个数据,每2个节点一组,一共有n组数据
lst = list(map(int, input().split()))

# 构建邻接表
neighbor_dic = defaultdict(list)

# 构建入度哈希表(因为节点的个数不确定,因此用哈希表而不是数组)
indegree_dic = defaultdict(int)

# 每两个一组,取lst中的数据
for i in range(0, 2*n, 2):
    # a为该条边的起始节点,b为该条边的终止节点
    # 即存在a指向b
    a, b = lst[i], lst[i+1]
    # 节点b的入度+1
    indegree_dic[b] += 1
    # 节点a的邻接表延长
    neighbor_dic[a].append(b)

head_node = -1
# 遍历邻接表中的所有起始节点k
# 如果k不存在于入度哈希表中
# 说明k的入度为0,即k是头节点
for k in neighbor_dic.keys():
    if k not in indegree_dic:
        # 由于题目明确只存在一个头节点
        # 因此可以直接退出循环
        head_node = k
        break

# 构建尾节点序列
tail_nodes = list()
# 遍历入度哈希表中的所有节点k
# 如果k不存在于邻接表中
# 说明k不是任何一条边的起始节点,即k是一个尾节点
for k in indegree_dic.keys():
    if k not in neighbor_dic:
        tail_nodes.append(k)

# 尾节点数组进行排序
tail_nodes.sort()

# 调用check()函数,进行拓扑排序
# 如果能够顺利完成拓扑排序,则说明图中不存在环,直接输出头节点和若干尾节点
# 否则输出-1
print(head_node, *tail_nodes) if check(head_node, neighbor_dic, indegree_dic) else print(-1)

Java

import java.util.*;

public class Main {
    public static boolean check(int headNode, Map<Integer, List<Integer>> neighborDic, Map<Integer, Integer> indegreeDic) {
        Queue<Integer> q = new LinkedList<>();
        q.offer(headNode);
        
        while (!q.isEmpty()) {
            int curNode = q.poll();
            
            for (int nxtNode : neighborDic.getOrDefault(curNode, new ArrayList<>())) {
                indegreeDic.put(nxtNode, indegreeDic.get(nxtNode) - 1);
                if (indegreeDic.get(nxtNode) == 0) {
                    q.offer(nxtNode);
                }
            }
        }
        
        for (int value : indegreeDic.values()) {
            if (value > 0) {
                return false;
            }
        }
        
        return true;
    }

    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        int n = scanner.nextInt();
        int[] lst = new int[2 * n];
        for (int i = 0; i < 2 * n; i++) {
            lst[i] = scanner.nextInt();
        }
        
        Map<Integer, List<Integer>> neighborDic = new HashMap<>();
        Map<Integer, Integer> indegreeDic = new HashMap<>();
        
        for (int i = 0; i < 2 * n; i += 2) {
            int a = lst[i], b = lst[i + 1];
            indegreeDic.put(b, indegreeDic.getOrDefault(b, 0) + 1);
            neighborDic.computeIfAbsent(a, k -> new ArrayList<>()).add(b);
        }
        
        int headNode = -1;
        for (int node : neighborDic.keySet()) {
            if (!indegreeDic.containsKey(node)) {
                headNode = node;
                break;
            }
        }
        
        List<Integer> tailNodes = new ArrayList<>();
        for (int node : indegreeDic.keySet()) {
            if (!neighborDic.containsKey(node)) {
                tailNodes.add(node);
            }
        }
        Collections.sort(tailNodes);
        
        if (check(headNode, neighborDic, indegreeDic)) {
            System.out.print(headNode + " ");
            for (int node : tailNodes) {
                System.out.print(node + " ");
            }
            System.out.println();
        } else {
            System.out.println(-1);
        }
    }
}

C++

#include 
#include 
#include 
#include 
#include 
#include 
using namespace std;

bool check(int headNode, unordered_map<int, vector<int>>& neighborDic, unordered_map<int, int>& indegreeDic) {
    queue<int> q;
    q.push(headNode);
    
    while (!q.empty()) {
        int curNode = q.front();
        q.pop();
        
        for (int nxtNode : neighborDic[curNode]) {
            indegreeDic[nxtNode]--;
            if (indegreeDic[nxtNode] == 0) {
                q.push(nxtNode);
            }
        }
    }
    
    for (auto entry : indegreeDic) {
        if (entry.second > 0) {
            return false;
        }
    }
    
    return true;
}

int main() {
    int n;
    cin >> n;
    vector<int> lst(2 * n);
    for (int i = 0; i < 2 * n; i++) {
        cin >> lst[i];
    }
    
    unordered_map<int, vector<int>> neighborDic;
    unordered_map<int, int> indegreeDic;
    
    for (int i = 0; i < 2 * n; i += 2) {
        int a = lst[i], b = lst[i + 1];
        indegreeDic[b]++;
        neighborDic[a].push_back(b);
    }
    
    int headNode = -1;
    for (auto entry : neighborDic) {
        if (indegreeDic.find(entry.first) == indegreeDic.end()) {
            headNode = entry.first;
            break;
        }
    }
    
    vector<int> tailNodes;
    for (auto entry : indegreeDic) {
        if (neighborDic.find(entry.first) == neighborDic.end()) {
            tailNodes.push_back(entry.first);
        }
    }
    sort(tailNodes.begin(), tailNodes.end());
    
    if (check(headNode, neighborDic, indegreeDic)) {
        cout << headNode << " ";
        for (int node : tailNodes) {
            cout << node << " ";
        }
        cout << endl;
    } else {
        cout << -1 << endl;
    }
    
    return 0;
}

时空复杂度

时间复杂度:O(N)

空间复杂度:O(N)


华为OD算法/大厂面试高频题算法练习冲刺训练

  • 华为OD算法/大厂面试高频题算法冲刺训练目前开始常态化报名!目前已服务100+同学成功上岸!

  • 课程讲师为全网50w+粉丝编程博主@吴师兄学算法 以及小红书头部编程博主@闭着眼睛学数理化

  • 每期人数维持在20人内,保证能够最大限度地满足到每一个同学的需求,达到和1v1同样的学习效果!

  • 60+天陪伴式学习,40+直播课时,300+动画图解视频,300+LeetCode经典题,200+华为OD真题/大厂真题,还有简历修改、模拟面试、专属HR对接将为你解锁

  • 可上全网独家的欧弟OJ系统练习华子OD、大厂真题

  • 可查看链接 大厂真题汇总 & OD真题汇总(持续更新)

  • 绿色聊天软件戳 od1336了解更多

你可能感兴趣的:(最新华为OD真题,#,BFS,算法,java,c++,python,华为od,宽度优先)