华为机试题刷题——dfs题型(塔子哥网站整理)

✊✊✊🌈大家好!本篇文章将较详细介绍两道考点为dfs的华为机试题目。代码语言为:C++代码😇。


2022.9.23 T1 最佳检测顺序

🔒1、题目:

塔子哥有一家云存储服务提供商,他们家的核心产品是一个可扩展的分布式存储系统。他们的客户使用他们的服务来存储和管理各种数据,包括文档、图片、视频等。由于客户对数据的可靠性和可用性要求非常高,他们需要提供高可用性的存储服务,以确保在任何情况下都能保持服务的可用性。

为了实现高可用性,他们使用了主备模式来管理他们的存储系统。当主节点发生故障时,系统会自动将业务切换到备用节点。为了保证存储系统的稳定性,他们需要及时检测服务状态,并在必要时触发主备切换。

在存储系统中,不同的服务之间存在 依赖关系 ,每个服务最多只会依赖一个其他服务并且保证依赖不成环。例如,某些服务可能需要访问其他服务的数据才能正常工作。因此,当某个服务发生故障时,它所依赖的服务也会受到影响,可能导致更多的服务发生故障。

为了最大限度地减少服务故障对业务的影响,他们需要优先检测对业务影响大的服务,并按照 节点编号升序编排 检测顺序,现在请你帮忙解决一下这个问题。

注意: 如果业务影响相同时,则按节点编号大小升序编排。
在这里插入图片描述
🌲 示例 🌲:

输入
	5
	-1 -1 1 2 3
输出
	1 2 3 0 4

解释:服务0、1没有依赖,2依赖1,3依赖2,4依赖3。此时2、3、4业务影响都是1,0、1为0。按序号排为1 2 3 0 4。

☀️2、思路:

简单dfs+排序
根据题目内容,我们可以提取3个关键信息:
1.一个点发生故障影响的是它的后继节点
2.后继节点越多,影响越大
3.影响相同的情况下,编号小的排前面

一种可行的做法就是:
先对每个点dfs一下,求每个点的后继结点个数。接着对节点按后继结点个数降序排序,相同的按编号升序排序。

细节

1.利用vector e[N]存储节点i的子节点,后续dfs时可以遍历e[N]。
2.最初将ans[i]初始化为i,是为了后面更好输出。利用cmp排序对ans数组进行排序,之后直接按序输出ans数组即为答案。

int ans[N];
bool cmp(int& x, int& y) {		//重写排序函数,按照子树中节点个数排序,相等时按大小排序
	if (cnt[x] == cnt[y]) return x < y;
	return cnt[x] > cnt[y];
}

for (int i = 0; i < n; i++) {
		ans[i] = i;
		if (father[i] == -1)
			dfs(i);
	}
sort(ans, ans + n, cmp);

🔑3、代码:

#include<bits/stdc++.h>
#include<iostream>
using namespace std;
const int N = 1e5 + 10;
int n;
int father[N];				//节点i的父节点
vector<int> e[N];		//节点i的子节点
int cnt[N];				//节点i的子树中的节点个数

int dfs(int s)		//计算s的 子树中的节点个数
{
	cnt[s] = 1;		//s本身
	for (auto g : e[s])	//遍历s的每个子节点
	{
		cnt[s] += dfs(g);	//递归计算每个子节点的子树的节点个数,然后加到s上
	}
	return cnt[s];		//返回s的子树的节点个数
}


int ans[N];
bool cmp(int& x, int& y) {		//重写排序函数,按照子树中节点个数排序,相等时按大小排序
	if (cnt[x] == cnt[y]) return x < y;
	return cnt[x] > cnt[y];
}
int main() {
	cin >> n;
	for (int i = 0; i < n; i++) {
		cin >> father[i];
		if (father[i] != -1) {
			e[father[i]].emplace_back(i);
		}
	}
	for (int i = 0; i < n; i++) {
		ans[i] = i;
		if (father[i] == -1)
			dfs(i);
	}
	sort(ans, ans + n, cmp);
	for (int i = 0; i < n; i++) {
		cout << ans[i] << " \n"[i + 1 == n];	//输出," \n"[i+1==n]代表最后一次输出回车,前面输出空格分割
	}
	return 0;
}

2022.9.23 T1 最佳检测顺序

🔒1、题目:

塔子哥居住在数据结构之城,如果将这个城市的路口看做点,两个路口之间的路看做边,那么该城市的道路能够构成一棵由市中心路口向城市四周生长的树,树的叶子节点即是出城口。

塔子哥今天想要出城办事,但不巧的是,有几个路口堵车了,塔子哥无法从一个正常的路口前往堵车的路口。假定塔子哥从一个正常的路口出发,请问塔子哥能否顺利出城(到达出城口)?如果可以,请帮塔子哥找到最省油的路径(经过路口最少的路径),否则请输出“ N U L L NULL NULL”。
在这里插入图片描述
🌲 示例 1 🌲:

输入:
	4
	3
	0 1
	0 2
	0 3
	2
	2
	3
输出:
	0->1

说明:
在这里插入图片描述
🌲 示例 2 🌲:

输入:
	7
	6
	0 1
	0 3
	1 2
	3 4
	1 5
	5 6
	1
	4
输出:
	0->1->2

说明:
在这里插入图片描述

☀️2、思路:

题意化简:给定一棵树,其中有些点无法访问。需要找一条从根节点到叶子结点的路径,要求满足长度最短且字典序最小。
直接dfs,在这个过程中存储好已访问的路径。然后遇到叶子结点就用当前路径更新答案路径。

这样做的复杂度是 O ( n + ∑ d e p t h ( l e a f n o d e ) ) O(n+∑depth(leafnode)) O(n+depth(leafnode)),其中第一项来自于dfs的开销,第二项来自于 <在叶子结点的记录答案>的开销。

🔑3、代码:

#include<bits/stdc++.h>
#include <vector>
using namespace std;
int n, m;
vector<vector<int>> edges;
vector<int> blocks;
vector<int> path;  // 当前路径
vector<int> res; // 答案路径
int tmp = INT_MAX;
vector<bool> used; // 标记是否被访问.
bool judge = false;

// idx 为当前所在点 , num 为深度
void dfs(int idx, int num) {
    // 到叶子节点,更新答案
    if (edges[idx].size() == 0) {
        if (num < tmp) {
            tmp = num;
            res = path;
        }
        judge = true;
    }
    //对同一层进行排序。这样保证了最先遇到的最短的答案也是字典序最小的
    sort(edges[idx].begin(), edges[idx].end());
    // 递归
    for (auto& a : edges[idx]) {
        if (blocks[a] == 0 && used[a] == false) {
            used[a] = true;
            path.push_back(a);
            dfs(a, num + 1);
            path.pop_back();
            used[a] = false;
        }
    }
}
int main() {
    // 读入
    cin >> n >> m;
    edges.resize(n + 1);
    for (int i = 0; i < m; i++) {
        int a, b;
        cin >> a >> b;
        edges[a].push_back(b);
    }
    // 标记 不能访问的点
    int k;
    cin >> k;
    blocks.resize(n + 1, 0);
    for (int i = 0; i < k; i++) {
        int k1;
        cin >> k1;
        blocks[k1] = 1;
    }
    used.resize(n + 1, false);
    path.push_back(0);
    used[0] = true;
    // dfs
    dfs(0, 1);
    // 输出
    if (judge) {
        for (int i = 0; i < res.size(); i++) {
            cout << res[i];
            if (i != res.size() - 1) {
                cout << "->";
            }
        }
    }
    else cout << "NULL" << endl;
    return 0;
}
  • 25
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

君莫笑lucky

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

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

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

打赏作者

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

抵扣说明:

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

余额充值