先自我介绍一下,小编浙江大学毕业,去过华为、字节跳动等大厂,目前阿里P7
深知大多数程序员,想要提升技能,往往是自己摸索成长,但自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!
因此收集整理了一份《2024年最新Android移动开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友。
既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,涵盖了95%以上Android开发知识点,真正体系化!
由于文件比较多,这里只是将部分目录截图出来,全套包含大厂面经、学习笔记、源码讲义、实战项目、大纲路线、讲解视频,并且后续会持续更新
如果你需要这些资料,可以添加V获取:vip204888 (备注Android)
正文
-
//先序遍历
-
int PreOrderVisitTree(pBiTree T, VisitType pFuncVisit)
-
{
-
if (T)
-
{
-
(*pFuncVisit)(T->data);
-
if (PreOrderVisitTree(T->leftChild, pFuncVisit) == RET_OK)
-
{
-
if (PreOrderVisitTree(T->rightChild, pFuncVisit) == RET_OK)
-
{
-
return RET_OK;
-
}
-
}
-
return RET_ERROR;
-
}
-
else
-
{
-
return RET_OK;
-
}
-
}
中序遍历和后序遍历
有了先序的经验,这两个就很好理解了,中序是先访问左子树, 再根结点,再右子树, 后序是先访问左子树, 再右子树,再根结点。代码更容易,只要改一下调用顺序就可以了。
不过我这里给出一种非递归的实现。递归固然是清晰明了,但是存在效率低的问题,非递归的方案用栈结构来存结点信息,通过出栈访问来遍历二叉树。它思想是这样的,当栈顶中的指针非空时,遍历左子树,也就是左子树根的指针进栈。当栈顶指针为空时,应退至上一层,如果是从左子树返回的,访问当前层,也就是栈顶中的根指针结点。如果是从右子树返回,说明当前层遍历完毕,继续退栈。代码如下:
[cpp] view plain copy
-
//中序遍历, 非递归实现
-
int InOrderVisitTree(pBiTree T, VisitType pFuncVisit)
-
{
-
ponyStack binaryTreeStack;
-
InitStack(&binaryTreeStack, 4);
-
Push(&binaryTreeStack, &T);
-
pBiTree pTempNode;
-
while (!IsEmptyStack(binaryTreeStack))
-
{
-
while((GetTop(binaryTreeStack, &pTempNode) == RET_OK) && (pTempNode != NULL))
-
{
-
Push(&binaryTreeStack, &(pTempNode->leftChild));
-
}
-
Pop(&binaryTreeStack, &pTempNode);
-
if (!IsEmptyStack(binaryTreeStack))
-
{
-
Pop(&binaryTreeStack, &pTempNode);
-
(*pFuncVisit)(pTempNode->data);
-
Push(&binaryTreeStack, &(pTempNode->rightChild));
-
}
-
}
-
return RET_OK;
-
}
树是一种比较重要的数据结构,尤其是二叉树。二叉树是一种特殊的树,在二叉树中每个节点最多有两个子节点,一般称为左子节点和右子节点(或左孩子和右孩子),并且二叉树的子树有左右之分,其次序不能任意颠倒。二叉树是递归定义的,因此,与二叉树有关的题目基本都可以用递归思想解决,当然有些题目非递归解法也应该掌握,如非递归遍历节点等等。本文努力对二叉树相关题目做一个较全的整理总结,希望对找工作的同学有所帮助。
二叉树节点定义如下:
struct BinaryTreeNode
{
int m_nValue;
BinaryTreeNode* m_pLeft;
BinaryTreeNode* m_pRight;
};
相关链接:
题目列表:
详细解答
递归解法:
(1)如果二叉树为空,节点个数为0
(2)如果二叉树不为空,二叉树节点个数 = 左子树节点个数 + 右子树节点个数 + 1
参考代码如下:
[cpp] view plain copy
-
int GetNodeNum(BinaryTreeNode * pRoot)
-
{
-
if(pRoot == NULL) // 递归出口
-
return 0;
-
return GetNodeNum(pRoot->m_pLeft) + GetNodeNum(pRoot->m_pRight) + 1;
-
}
递归解法:
(1)如果二叉树为空,二叉树的深度为0
(2)如果二叉树不为空,二叉树的深度 = max(左子树深度, 右子树深度) + 1
参考代码如下:
[cpp] view plain copy
-
int GetDepth(BinaryTreeNode * pRoot)
-
{
-
if(pRoot == NULL) // 递归出口
-
return 0;
-
int depthLeft = GetDepth(pRoot->m_pLeft);
-
int depthRight = GetDepth(pRoot->m_pRight);
-
return depthLeft > depthRight ? (depthLeft + 1) : (depthRight + 1);
-
}
前序遍历递归解法:
(1)如果二叉树为空,空操作
(2)如果二叉树不为空,访问根节点,前序遍历左子树,前序遍历右子树
参考代码如下:
[cpp] view plain copy
-
void PreOrderTraverse(BinaryTreeNode * pRoot)
-
{
-
if(pRoot == NULL)
-
return;
-
Visit(pRoot); // 访问根节点
-
PreOrderTraverse(pRoot->m_pLeft); // 前序遍历左子树
-
PreOrderTraverse(pRoot->m_pRight); // 前序遍历右子树
-
}
中序遍历递归解法
(1)如果二叉树为空,空操作。
(2)如果二叉树不为空,中序遍历左子树,访问根节点,中序遍历右子树
参考代码如下:
[cpp] view plain copy
-
void InOrderTraverse(BinaryTreeNode * pRoot)
-
{
-
if(pRoot == NULL)
-
return;
-
InOrderTraverse(pRoot->m_pLeft); // 中序遍历左子树
-
Visit(pRoot); // 访问根节点
-
InOrderTraverse(pRoot->m_pRight); // 中序遍历右子树
-
}
后序遍历递归解法
(1)如果二叉树为空,空操作
(2)如果二叉树不为空,后序遍历左子树,后序遍历右子树,访问根节点
参考代码如下:
[cpp] view plain copy
-
void PostOrderTraverse(BinaryTreeNode * pRoot)
-
{
-
if(pRoot == NULL)
-
return;
-
PostOrderTraverse(pRoot->m_pLeft); // 后序遍历左子树
-
PostOrderTraverse(pRoot->m_pRight); // 后序遍历右子树
-
Visit(pRoot); // 访问根节点
-
}
相当于广度优先搜索,使用队列实现。队列初始化,将根节点压入队列。当队列不为空,进行如下操作:弹出一个节点,访问,若左子节点或右子节点不为空,将其压入队列。
[cpp] view plain copy
-
void LevelTraverse(BinaryTreeNode * pRoot)
-
{
-
if(pRoot == NULL)
-
return;
-
queue<BinaryTreeNode *> q;
-
q.push(pRoot);
-
while(!q.empty())
-
{
-
BinaryTreeNode * pNode = q.front();
-
q.pop();
-
Visit(pNode); // 访问节点
-
if(pNode->m_pLeft != NULL)
-
q.push(pNode->m_pLeft);
-
if(pNode->m_pRight != NULL)
-
q.push(pNode->m_pRight);
-
}
-
return;
-
}
要求不能创建新节点,只调整指针。
递归解法:
(1)如果二叉树查找树为空,不需要转换,对应双向链表的第一个节点是NULL,最后一个节点是NULL
(2)如果二叉查找树不为空:
如果左子树为空,对应双向有序链表的第一个节点是根节点,左边不需要其他操作;
如果左子树不为空,转换左子树,二叉查找树对应双向有序链表的第一个节点就是左子树转换后双向有序链表的第一个节点,同时将根节点和左子树转换后的双向有序链 表的最后一个节点连接;
如果右子树为空,对应双向有序链表的最后一个节点是根节点,右边不需要其他操作;
如果右子树不为空,对应双向有序链表的最后一个节点就是右子树转换后双向有序链表的最后一个节点,同时将根节点和右子树转换后的双向有序链表的第一个节点连 接。
参考代码如下:
[cpp] view plain copy
-
/******************************************************************************
-
参数:
-
pRoot: 二叉查找树根节点指针
-
pFirstNode: 转换后双向有序链表的第一个节点指针
-
pLastNode: 转换后双向有序链表的最后一个节点指针
-
******************************************************************************/
-
void Convert(BinaryTreeNode * pRoot,
-
BinaryTreeNode * & pFirstNode, BinaryTreeNode * & pLastNode)
-
{
-
BinaryTreeNode *pFirstLeft, *pLastLeft, * pFirstRight, *pLastRight;
-
if(pRoot == NULL)
-
{
-
pFirstNode = NULL;
-
pLastNode = NULL;
-
return;
-
}
-
if(pRoot->m_pLeft == NULL)
-
{
-
// 如果左子树为空,对应双向有序链表的第一个节点是根节点
-
pFirstNode = pRoot;
-
}
-
else
-
{
-
Convert(pRoot->m_pLeft, pFirstLeft, pLastLeft);
-
// 二叉查找树对应双向有序链表的第一个节点就是左子树转换后双向有序链表的第一个节点
-
pFirstNode = pFirstLeft;
-
// 将根节点和左子树转换后的双向有序链表的最后一个节点连接
-
pRoot->m_pLeft = pLastLeft;
-
pLastLeft->m_pRight = pRoot;
-
}
-
if(pRoot->m_pRight == NULL)
-
{
-
// 对应双向有序链表的最后一个节点是根节点
-
pLastNode = pRoot;
-
}
-
else
-
{
-
Convert(pRoot->m_pRight, pFirstRight, pLastRight);
-
// 对应双向有序链表的最后一个节点就是右子树转换后双向有序链表的最后一个节点
-
pLastNode = pLastRight;
-
// 将根节点和右子树转换后的双向有序链表的第一个节点连接
-
pRoot->m_pRight = pFirstRight;
-
pFirstRight->m_pLeft = pRoot;
-
}
-
return;
-
}
递归解法:
(1)如果二叉树为空或者k<1返回0
(2)如果二叉树不为空并且k==1,返回1
(3)如果二叉树不为空且k>1,返回左子树中k-1层的节点个数与右子树k-1层节点个数之和
参考代码如下:
[cpp] view plain copy
-
int GetNodeNumKthLevel(BinaryTreeNode * pRoot, int k)
-
{
-
if(pRoot == NULL || k < 1)
-
return 0;
-
if(k == 1)
-
return 1;
-
int numLeft = GetNodeNumKthLevel(pRoot->m_pLeft, k-1); // 左子树中k-1层的节点个数
-
int numRight = GetNodeNumKthLevel(pRoot->m_pRight, k-1); // 右子树中k-1层的节点个数
-
return (numLeft + numRight);
-
}
递归解法:
(1)如果二叉树为空,返回0
(2)如果二叉树不为空且左右子树为空,返回1
(3)如果二叉树不为空,且左右子树不同时为空,返回左子树中叶子节点个数加上右子树中叶子节点个数
参考代码如下:
[cpp] view plain copy
-
int GetLeafNodeNum(BinaryTreeNode * pRoot)
-
{
-
if(pRoot == NULL)
-
return 0;
-
if(pRoot->m_pLeft == NULL && pRoot->m_pRight == NULL)
-
return 1;
-
int numLeft = GetLeafNodeNum(pRoot->m_pLeft); // 左子树中叶节点的个数
-
int numRight = GetLeafNodeNum(pRoot->m_pRight); // 右子树中叶节点的个数
-
return (numLeft + numRight);
-
}
不考虑数据内容。结构相同意味着对应的左子树和对应的右子树都结构相同。
递归解法:
(1)如果两棵二叉树都为空,返回真
(2)如果两棵二叉树一棵为空,另一棵不为空,返回假
(3)如果两棵二叉树都不为空,如果对应的左子树和右子树都同构返回真,其他返回假
参考代码如下:
[cpp] view plain copy
-
bool StructureCmp(BinaryTreeNode * pRoot1, BinaryTreeNode * pRoot2)
-
{
-
if(pRoot1 == NULL && pRoot2 == NULL) // 都为空,返回真
-
return true;
-
else if(pRoot1 == NULL || pRoot2 == NULL) // 有一个为空,一个不为空,返回假
-
return false;
-
bool resultLeft = StructureCmp(pRoot1->m_pLeft, pRoot2->m_pLeft); // 比较对应左子树
-
bool resultRight = StructureCmp(pRoot1->m_pRight, pRoot2->m_pRight); // 比较对应右子树
-
return (resultLeft && resultRight);
-
}
递归解法:
(1)如果二叉树为空,返回真
(2)如果二叉树不为空,如果左子树和右子树都是AVL树并且左子树和右子树高度相差不大于1,返回真,其他返回假
参考代码:
[cpp] view plain copy
-
bool IsAVL(BinaryTreeNode * pRoot, int & height)
-
{
-
if(pRoot == NULL) // 空树,返回真
-
{
-
height = 0;
-
return true;
-
}
-
int heightLeft;
-
bool resultLeft = IsAVL(pRoot->m_pLeft, heightLeft);
-
int heightRight;
-
bool resultRight = IsAVL(pRoot->m_pRight, heightRight);
-
if(resultLeft && resultRight && abs(heightLeft - heightRight) <= 1) // 左子树和右子树都是AVL,并且高度相差不大于1,返回真
-
{
-
height = max(heightLeft, heightRight) + 1;
-
return true;
-
}
-
else
-
{
-
height = max(heightLeft, heightRight) + 1;
-
return false;
-
}
-
}
递归解法:
(1)如果二叉树为空,返回空
(2)如果二叉树不为空,求左子树和右子树的镜像,然后交换左子树和右子树
参考代码如下:
[cpp] view plain copy
-
BinaryTreeNode * Mirror(BinaryTreeNode * pRoot)
-
{
-
if(pRoot == NULL) // 返回NULL
-
return NULL;
-
BinaryTreeNode * pLeft = Mirror(pRoot->m_pLeft); // 求左子树镜像
-
BinaryTreeNode * pRight = Mirror(pRoot->m_pRight); // 求右子树镜像
-
// 交换左子树和右子树
-
pRoot->m_pLeft = pRight;
-
pRoot->m_pRight = pLeft;
-
return pRoot;
-
}
递归解法:
(1)如果两个节点分别在根节点的左子树和右子树,则返回根节点
(2)如果两个节点都在左子树,则递归处理左子树;如果两个节点都在右子树,则递归处理右子树
参考代码如下:
[cpp] view plain copy
-
bool FindNode(BinaryTreeNode * pRoot, BinaryTreeNode * pNode)
-
{
-
if(pRoot == NULL || pNode == NULL)
-
return false;
-
if(pRoot == pNode)
-
return true;
-
bool found = FindNode(pRoot->m_pLeft, pNode);
-
if(!found)
-
found = FindNode(pRoot->m_pRight, pNode);
-
return found;
-
}
-
BinaryTreeNode * GetLastCommonParent(BinaryTreeNode * pRoot,
-
BinaryTreeNode * pNode1,
-
BinaryTreeNode * pNode2)
-
{
-
if(FindNode(pRoot->m_pLeft, pNode1))
-
{
-
if(FindNode(pRoot->m_pRight, pNode2))
-
return pRoot;
-
else
-
return GetLastCommonParent(pRoot->m_pLeft, pNode1, pNode2);
-
}
-
else
-
{
-
if(FindNode(pRoot->m_pLeft, pNode2))
-
return pRoot;
-
else
-
return GetLastCommonParent(pRoot->m_pRight, pNode1, pNode2);
-
}
-
}
递归解法效率很低,有很多重复的遍历,下面看一下非递归解法。
非递归解法:
先求从根节点到两个节点的路径,然后再比较对应路径的节点就行,最后一个相同的节点也就是他们在二叉树中的最低公共祖先节点
参考代码如下:
[cpp] view plain copy
-
bool GetNodePath(BinaryTreeNode * pRoot, BinaryTreeNode * pNode,
-
list<BinaryTreeNode *> & path)
-
{
-
if(pRoot == pNode)
-
{
-
path.push_back(pRoot);
-
return true;
-
}
-
if(pRoot == NULL)
-
return false;
-
path.push_back(pRoot);
-
bool found = false;
-
found = GetNodePath(pRoot->m_pLeft, pNode, path);
-
if(!found)
-
found = GetNodePath(pRoot->m_pRight, pNode, path);
-
if(!found)
-
path.pop_back();
如何做好面试突击,规划学习方向?
面试题集可以帮助你查漏补缺,有方向有针对性的学习,为之后进大厂做准备。但是如果你仅仅是看一遍,而不去学习和深究。那么这份面试题对你的帮助会很有限。最终还是要靠资深技术水平说话。
网上学习 Android的资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。建议先制定学习计划,根据学习计划把知识点关联起来,形成一个系统化的知识体系。
学习方向很容易规划,但是如果只通过碎片化的学习,对自己的提升是很慢的。
同时我还搜集整理2020年字节跳动,以及腾讯,阿里,华为,小米等公司的面试题,把面试的要求和技术点梳理成一份大而全的“ Android架构师”面试 Xmind(实际上比预期多花了不少精力),包含知识脉络 + 分支细节。
在搭建这些技术框架的时候,还整理了系统的高级进阶教程,会比自己碎片化学习效果强太多。
网上学习 Android的资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。希望这份系统化的技术体系对大家有一个方向参考。
网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。
需要这份系统化的资料的朋友,可以添加V获取:vip204888 (备注Android)
一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!
);
-
if(!found)
-
path.pop_back();
如何做好面试突击,规划学习方向?
面试题集可以帮助你查漏补缺,有方向有针对性的学习,为之后进大厂做准备。但是如果你仅仅是看一遍,而不去学习和深究。那么这份面试题对你的帮助会很有限。最终还是要靠资深技术水平说话。
网上学习 Android的资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。建议先制定学习计划,根据学习计划把知识点关联起来,形成一个系统化的知识体系。
学习方向很容易规划,但是如果只通过碎片化的学习,对自己的提升是很慢的。
同时我还搜集整理2020年字节跳动,以及腾讯,阿里,华为,小米等公司的面试题,把面试的要求和技术点梳理成一份大而全的“ Android架构师”面试 Xmind(实际上比预期多花了不少精力),包含知识脉络 + 分支细节。
[外链图片转存中…(img-ttjDahHf-1713305505849)]
在搭建这些技术框架的时候,还整理了系统的高级进阶教程,会比自己碎片化学习效果强太多。
[外链图片转存中…(img-aq4SjE82-1713305505849)]
网上学习 Android的资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。希望这份系统化的技术体系对大家有一个方向参考。
网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。
需要这份系统化的资料的朋友,可以添加V获取:vip204888 (备注Android)
[外链图片转存中…(img-tKce4tyC-1713305505850)]
一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!