技术提高是一个循序渐进的过程,所以我讲的leetcode算法题从最简单的level开始写的,然后> 到中级难度,最后到hard难度全部完。目前我选择C语言,Python和Java作为实现语言,因为这三种语言还是比较典型的。由于篇幅和> 精力有限,其他语言的实现有兴趣的朋友请自己尝试。初级难度说的差不多的时候,我打算再加点其他内容,我可能会从操作系统到协议栈,从分布式> 聊到大数据框架,从大数据聊到人工智能,... ...。
如果有任何问题可以在文章后评论或者私信给我。
我会持续分享下去,敬请您的关注。
LeetCode 257. 二叉树的所有路径(Binary Tree Paths)
问题描述:
给定一个二叉树,返回所有从根节点到叶子节点的路径。
注: 叶子节点是指没有子节点的节点。
示例:
C语言实现:
很明显我们通过前序遍历就可以求出。
当我们遍历到叶子节点的时候,我们需要知道从根到该节点的路径,所以我们要在遍历的过程中保存经过的节点路径,我们可以通过一个字符指针来保存,我们定义它为tmp。
过程很简单,但是有两个棘手的问题:
- 我们无法知道有多少个路径字符串;
- 我们无法知道每个路径字符串的确切长度;
当然第一个问题等价于有多少个叶子节点。
我们可以通过对树root遍历一次求出叶子节点树来获得,但是我们完全不必这么做。
我更倾向于通过realloc动态调节,而不是通过额外的遍历来先获得叶子节点数。
第二个问题可能才是比较困难的。如果静态给定一个长度,很可能是不够的,因为题目没有限定root的规模以及节点的val的取值。解决方法有两个:
- 先给定一个长度,然后每次判断一下,如果不够再realloc扩展,这种方法显然不太好。
- 比较好的办法是用asprintf函数来代替sprintf函数来填充tmp。
估计知道asprintf函数的朋友不太多,这是一个从BSD中移植到Linux中的函数,它和sprintf功能基本一致,区别是对于第一个参数,asprintf会动态的为其分配空间,以使其足够容纳内容,这意味着第一个参数可以为NULL,也不会存现字符串溢出的问题,更安全。
详细代码如下:
我们定义一个traverse函数来实现遍历,并填充res。
如果是当前节点是叶子节点:则创建路径字符串,并动态扩展res的长度,将新的路径字符串添加到res的结尾。
如果当前节点不是叶子节点:创建临时字符串给tmp。如果存在左右子节点,则将tmp传递过去,继续向下递归,直到碰到叶子节点。
注意第30行,tmp如果仅仅是产生的临时的中间字符串,而不是作为最终要添加到res中的字符串,那么递归回溯的过程一定要释放掉,否则会出现内存泄漏的问题,因为asprintf每次都会动态的为tmp分配内存空间。所以不要认为tmp总是不变的保存在res中的那个。
注意 *returnSize 的初始化一定要在判断root是否为空的前面。我之前就没有注意这个顺序,总是提交不了,当root为空时报运行时错误,就是因为这个问题。
root为空直接返回了,如果 *returnSize 未初始化初始化当然会出错。
Java语言实现:
Java 的实现和C语言的实现类似,只是要简单的多,不用考虑列表和字符串长度的问题。
代码如下:
Python语言实现:
Python 的实现和Java语言的实现类似,但要更简单。
路径我们可以通过列表生成器+递归方式获得。
这里注意函数声明中已经说明,返回的列表中的元素必须是字符串,所以注意第14行,root.val要强制转换成str。否则对于某些case会过不了。
代码如下:
谢谢大家一直以来的关注和支持!
我一直在努力的写好每一篇文章,画好每一份插图。但是作为一个996从业人员,时间精力十分有限。所以针对评论部分,以后只回答粉丝的问题和私信。希望仅仅是路过的朋友能够体谅,希望更多人关注《吾是我师》,谢谢!