二叉树不是树的特殊形式_先锋阅享 | 就业加油站(14)—— 链表与树在线剖析...

6f8269ea8fe8a436ce9f91a8292a30a6.png点击上方“蓝字”,关注我们

1fb82177fd318d5463f810cdd8d4d71f.png

OMG!媒媒这次又带着大家心心念念的重点来了呢。这次是由闫文鑫同学给我们带来的关于链表与树的面试题,这里可都是满满的干货呢,还在等什么,快拿出小本本记下来吧。

7b90e047ed3fc56e992d3ca878e2fdf5.png 67fd19938c1c77658760a4d7f55aef52.png 面试题1:合并两个排序的链表

输入两个递增排序的链表,合并这两个链表并使新链表中的结点仍然是按照递增排序的。例如输入下图中的链表1和链表2,则合并之后的升序链表如链表3所示。链表结点定义如下:

7cc386e751eed99c15bf93b46966224b.png

1.易犯错误:

1.写代码之前没有对合并过程想清楚,最终合并出来的链表要么中间断开了,要么没有做到递增排序

2.代码在鲁棒性方面存在问题。程序一旦有特殊的输入(如空链表)就会崩溃。

2.解决方法:

首先,分析合并两个链表的过程。链表1的头结点的值小于链表2的头结点的值,因此链表1的头结点将是合并后链表的头结点(如下图)

5c8884127937081e07387b468eec1bae.png

两个链表中剩下的结点依然是按照前面的步骤进行逐步排序链接。我们比较两个链表的头结点的值,此时链表2的头结点的值将是合并剩余结点得到的链表的头结点。我们把这个结点和前面合并链表时得到的链表的尾结点(值为1的结点)链接起来。接下来的结点重复上述步骤。这就是典型的递归的过程,我们可以定义递归函数完成这一合并过程。

接下来我们解决鲁棒性的问题。在本题中一旦输入空链表就会引入空的指针,因此我们要对空链表单独处理。当第一个链表是空链表,也就是它的头结点是一个空指针时,那么把它和第二个链表合并,显然合并的结果就是第二个链表。同样,当第二个链表是空链表,也就是它的头结点是一个空指针时,那么把它和第一个链表合并,显然合并的结果就是第一个链表。如果两个链表都是空链表,合并的结果是得到一个空链表。

3.参考代码:

我们想清楚合并过程,并且知道那些输入可能会引起鲁棒性问题之后,就可以动手写代码了。

786f0ca40daadf7134d7be0e799ce5c8.png

4.测试用例:

1.功能测试(输入的两个链表有多个结点,结点的值互不相同或者存在值相等的多个结点)。

2.特殊输入测试(两个链表的一个或者两个头结点为NULL指针、两个链表中只有一个结点)。

5.本题考点:

1.考查应聘者分析问题的能力。解决这个问题需要大量的指针操作,应聘者如果没有透彻地分析问题形成清晰的思路,那么他很难写出正确的代码。

2.考查应聘者能不能写出鲁棒的代码。由于有大量指针操作,应聘者如何稍有不慎就会在代码中遗留很多与鲁棒性相关的隐患,建议应聘者在写代码之前全面分析哪些情况会引入空指针,并考虑清楚怎么处理这些空指针。

面试题2:树的子结构

题目:输入两颗二叉树A和B,判断B是不是A的子结构。二叉树结点的定义如下:

1b07bfd23f4985cd92f972120c289f5b.png

例如下图中的两科二叉树,由于A中有一部分子树的结构和B是一样的,因此B是A的子结构。

1ee6016d3b61bd285e089561ef2db3ba.png

两棵二叉树A和B,右边的树B是左边的树A的子结构。

1.思路解析:

和链表相比,树中的指针操作更多也更复杂,面对着大量的指针操作,我们要更加小心,否则一不小心就会在代码中留下隐患。要判断树A中是否存在和树B结构一样的子树,我们可以分成两步:第一步在树A中找到和树B的根结点的值一样的结点R;第二步再判断树A中以R为根结点的子树是不是包含和树B一样的结构。

2.分析过程:

以上面两棵树为例来分析这个过程。首先我们试着在树A中找到值为8(树B的根结点的值)的结点。从树A的根结点开始遍历,我们发现他的根结点的值就是8.接着我们就去判断树A的根结点下面的子树是不是含有和树B一样的结构(如下图所示)。在树A中,根结点的左子结点的值是8,而树B的根结点的左子结点是9.对应的两个结点不同。

9be94d51518b61b55c2e29f23fd8a23b.png

因此我们仍然需要遍历树A,接着找值为8的结点。我们在树的第二层中找到了一个值为8的结点,然后进行第二步判断,即判断这个结点下面的子树是否含有和树B一样结构的子树,先后得到两个子结点9和2,这和树B的结构完全相同。此时我们在树A中找到了一个和树B的结构一样的子树。因此,树B是树A的子结构。

39bdf1af73fcff830ebab95ea1b9c9b1.png

3.代码实现:

第一步在树A中查找与根结点的值一样的结点。这实际上就是树的遍历。对二叉树这种数据结构熟悉的知道可以用递归的方法去遍历,也可以用循环的方法去遍历。由于递归的代码实现比较简洁,面试时如没有特殊要求,我们通常都会采用递归的方式。

9c59ed42a8a221b49dc5382a43074c15.png

面试时一定要注意边界条件的检查,即检查空指针。当树A或树B为空的时候,定义相应的输出,如果没有检查并做出相应的处理,程序非常容易崩溃。这是面试时非常忌讳的事情。递归调用HasSubtree遍历二叉树A。如果发现某一结点的值和树B的头结点的值相同,则调用DoesTreeHaveTree2,做第二步判断。

第二步判断树A中以R为根结点的子树是不是和树B具有相同的结构。同样,我们也可以用递归的思路来考虑;如果结点R的值和树B的根结点不相同,则以R为根结点的子树和树B肯定不具有相同的结点;如果它们的值相同,则递归地判断它们各自的左右结点的值是不是相同。递归的终止条件是我们到达了树A或者树B的叶结点。

6d31cca1e2b57095d913c9d7c186595c.png4.本题考点:

注意:上述代码有多处判断一个指针是不是NULL,这样做是为了避免试图访问空指针而造成程序崩溃,同时也设置了递归调用的退出条件。在写遍历树的代码的时候一定要高度警惕,在每一处需要访问地址的时候都要问自己这个地址有没有可能是NULL,如果是,怎么处理。

4.面试小提示:

了确保自己的代码的完整正确,在写出代码之后应聘者至少要用几个测试用例来检验自己的程序;树A和树B的头结点有一个或者两个都是空指针,在树A和树B中所有结点都只有左子结点或者右子结点。树A和树B的结点中含有分叉。只有这样才能写出让面试官满意的鲁棒代码。

5.测试用例:

1.功能测试(树A和树B都是普通的二叉树,树B是或者不是树A的子结构)。

2.特殊输入测试(两棵二叉树的一个或者两个根结点为NULL指针、二叉树的所有结点都没有左子树或者右子树)。

6.本题考点:

1.考查对二叉树遍历算法的理解及递归编程能力。

2.考查代码的鲁棒性。本题的代码中含有大量的指针操作,稍有不慎程序就会崩溃。应聘者需要采用防御性编程的方式,每次访问指针地址之前都要考虑这个指针有没有可能是NULL。

测试用

跟着媒媒看完这些,是不是觉得收获许多呢?这里的知识有没有让大家感到思路十分清晰呢?相信这期的干货已经使大家充满能量,那我们也继续期待下一期媒媒的回归吧!

2f9537dbe2ad8b29eddf032ec55ed502.gif faffdfc6131598d2e5391131f55c6eb0.png 183b5ef6e96293fdb923d1747212c962.png河北师大软件学院融媒体中心扫码关注我们

审核 | 冯斐然

终审 | 闵    杰

河北师范大学软件学院融媒体中心

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值