关于二叉树遍历及二叉树面试题目,Android社招面经分享

  1. {

  2. *root = NULL;

  3. }

  4. else

  5. {

  6. *root = (BiTNode *)malloc(sizeof(BiTNode));

  7. if (!(*root))

  8. {

  9. return RET_ERROR;

  10. }

  11. (*root)->data = GetRandom();

  12. CreateBiTree(&(*root)->leftChild);

  13. CreateBiTree(&(*root)->rightChild);

  14. }

  15. return RET_OK;

  16. }

  17. int g_i = 0;

  18. //创建二叉树,自动执行,方便测试

  19. int CreateBiTreeAuto(pBiTree *root)

  20. {

  21. char szOrder[] = “bbaabaa”;

  22. char ch = 0;

  23. if (szOrder[g_i++] == ‘a’)//控制树的结构

  24. {

  25. *root = NULL;

  26. }

  27. else

  28. {

  29. *root = (BiTNode *)malloc(sizeof(BiTNode));

  30. if (!(*root))

  31. {

  32. return RET_ERROR;

  33. }

  34. (*root)->data = GetRandom();

  35. CreateBiTreeAuto(&(*root)->leftChild);

  36. CreateBiTreeAuto(&(*root)->rightChild);

  37. }

  38. return RET_OK;

  39. }

三遍历顺序

先序遍历

先序遍历是先访问根结点,再左子树,再右子树,比如图1中的右图,先序遍历的输出如下:

A,B,D,H,I,E,J,K,C,F,G

根据上面的思想,很容易用递归的形式写出先序遍历的代码:

[cpp]  view plain  copy

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传 外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

  1. //先序遍历

  2. int PreOrderVisitTree(pBiTree T, VisitType pFuncVisit)

  3. {

  4. if (T)

  5. {

  6. (*pFuncVisit)(T->data);

  7. if (PreOrderVisitTree(T->leftChild, pFuncVisit) == RET_OK)

  8. {

  9. if (PreOrderVisitTree(T->rightChild, pFuncVisit) == RET_OK)

  10. {

  11. return RET_OK;

  12. }

  13. }

  14. return RET_ERROR;

  15. }

  16. else

  17. {

  18. return RET_OK;

  19. }

  20. }

中序遍历和后序遍历

有了先序的经验,这两个就很好理解了,中序是先访问左子树, 再根结点,再右子树, 后序是先访问左子树, 再右子树,再根结点。代码更容易,只要改一下调用顺序就可以了。

不过我这里给出一种非递归的实现。递归固然是清晰明了,但是存在效率低的问题,非递归的方案用栈结构来存结点信息,通过出栈访问来遍历二叉树。它思想是这样的,当栈顶中的指针非空时,遍历左子树,也就是左子树根的指针进栈。当栈顶指针为空时,应退至上一层,如果是从左子树返回的,访问当前层,也就是栈顶中的根指针结点。如果是从右子树返回,说明当前层遍历完毕,继续退栈。代码如下:

[cpp]  view plain  copy

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传 外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

  1. //中序遍历, 非递归实现

  2. int InOrderVisitTree(pBiTree T, VisitType pFuncVisit)

  3. {

  4. ponyStack binaryTreeStack;

  5. InitStack(&binaryTreeStack, 4);

  6. Push(&binaryTreeStack, &T);

  7. pBiTree pTempNode;

  8. while (!IsEmptyStack(binaryTreeStack))

  9. {

  10. while((GetTop(binaryTreeStack, &pTempNode) == RET_OK) && (pTempNode != NULL))

  11. {

  12. Push(&binaryTreeStack, &(pTempNode->leftChild));

  13. }

  14. Pop(&binaryTreeStack, &pTempNode);

  15. if (!IsEmptyStack(binaryTreeStack))

  16. {

  17. Pop(&binaryTreeStack, &pTempNode);

  18. (*pFuncVisit)(pTempNode->data);

  19. Push(&binaryTreeStack, &(pTempNode->rightChild));

  20. }

  21. }

  22. return RET_OK;

  23. }

树是一种比较重要的数据结构,尤其是二叉树。二叉树是一种特殊的树,在二叉树中每个节点最多有两个子节点,一般称为左子节点和右子节点(或左孩子和右孩子),并且二叉树的子树有左右之分,其次序不能任意颠倒。二叉树是递归定义的,因此,与二叉树有关的题目基本都可以用递归思想解决,当然有些题目非递归解法也应该掌握,如非递归遍历节点等等。本文努力对二叉树相关题目做一个较全的整理总结,希望对找工作的同学有所帮助。

二叉树节点定义如下:

struct BinaryTreeNode

{

int m_nValue;

BinaryTreeNode* m_pLeft;

BinaryTreeNode* m_pRight;

};

相关链接:

轻松搞定面试中的链表题目

题目列表:

1. 求二叉树中的节点个数

**2. 求二叉树的深度

3. 前序遍历,中序遍历,后序遍历

4.分层遍历二叉树(按层次从上往下,从左往右)

5. 将二叉查找树变为有序的双向链表

6. 求二叉树第K层的节点个数

7. 求二叉树中叶子节点的个数

8. 判断两棵二叉树是否结构相同

9. 判断二叉树是不是平衡二叉树

10. 求二叉树的镜像

11. 求二叉树中两个节点的最低公共祖先节点

12. 求二叉树中节点的最大距离

13. 由前序遍历序列和中序遍历序列重建二叉树

14.判断二叉树是不是完全二叉树**

详细解答

1. 求二叉树中的节点个数

递归解法:

(1)如果二叉树为空,节点个数为0

(2)如果二叉树不为空,二叉树节点个数 = 左子树节点个数 + 右子树节点个数 + 1

参考代码如下:

[cpp]  view plain  copy

  1. int GetNodeNum(BinaryTreeNode * pRoot)

  2. {

  3. if(pRoot == NULL) // 递归出口

  4. return 0;

  5. return GetNodeNum(pRoot->m_pLeft) + GetNodeNum(pRoot->m_pRight) + 1;

  6. }

2. 求二叉树的深度

递归解法:

(1)如果二叉树为空,二叉树的深度为0

(2)如果二叉树不为空,二叉树的深度 = max(左子树深度, 右子树深度) + 1

参考代码如下:

[cpp]  view plain  copy

  1. int GetDepth(BinaryTreeNode * pRoot)

  2. {

  3. if(pRoot == NULL) // 递归出口

  4. return 0;

  5. int depthLeft = GetDepth(pRoot->m_pLeft);

  6. int depthRight = GetDepth(pRoot->m_pRight);

  7. return depthLeft > depthRight ? (depthLeft + 1) : (depthRight + 1);

  8. }

3. 前序遍历,中序遍历,后序遍历

前序遍历递归解法:

(1)如果二叉树为空,空操作

(2)如果二叉树不为空,访问根节点,前序遍历左子树,前序遍历右子树

参考代码如下:

[cpp]  view plain  copy

  1. void PreOrderTraverse(BinaryTreeNode * pRoot)

  2. {

  3. if(pRoot == NULL)

  4. return;

  5. Visit(pRoot); // 访问根节点

  6. PreOrderTraverse(pRoot->m_pLeft); // 前序遍历左子树

  7. PreOrderTraverse(pRoot->m_pRight); // 前序遍历右子树

  8. }

中序遍历递归解法

(1)如果二叉树为空,空操作。

(2)如果二叉树不为空,中序遍历左子树,访问根节点,中序遍历右子树

参考代码如下:

[cpp]  view plain  copy

  1. void InOrderTraverse(BinaryTreeNode * pRoot)

  2. {

  3. if(pRoot == NULL)

  4. return;

  5. InOrderTraverse(pRoot->m_pLeft); // 中序遍历左子树

  6. Visit(pRoot); // 访问根节点

  7. InOrderTraverse(pRoot->m_pRight); // 中序遍历右子树

  8. }

后序遍历递归解法

(1)如果二叉树为空,空操作

(2)如果二叉树不为空,后序遍历左子树,后序遍历右子树,访问根节点

参考代码如下:

[cpp]  view plain  copy

  1. void PostOrderTraverse(BinaryTreeNode * pRoot)

  2. {

  3. if(pRoot == NULL)

  4. return;

  5. PostOrderTraverse(pRoot->m_pLeft); // 后序遍历左子树

  6. PostOrderTraverse(pRoot->m_pRight); // 后序遍历右子树

  7. Visit(pRoot); // 访问根节点

  8. }

4.分层遍历二叉树(按层次从上往下,从左往右)

相当于广度优先搜索,使用队列实现。队列初始化,将根节点压入队列。当队列不为空,进行如下操作:弹出一个节点,访问,若左子节点或右子节点不为空,将其压入队列。

[cpp]  view plain  copy

  1. void LevelTraverse(BinaryTreeNode * pRoot)

  2. {

  3. if(pRoot == NULL)

  4. return;

  5. queue<BinaryTreeNode *> q;

  6. q.push(pRoot);

  7. while(!q.empty())

  8. {

  9. BinaryTreeNode * pNode = q.front();

  10. q.pop();

  11. Visit(pNode); // 访问节点

  12. if(pNode->m_pLeft != NULL)

  13. q.push(pNode->m_pLeft);

  14. if(pNode->m_pRight != NULL)

  15. q.push(pNode->m_pRight);

  16. }

  17. return;

  18. }

5. 将二叉查找树变为有序的双向链表

要求不能创建新节点,只调整指针。

递归解法:

(1)如果二叉树查找树为空,不需要转换,对应双向链表的第一个节点是NULL,最后一个节点是NULL

(2)如果二叉查找树不为空:

如果左子树为空,对应双向有序链表的第一个节点是根节点,左边不需要其他操作;

如果左子树不为空,转换左子树,二叉查找树对应双向有序链表的第一个节点就是左子树转换后双向有序链表的第一个节点,同时将根节点和左子树转换后的双向有序链  表的最后一个节点连接;

如果右子树为空,对应双向有序链表的最后一个节点是根节点,右边不需要其他操作;

如果右子树不为空,对应双向有序链表的最后一个节点就是右子树转换后双向有序链表的最后一个节点,同时将根节点和右子树转换后的双向有序链表的第一个节点连  接。

参考代码如下:

[cpp]  view plain  copy

  1. /******************************************************************************

  2. 参数:

  3. pRoot: 二叉查找树根节点指针

  4. pFirstNode: 转换后双向有序链表的第一个节点指针

  5. pLastNode: 转换后双向有序链表的最后一个节点指针

  6. ******************************************************************************/

  7. void Convert(BinaryTreeNode * pRoot,

  8. BinaryTreeNode * & pFirstNode, BinaryTreeNode * & pLastNode)

  9. {

  10. BinaryTreeNode *pFirstLeft, *pLastLeft, * pFirstRight, *pLastRight;

  11. if(pRoot == NULL)

  12. {

  13. pFirstNode = NULL;

  14. pLastNode = NULL;

  15. return;

  16. }

  17. if(pRoot->m_pLeft == NULL)

  18. {

  19. // 如果左子树为空,对应双向有序链表的第一个节点是根节点

  20. pFirstNode = pRoot;

  21. }

  22. else

  23. {

  24. Convert(pRoot->m_pLeft, pFirstLeft, pLastLeft);

  25. // 二叉查找树对应双向有序链表的第一个节点就是左子树转换后双向有序链表的第一个节点

  26. pFirstNode = pFirstLeft;

  27. // 将根节点和左子树转换后的双向有序链表的最后一个节点连接

  28. pRoot->m_pLeft = pLastLeft;

  29. pLastLeft->m_pRight = pRoot;

  30. }

  31. if(pRoot->m_pRight == NULL)

  32. {

  33. // 对应双向有序链表的最后一个节点是根节点

  34. pLastNode = pRoot;

  35. }

  36. else

  37. {

  38. Convert(pRoot->m_pRight, pFirstRight, pLastRight);

  39. // 对应双向有序链表的最后一个节点就是右子树转换后双向有序链表的最后一个节点

  40. pLastNode = pLastRight;

  41. // 将根节点和右子树转换后的双向有序链表的第一个节点连接

  42. pRoot->m_pRight = pFirstRight;

  43. pFirstRight->m_pLeft = pRoot;

  44. }

  45. return;

  46. }

6. 求二叉树第K层的节点个数

递归解法:

(1)如果二叉树为空或者k<1返回0

(2)如果二叉树不为空并且k==1,返回1

(3)如果二叉树不为空且k>1,返回左子树中k-1层的节点个数与右子树k-1层节点个数之和

参考代码如下:

[cpp]  view plain  copy

  1. int GetNodeNumKthLevel(BinaryTreeNode * pRoot, int k)

  2. {

  3. if(pRoot == NULL || k < 1)

  4. return 0;

  5. if(k == 1)

  6. return 1;

  7. int numLeft = GetNodeNumKthLevel(pRoot->m_pLeft, k-1); // 左子树中k-1层的节点个数

  8. int numRight = GetNodeNumKthLevel(pRoot->m_pRight, k-1); // 右子树中k-1层的节点个数

  9. return (numLeft + numRight);

  10. }

7. 求二叉树中叶子节点的个数

递归解法:

(1)如果二叉树为空,返回0

(2)如果二叉树不为空且左右子树为空,返回1

(3)如果二叉树不为空,且左右子树不同时为空,返回左子树中叶子节点个数加上右子树中叶子节点个数

参考代码如下:

[cpp]  view plain  copy

  1. int GetLeafNodeNum(BinaryTreeNode * pRoot)

  2. {

  3. if(pRoot == NULL)

  4. return 0;

  5. if(pRoot->m_pLeft == NULL && pRoot->m_pRight == NULL)

  6. return 1;

  7. int numLeft = GetLeafNodeNum(pRoot->m_pLeft); // 左子树中叶节点的个数

  8. int numRight = GetLeafNodeNum(pRoot->m_pRight); // 右子树中叶节点的个数

  9. return (numLeft + numRight);

  10. }

8. 判断两棵二叉树是否结构相同

不考虑数据内容。结构相同意味着对应的左子树和对应的右子树都结构相同。

递归解法:

(1)如果两棵二叉树都为空,返回真

(2)如果两棵二叉树一棵为空,另一棵不为空,返回假

(3)如果两棵二叉树都不为空,如果对应的左子树和右子树都同构返回真,其他返回假

参考代码如下:

[cpp]  view plain  copy

  1. bool StructureCmp(BinaryTreeNode * pRoot1, BinaryTreeNode * pRoot2)

  2. {

  3. if(pRoot1 == NULL && pRoot2 == NULL) // 都为空,返回真

  4. return true;

  5. else if(pRoot1 == NULL || pRoot2 == NULL) // 有一个为空,一个不为空,返回假

  6. return false;

  7. bool resultLeft = StructureCmp(pRoot1->m_pLeft, pRoot2->m_pLeft); // 比较对应左子树

  8. bool resultRight = StructureCmp(pRoot1->m_pRight, pRoot2->m_pRight); // 比较对应右子树

  9. return (resultLeft && resultRight);

  10. }

9. 判断二叉树是不是平衡二叉树

递归解法:

(1)如果二叉树为空,返回真

(2)如果二叉树不为空,如果左子树和右子树都是AVL树并且左子树和右子树高度相差不大于1,返回真,其他返回假

参考代码:

[cpp]  view plain  copy

  1. bool IsAVL(BinaryTreeNode * pRoot, int & height)

  2. {

  3. if(pRoot == NULL) // 空树,返回真

  4. {

  5. height = 0;

  6. return true;

  7. }

  8. int heightLeft;

  9. bool resultLeft = IsAVL(pRoot->m_pLeft, heightLeft);

  10. int heightRight;

  11. bool resultRight = IsAVL(pRoot->m_pRight, heightRight);

  12. if(resultLeft && resultRight && abs(heightLeft - heightRight) <= 1) // 左子树和右子树都是AVL,并且高度相差不大于1,返回真

  13. {

  14. height = max(heightLeft, heightRight) + 1;

  15. return true;

  16. }

  17. else

  18. {

  19. height = max(heightLeft, heightRight) + 1;

  20. return false;

  21. }

  22. }

10. 求二叉树的镜像

递归解法:

(1)如果二叉树为空,返回空

(2)如果二叉树不为空,求左子树和右子树的镜像,然后交换左子树和右子树

参考代码如下:

[cpp]  view plain  copy

  1. BinaryTreeNode * Mirror(BinaryTreeNode * pRoot)

  2. {

  3. if(pRoot == NULL) // 返回NULL

  4. return NULL;

  5. BinaryTreeNode * pLeft = Mirror(pRoot->m_pLeft); // 求左子树镜像

  6. BinaryTreeNode * pRight = Mirror(pRoot->m_pRight); // 求右子树镜像

  7. // 交换左子树和右子树

  8. pRoot->m_pLeft = pRight;

  9. pRoot->m_pRight = pLeft;

  10. return pRoot;

  11. }

11. 求二叉树中两个节点的最低公共祖先节点

递归解法:

(1)如果两个节点分别在根节点的左子树和右子树,则返回根节点

(2)如果两个节点都在左子树,则递归处理左子树;如果两个节点都在右子树,则递归处理右子树

参考代码如下:

[cpp]  view plain  copy

  1. bool FindNode(BinaryTreeNode * pRoot, BinaryTreeNode * pNode)

  2. {

  3. if(pRoot == NULL || pNode == NULL)

自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。

深知大多数Android工程师,想要提升技能,往往是自己摸索成长或者是报班学习,但对于培训机构动则几千的学费,着实压力不小。自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!

因此收集整理了一份《2024年Android移动开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。
img
img
img
img
img
img
img

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Android开发知识点,真正体系化!

由于文件比较大,这里只是将部分目录大纲截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且后续会持续更新

如果你觉得这些内容对你有帮助,可以添加V获取:vip204888 (备注Android)
img

总结

最后为了帮助大家深刻理解Android相关知识点的原理以及面试相关知识,这里放上相关的我搜集整理的24套腾讯、字节跳动、阿里、百度2019-2021面试真题解析,我把技术点整理成了视频和PDF(实际上比预期多花了不少精力),包知识脉络 + 诸多细节

还有 高级架构技术进阶脑图、Android开发面试专题资料 帮助大家学习提升进阶,也节省大家在网上搜索资料的时间来学习,也可以分享给身边好友一起学习。

一线互联网面试专题

379页的Android进阶知识大全

379页的Android进阶知识大全

网上学习 Android的资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。希望这份系统化的技术体系对大家有一个方向参考。

2021年虽然路途坎坷,都在说Android要没落,但是,不要慌,做自己的计划,学自己的习,竞争无处不在,每个行业都是如此。相信自己,没有做不到的,只有想不到的。祝大家2021年万事大吉。

2549531)]
[外链图片转存中…(img-wk5naERT-1712062549531)]
img

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Android开发知识点,真正体系化!

由于文件比较大,这里只是将部分目录大纲截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且后续会持续更新

如果你觉得这些内容对你有帮助,可以添加V获取:vip204888 (备注Android)
[外链图片转存中…(img-EtlS7eLV-1712062549532)]

总结

最后为了帮助大家深刻理解Android相关知识点的原理以及面试相关知识,这里放上相关的我搜集整理的24套腾讯、字节跳动、阿里、百度2019-2021面试真题解析,我把技术点整理成了视频和PDF(实际上比预期多花了不少精力),包知识脉络 + 诸多细节

还有 高级架构技术进阶脑图、Android开发面试专题资料 帮助大家学习提升进阶,也节省大家在网上搜索资料的时间来学习,也可以分享给身边好友一起学习。

[外链图片转存中…(img-IEMzNYWT-1712062549533)]

[外链图片转存中…(img-zaXyTwYl-1712062549533)]

[外链图片转存中…(img-NwUx4mSO-1712062549534)]

网上学习 Android的资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。希望这份系统化的技术体系对大家有一个方向参考。

2021年虽然路途坎坷,都在说Android要没落,但是,不要慌,做自己的计划,学自己的习,竞争无处不在,每个行业都是如此。相信自己,没有做不到的,只有想不到的。祝大家2021年万事大吉。

本文已被CODING开源项目:《Android学习笔记总结+移动架构视频+大厂面试真题+项目实战源码》收录

  • 27
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值