校招面试精选「字节」

 前言

        这次字节的面试更关注项目经历、算法能力,包括基础知识也是以综合的形式进行考察,难度较大。

知识点

1. 手撕算法:最少解开密码锁次数

        这道题是力扣1878分的中等题,题意是求将“0000”的数字串密码锁转到某个目标串解锁的最小次数,每次只能转一位数字,0和9可以互转,同时给定一些不能经过的数字字符串。

水平比较好的同学很自然会想到标准做法,并且会往启发式的方向思考。但面试时因为紧张等因素可能一下子脑子短路,这里给出一上来没有思路时应该怎么想,面试官一般也是这样去引导的,在你写完之后一般也会让你说说自己的思路。这部分熟练可以选择跳过,文末有Leetcode链接。

        在没做过这道题的情况下如何得出思路?由题意可知只有“0000”到“9999”一共10^4种状态,要求最小的变换次数,先从暴力枚举的角度考虑,找出所有的变换路径,取最小值,能否可行?来看单条路径怎么找,模拟变换的过程,第一次变换,四个位置分别正反转,共8种可能转法,除去不能经过的串后,剩下的串都是下一步的可能答案,自然,下下步的可能答案都建立在这一步的可能答案基础上。

        这时候来到一个关键点了,也是一个比较容易想的点。纯暴力去搜索的话,第一步8种转法,第二步也8种转法,每一步都8种转法,假如解锁要转n次,那搜索的范围就是8的n次方,8是2的三次方,8的n次方就是2的3n次方,时间限制1秒计算机大概能执行10^7到10^8次基本操作。通过换算大致估计一下1秒计算机实际只能算不到10步,而“0000”转到“5555”正常就要转20次,所以这种纯暴力一定是不行的。

        “转”这个动作没有什么优化的空间,所以只能在搜索的范围上进行优化,那么如上所述考虑这样一棵“八叉树”,有没有哪些分叉是可以剪枝的呢?其实在模拟的时候你应该就想到了,第二次转有一种情况会转回去,比如“1000”复原到“0000”,所以实际上这棵搜索树并不是“八叉”的。进一步思考,这棵树也不是“七叉”的,其实包含了大量的重复遍历,比如“0101”就既可以是“0001”的子节点也可以是“0100”的子节点,而“0101”又会分化出“0001”和“0100”,又回到第一步去了。那有什么办法可以防止它遍历到重复的节点呢?这时记忆化搜索就呼之欲出了。

        记忆化搜索最简单的操作就是维护一个表,走过的节点就记下,下次碰到就知道走没走过了,由于搜索过程与顺序无关,肯定是使用无序表,或者说哈希表来记忆,以免增加不必要的时间开销。至此,脑海里应该已经建立起一棵搜索树了,目标串就在树上某个节点,那么确定了路径之后,应该写深度优先还是广度优先的搜索呢?

        由于使用了记忆化避免重复走一个节点,也就是说目标节点只会被遍历到一次,既然只遍历到一次,那也就没有开头“找出所有的变换路径,取最小值”的这种说法了。而bfs刚好就是在第一次遍历到的时候给出答案这样一种做法。但还是要明确,变换路径并不能保证是唯一的,为什么bfs第一次遍历到的时候就能给出答案呢?它本质上其实和Dijkstra算法是一个道理,只是这里没有权重或者说权重都为1,从树的bfs角度也很好理解,每一层的变换步数是一样的,一层一层遍历下去,下一层是上一层状态的延伸并且步数+1,这个步数都是最少要走的步数,数学归纳法或者反证法都可以证明。

        剩下的就是一些特判了,以下是示例代码,用了C++比较新的语法,关键地方加了注释,可以发现正如我们分析的那样,如果把代码中的queue换成priority_queue,就很像Dijkstra算法了:

class Solution {
public:
    int openLock(vector<string>& deadends, string target) {
        if (target == "0000") { // 特判,类似根节点为空直接返回
            return 0;
        }

        unordered_set<string> dead(deadends.begin(), deadends.end());
        if (dead.count("0000")) {// 特判,死节点一定到不了
            return -1;
        }

        auto num_prev = [](char x) -> char { //往前转
            return (x == '0' ? '9' : x - 1);
        };
        auto num_s
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值