二叉树的基础题目学习(EPI)

1.判断是个二叉树是不是平衡二叉树。

二叉树的定义都是利用递归的方法,所以二叉树有着天然的递归属性。所以一般情况下,递归解决二叉树问题中,递归解法比较简洁。平衡二叉树的定义是左子树和右子树均是平衡二叉树,并且左子树和右子树的高度差不超过1,三个条件缺一不可。

根据递归的定义,递归实现起来需要返回子树的高度,又要返回子树是否平衡的属性,所以判断平衡二叉树的递归算法需要传会两个参数,所以把递归函数原型定义为int balancedTree(TreeNode* root, bool &isBalanced)形式,返回值返回高度,其中引用形参也当做返回值代表子树是否平衡,这样实现起来比较容易了。(二叉树中好多算法递归均需要这种返回多个变量的递归写法,可以练习一下。举印象深刻的两个类似的:a.二叉树中最大路径(路径定义从任意一节点至任意另一节点),b.最近公共祖先问题,有更多的欢迎补充)。

一种开阔视野的方法:一种将空间复杂度近一步降低的方法是采用先序遍历,然后利用一个全局变量实时的记录stack的最大深度。《计算机编程艺术》第三卷,排序和查找460页证明了n个节点的平衡树的高度不会超过hn=1.4405lg(n/2+3)-0.3277。如果遍历的时候栈的高度超过了这个高度,则这颗树为不平衡的树。

2.首先,设定一个概念,如果一棵树的左子树的节点数量和右子树的节点数量的差的绝对值不大于k,则该节点不是k平衡的。

题目:给定一个二叉树,找出二叉树中一个不是k平衡的节点,且此节点的后继节点均是k平衡的节点。

递归的查找即可,设定一个全局节点变量,初始化为NULL,递归中寻找到节点之后赋值给全局节点变量,递归函数中如果全局变量不为NULL,直接返回。递归函数返回值为当前树的节点数量即可。当前树的节点的数量=左子树节点数量+右子树节点数量+1,递归结束条件为节点未NULL,返回节点数量0。唯一赋值全局节点变量的地方就是第一次出现左子树与右子树差值绝对值超过k的时候,这时候沿着递归路径一路返回,全局变量及为所求。

3.判断二叉树是不是镜像的。

leetcode中存在原题,比较容易,同样利用递归判断,不过起始条件为左子树节点和右子树节点。递归函数isSymmetric(TreeNode *left, TreeNode *right),首先根节点必须相等,其次必须满足继续递归isSymmetric(left->right,right->let)&&isSymmetric(left->left,right->right),三个条件同时满足,则才能够判断是镜像树。递归代码很简洁。

4.实现一个二叉树加锁的机制,限制条件是如果一个节点的后继节点或者祖先节点已经被锁定,则该节点不能够被锁定。实现isLock(),lock(),和unlock()操作,时间复杂度分别限制为O(1),O(h),O(h),其中h为树的高度。假设此二叉树存在指向父节点的指针。

每个节点添加一个标志位,简单实现O(1)的isLock()操作。

另外就是维护lock(),unlock()的时候维护节点的标志位操作了。加锁和解锁的时候需要考虑祖先,也需要考虑孩子,祖先可以在O(h)的时间复杂度内遍历获取状态。孩子节点如果逐个考虑可能需要O(n)了。所以不能逐个遍历,

这里可以在节点中继续保存一个变量,其为孩子节点是否被锁。该变量在lock的时候向上回溯祖先的时候逐个设置标志位即可。

所以解锁的时候进行相应的操作,解锁的时候需要释放其祖先节点中孩子加锁的标志,并且也需要更改是否锁定的标志。

5.给定一个存在父节点指针的二叉树表示,使用O(1)的额外空间遍历二叉树。(先序,中序,后序),不能修改树的结构。

Morris方法能够O(1)的空间复杂度实现二叉树的遍历,但是遍历过程中需要临时改变树形结构,所以该遍历算法不是线程安全的。

题目存在父节点指针,所以不需要利用栈来进行回溯,可以利用父节点指针回溯,所以可以达到O(1)的时间复杂度。

算法模板参考利用栈实现的后续遍历,设置当前节点指针cur,上一个节点指针pre。然后分三种情况讨论,

pre == NULL || pre->left == cur || pre->right == cur 这种属于traversal down的情况,继续向下遍历即可,临时利用next指针指向下一个节点

cur->left == pre 这种属于刚遍历完左子树,traversal up的情况

cur->right == pre 这种属于刚遍历完右子树,traversal up的情况

6.获得二叉树中序遍历的第k个节点的朴素方法遍历,统计到第k个节点输出即可,时间复杂度O(n)。假如给定的二叉树中每个节点包含其所有子树节点数量和,如何优化寻找第k个节点的算法。时间复杂度可以优化至O(h),h为树的高度。

其实题目思路比较容易获得,快速的缩减问题规模就可以了。查看当前节点的左子树的数量,

如果当前节点左子树的数量等于k,则可知当前节点为查找所需的节点。如果左子树的节点数量大于或等于k,则可以将问题缩小到寻找左子树的第k个节点。

如果当前节点左子树的数量小于k-1 ,则可知第k个节点在右子树中,寻找右子树中第k-(左子树节点数量+1)即可。

7.根据中序遍历和后序或者先序遍历还原二叉树。

leetcode中的题目,思路比较简单,后序/先序可以先确定根节点,根据根节点去分割中序遍历,递归调用左子树和右子树即可。

另外先序和后序无法唯一还原出二叉树,所以这种题目一般就是中序匹配另外一个遍历序。

扩展问题:根据给定的一个数组A,构造max树:max树定义是根节点为A数组中最大值,假设最大值的坐标为m,则左子树右A[0:m-1]构成,右子树由A[m+1,n]构成,递归的利用最大值构造左子树,右子树。设计高效的构造max树的方法。

比较直观的方法每次寻找最大值即可,这样每寻找最大值的时间复杂度为O(n),所以总的时间复杂度为O(n^2)。如果想要优化必须考虑从最大值获取的角度优化。可以分别从左至右,从右至左保存最大值。或者利用单调队列借助栈的功能完成预处理。

8.如果给定一个包含NULL节点的先序遍历结果,能够O(n)还原出二叉树吗?后序和中序呢?

这里借助栈即可,先序和后序,不断的从栈中弹出两个节点与当前节点构成小树压入栈。最后为一个完整的二叉树。

中序的遍历无法唯一还原出二叉树。因为所有树形的遍历结果序列均相同。

可以思考下包含NULL的层序遍历二叉树(也很简单)。

9.将二叉树的所有子节点连接成链表。

递归,然后判断是否子节点,如果子节点连接一下。保存一个全局的头结点变量即可。

10.设计一个输出二叉树外围节点的方法。外围节点的解释:下图需要输出的序列A,B,D,G,J,K,L,M,N,I,F,C

前一个题目已经完成了叶子节点的链表化了,输出也不是困难的事情,所以这个题目的难点在于输出左边外围和右边外围。

刚开始思考能否利用Morris遍历输出外围节点,后来画一个非完全二叉树之后发现挺复杂的。

答案的思路非常的简洁,左边外围节点的特点是如果当前节点外围节点,则该节点的左节点为外围节点,若左节点为空,则该节点的右子节点为外围节点。

右边外围节点的特点是如果当前节点外围节点,则该节点的右节点为外围节点,若右节点为空,则该节点的左子节点为外围节点。

注意,左边外围和右边外围节点的输出顺序,不同。右边可参考逆序输出链表,递归实现相当简洁。

11.求二叉树中两个节点最近公共组先的方法。

思路比较类似第一个题目的思路,递归,但是需要传递两个参数(或者最近公共祖先利用全局变量)

a.如果两个节点均在左子树,则递归左子树寻找最近公共祖先。

b.如果两个节点分别在左右子树,则当前节点返回。

c.如果当前节点为其中一个节点,则返回当前节点。

(这个算法从来没有coding过,需要练习一下,算是基础的操作)

12.如果二叉树中包含parent指针,可以优化二叉树中求两个节点最近公共祖先的方法吗?

有了parent指针,这样就可以同时从给定的两个节点向上找最先交叉的节点的,所以问题转换为两个单链表找最先出现交叉的节点。

a.方法是三遍扫描,首先针对每个节点,从当前节点统计至末尾节点,分别统计出两个链表的长度,然后长度较大的先走动长度之差的距离,然后两个指针同时走动,第一次汇合的地方就是最近的公共祖先。

b.如果使用上述的方法,时间复杂度为最深节点的深度。因为需要统计长度。然后后来该问题继续深入问了一下能够进一步优化时间复杂度。

当时想想确实没有办法进一步优化时间复杂度了,思路被阻塞了。完全没想到空间换时间的策略,题目没要求空间复杂度,就考虑一下牺牲空间。

策略是利用hash表,每次遍历两个节点,然后添加至hash表。同时向前推进,如果遍历的节点已经出现在hash表,表示该节点是第一次汇合的节点。(空间换取时间)

13.给定一个字符串S和一系列字符串集合D,找到S中最短的字符串前缀,满足该前缀不是D集合中任意一个字符串的前缀。

求最短公共前缀的方法是用Trie树,这里也需要使用Trie树的方法。(针对字符串,trie树的效率高于hash,fb的面试失败就是没有第一时间想到这个概念,用的不熟练)

Trie树字母表示在边的扩展中,节点一般存储true或者false代表是否到达单词,或者存储vector<string>容器代表该路径可达的所有单词,依据题目来决定Trie树的结构。

(需要练习一下)

时间: 2024-10-10 06:28:03

二叉树的基础题目学习(EPI)的相关文章

数组和字符串的基础题目学习(EPI)

学习的速度有些慢,脑袋转动的频率有些不是很高.不过今天的效率我觉得还是可以,应该不能称效率吧,就是整个感觉不错,感觉自己补充了很多的知识.其实G家和F家败了之后不知道看看算法题对接下来的找工作帮助是否会很大,但是看算法题目也是提高解决问题能力的一种方式吧,锻炼思维.僵化的思维实在有些不能忍受. 另外今天更是遇到之前leetcode之中的一些题目,当时那个题目第一时间没有思路,想了良久有了思路,有种灵机一动的感觉,今天碰到的时候竟然没有回想起来,即使自己的灵机一闪也无法依赖.那种灵机一动的时候人的

链表的基础题目学习(EPI)

链表的题目总体来说细节比较多,因为链表的题目在操作链表的过程中本身有些复杂,所以如果链表作为编程题出现的时候,多数情况下题目本身的思路可能不是很复杂,不要把题目往复杂的方向去思考就好了~这里的链表只是说单向链表,双向链表,跳表.树的链表表示形式不属于这个链表的范畴. 1.合并两个有序的链表.额外空间要求O(1). 思路很明确的一个题目,三个指针. 2.如何判断一个链表中是否存在环?如果存在环,如何快速的找出环的起点位置. 这个题目只要见过一次就很难再忘记它的解题方法了,非常有技巧的一个方法.fa

汇编基础题目学习--1

(1)1个CPU的寻址能力为8KB,那么它的地址总线的宽度为_______. (2)1KB的存储器有______个存储单元,存储单元的编号从 ___ 到___ . (3)1KB的存储器可以存储 ____ 个bit, ___个Byte. (4)1GB是 ___ 个Byte.1MB是 ___ 个Byte.1KB是 ___个Byte. (5)8080.8088.80296.80386的地址总线宽度分别为16根.20根.24根.32根,则它们的寻址能力分别为:___(KB). ___(MB). ___(

基础命令学习总结

基础命令总结: 1.获取帮助命令: help man info 2.日期时间命令: date hwclock cal 3.系统管理命令: shutdown halt reboot poweroff bash tty 4.文件管理命令: cd type file echo which  whereis whatis dirname basename makewhatis mkdir pwd      touch 基础命令学习总结,布布扣,bubuko.com

黑马程序员_JAVA 基础加强学习笔记

一.面向对象 (一)继承  1.继承的好处: (1) 提高了代码的复用性. (2) 让类与类之间产生了关系,提供了另一个特征多态的前提. 注意: 子类中所有的构造函数都会默认访问父类中的空参数的构造函数,因为每一个子类构造内第一行都有默认的语句super();  如果父类中没有空参数的构造函数,那么子类的构造函数内,必须通过super语句指定要访问的父类中的构造函数. 如果子类构造函数中用this来指定调用子类自己的构造函数,那么被调用的构造函数也一样会访问父类中的构造函数. 2.final特点

java 集合基础1 学习笔记

集合特点: 1.用于存储对象的容器. 2.集合的长度是可变的. 3.集合中不可以存储基本数据类型值. Collection接口常见方法: 1.添加 boolean add(obj); boolean addAll(Collection coll); 2.删除 boolean remove(obj); boolean removeAll(Collection coll); void clear();//清空集合 3.判断 boolean contains(obj); boolean contain

零基础如何学习java更有效呢?

零基础学java,不知道该如何入手?也不知道学习的方向,很多人会问零基础怎么样学习,有没有什么入门的书籍推荐:只要方法正确,零基础学好java也是有机会的哦. 一.理解Java思想 Java是一门面向对象编程语言.向对象编程是Java最核心的思想,这也是区分和C等其他编程语言的一个显著特征.掌握面一门语言,首先得掌握它的思想,思想决定高度.这就和我们学习语文和英语一样,汉语学拼音,英语学音标.抓住了核心,学习起来就事半功倍. 二.弄清基本概念 作为一门语言,Java肯定有着丰富而又简单的概念.弄

.Net程序员之Python基础教程学习----列表和元组 [First Day]

一. 通用序列操作: 其实对于列表,元组 都属于序列化数据,可以通过下表来访问的.下面就来看看序列的基本操作吧. 1.1 索引: 序列中的所有元素的下标是从0开始递增的. 如果索引的长度的是N,那么所以的范围是-N~N-1之间,超过这个范围就会提示 IndexError:  index out of range >>> greeting ='Hello world' >>> print greeting Hello world >>> print gr

计算机基础知识学习

第一周学习 一.知识方面,总得来说,就是学习计算机基础知识. 1.从计算机的发展,应用,组成,网络等方面了解: 2.对计算机操作系统的学习:常用的操作系统,操作系统的功能.分类: 3.办公软件的学习:Word\Excel\ppt,像Word中编号格式.自动生成目录,Excel中条件格式的应用,图表插入,以及各种基础函数的使用: =SUM(Eoo*$E$3,Foo*$F$3,Goo*$G$3)  表示:用E\F\G列的各数与E3\F3\G3相乘后求和,count(if)\a 计数函数, =COUN