目录
二十四 二叉树中和为某一值的路径3/2
参考:https://www.cnblogs.com/silentteller/p/10829084.html
参考:https://www.cnblogs.com/silentteller/p/11925160.html
参考:https://www.cnblogs.com/wanglei5205/p/8686863.html
【前序遍历】【全局变量】
三个链接里分别增加了全局变量,使得函数参数减少,一定程度上减少了麻烦。
思路:
1 使用二维向量res存储全部路径,使用一维向量s存储当前路径。
2 遍历二叉树的过程:按前序遍历顺序访问每一个节点。访问每个结点时,将结点添加到路径向量tmp中。如果当前结点是叶子结点,则判断当前路径是否是符合条件的路径,符合条件的路径存入到二维向量res;如果当前结点不是叶子结点,则递归当前节点的左右子节点。
/*
struct TreeNode {
int val;
struct TreeNode *left;
struct TreeNode *right;
TreeNode(int x) :
val(x), left(NULL), right(NULL) {
}
};*/
class Solution {
public:
vector<vector<int> > FindPath(TreeNode* root,int expectNumber) {
if(root) dfs(root, expectNumber);
else return res;
}
void dfs(TrrNode * root, int sum){
s.push_back(root);//先序遍历,访问每个结点时,将结点添加到路径向量s。
if(!(root -> left) && !(root -> right)){//是叶节点
if(sum - root -> val == 0)//则判断当前路径是否是符合条件的路径,
res.push_back(s);//符合条件的路径存入到二维向量res
}else{//不是叶节点,分别递归左右子树
if(root -> left) dfs(root -> left, sum - root -> val);
if(root -> right) dfs(root -> right, sum - root -> val);
}
if(!s.empty())//s非空
s.pop_back();返回父节点之前需要删除当前节点
}
private:
vector<vector<int>> res;//二维向量res存储全部路径
vector<int> s;//一维向量s存储当前路径
};
二十六 二叉搜索树与双向链表
参考:https://www.cnblogs.com/silentteller/p/11942608.html
【中序遍历】【双向链表】
class Solution {
public:
TreeNode* Convert(TreeNode* pRootOfTree)
{
if(pRootOfTree == nullptr)
return nullptr;//空树
if(pRootOfTree -> left == nullptr && pRootOfTree -> right == nullptr)
return pRootOfTree;//只有一个元素
helper(pRootOfTree);//完成中序遍历
for(int i = 0; i < node.size() - 1; i++){//按顺序连接所有节点,循环到size-1节点停下
node[i] -> right = node[i + 1];//前后互连 node0为头结点
node[i + 1] -> left = node[i];//【易错】【双向链表的连接】
}
return node[0];//返回链表头结点
}
void helper(TreeNode* root){
if(root == nullptr)
return;
helper(root -> left);//中序遍历
node.push_back(root);//node作为私有变量,存储中序遍历结果
helper(root -> right);
}
private:
vector<TreeNode*> ndoe;//一维向量存储节点
};
三十八 二叉树的深度
参考:https://www.cnblogs.com/silentteller/p/12046692.html
【深度】
深度 = max(左子树高度, 右子树高度)+1;
递归直到叶节点,停止条件:叶节点->left == nullptr, return 0;
class Solution {
public:
int TreeDepth(TreeNode* pRoot)
{
if(pRoot == nullptr) return 0;
else return THeight(pRoot);
}
int THeight(TreeNode* root){
if(root == nullptr)
return 0;
else
return max(THeight(root -> left) + 1,THeight(root -> right) + 1);//+1放在max()的里面
}
};
三十九 平衡二叉树
参考:https://www.cnblogs.com/wanglei5205/p/8918624.html
参考:https://blog.csdn.net/dawn_after_dark/article/details/81702988
参考:https://www.cnblogs.com/silentteller/p/12051453.html
【二叉树高度】
class Solution {
public:
bool IsBalanced_Solution(TreeNode* pRoot) {
//平衡二叉树:abs(左子树高度 - 右子树高度)<=1
//计算二叉树高度:递归找到叶节点
//-1表示false
if(getHeight(pRoot) == -1)
return false;
else
return true;
}
int getHeight(TreeNode* root){
if(root == nullptr)
return 0;
int LeftHeight = getHeight(root -> left);
if(LeftHeight == -1) return -1;
int RightHeight = getHeight(root -> right);
if(RightHeight == -1) return -1;
return abs(LeftHeight - RightHeight) <= 1? max(LeftHeight + 1, RightHeight + 1): -1;
}
};
五十八 二叉树的下一个节点
参考:https://www.cnblogs.com/silentteller/p/12114894.html
二叉树的中序遍历是左根右,所以如果一个结点的右子树不为空,那么这个节点的下一个节点一定是右子树的最左结点,如果右子树不存在左子树的话,就返回右子树的根结点。
如果一个结点的右子树为空,且当前结点是其父结点的左子树的根结点,那么直接返回其父节点,否则就一直访问其父结点,直到找到一个结点是其父结点的左子结点,同样返回其父结点。
【中序遍历】【父指针】【逻辑】
这题一定要画树
class Solution {
public:
TreeLinkNode* GetNext(TreeLinkNode* pNode)
{//中序遍历下一个节点是右儿子
if(pNode == nullptr) return pNode;
if(pNode->right != nullptr){//节点右儿子存在
pNode = pNode -> right;//指针移到右儿子上
while(pNode -> left != nullptr)//找到右子树中最左边(最小)节点
pNode = pNode -> left;
return pNode;
}
while(pNode -> next != nullptr && pNode -> next->left != pNode){
pNode = pNode -> next;//右子树不存在,一直向上找爸爸,直到找到一个爸爸是爷爷的左儿子。当找到根节点时候,他没有父指针,所以返回NULL
}
return pNode->next;//返回父节点
}
};
对称的二叉树
参考:https://www.cnblogs.com/silentteller/p/12114934.html
与镜像的区别,镜像是将树左右子树一直交换,交换的元素值可以不同
对称是左右两边必须是相同的数字
class Solution {
public:
bool isSymmetrical(TreeNode* pRoot)
{
if(pRoot == nullptr)
return true;//空树对称
return isDuichen(pRoot->left, pRoot -> right);
}
bool isDuichen(TreeNode* p1, TreeNode* p2){
if(p1 == nullptr && p2 == nullptr)
return true;//出口A 比较到左右树都空了
if(p1 == nullptr && p2 != nullptr)
return false;//出口B
if(p2 == nullptr && p1 != nullptr)
return false;//出口C 一个空一个不空
if(p1->val != p2->val)
return false;//两者非空,但值不相同
return isDuichen(p1->left,p2->right)&&isDuichen(p1->right,p2->left);
//【注意】比较的是最左和最右,中间两个互相比较;ABCD比较AD和BC
}
};
二叉搜索树的第k个节点
参考:https://www.cnblogs.com/silentteller/p/12118658.html
二叉搜索树的中序遍历结果正好是按数值升序排列的结果,我们可以利用中序遍历将结点按val值的顺序存储到数组中,然后直接按索引返回即可。
【中序遍历】
class Solution {
public:
TreeNode* KthNode(TreeNode* pRoot, int k)
{
if(pRoot == nullptr) return nullptr;
LDR(pRoot);
if(k>res.size() || k<1)
return nullptr;
return res[k - 1];
}
void LDR(TreeNode* root){
if(root == nullptr)
return;
LDR(root->left);
res.push_back(root);
LDR(root->right);
}
private:
vector<TreeNode*> res;
};
按之字顺序打印二叉树3/3
参考:https://www.cnblogs.com/silentteller/p/12115122.html
“实际上是二叉树的层次遍历,只不过每一行输出的方向都是相反的,每遍历二叉树的结点时,都将其内的每一个结点的左右孩子都保存好,设置一个标志,ture时就正序输出,false就逆序输出val,直到所有的结点都打印完即可。”
参考:https://yq.aliyun.com/articles/368042
思路:【层序遍历】+搞一个flag来标志是正向还是负向打印,打印完一行取反
层序遍历一般方法:队列,node出队,node左右儿子入队
本题思路:
A是打印行,B是打印行的儿子,考虑输出方向,C作为输出行;res(全局变量)保存所有结果,保存C
1 根节点压入A,
开始while循环,循环条件 A非空
2 A非空,将A中元素a的左右儿子bc压入B
3判断flag,真:A从左往右扫描输出(for循环 O(n))
假:A从右往左扫描输出
扫描输出结果存入C
4 flag取反
5 将C压入res
6 交换AB,清空B
最终调出循环条件是:A中节点 == NULL
即访问到叶节点的儿子那一层,则跳出while循环
class Solution {
public:
vector<vector<int> > Print(TreeNode* pRoot) {
if(pRoot == nullptr) return res;
vector<TreeNode*> A;
vector<TreeNode*> B;
bool flag = true;
A.push_back(pRoot);
while(!A.empty()){
for(auto i:A){//【错了一次】【扫描A中元素的左右儿子】
if(i->left != nullptr) B.push_back(i->left);
if(i->right != nullptr) B.push_back(i->right);
}
vector<int> C;
if(flag){
//从左往右输出
for(int i = 0; i < A.size(); i++){
C.push_back(A[i]->val);//C是int类型,要压入数字,不要忘记->val
}
}else{
//从右往左输出
for(int i = A.size() - 1; i >= 0; i--)
C.push_back(A[i]->val);
}
flag = !flag;
res.push_back(C);
A.swap(B);
B.clear();
}
return res;
}
private:
vector<vector<int>> res;
};
二叉树打印成多行
【层次遍历】
和上一题差不多,不需要flag和C
A是打印行,B是打印行的儿子;res保存所有结果,保存A
1 根节点压入A,
开始while循环,循环条件 A非空
2 A非空,将A中元素a的左右儿子bc压入B
3 A从左往右扫描输出(for循环 O(n))
输出结果存入C
4 将C压入res
5 交换AB,清空B
最终调出循环条件是:A中节点 == NULL
即访问到叶节点的儿子那一层,则跳出while循环
/*
struct TreeNode {
int val;
struct TreeNode *left;
struct TreeNode *right;
TreeNode(int x) :
val(x), left(NULL), right(NULL) {
}
};
*/
class Solution {
public:
vector<vector<int> > Print(TreeNode* pRoot) {
if(pRoot == nullptr)
return res;
vector<TreeNode*> A,B;
A.push_back(pRoot);
while(!A.empty()){
for(auto i:A){
if(i->left) B.push_back(i->left);
if(i->right) B.push_back(i->right);
}
vector<int> C;
for(int i = 0; i < A.size(); i++){
C.push_back(A[i]->val);
}
res.push_back(C);
A.swap(B);
B.clear();
}
return res;
}
private:
vector<vector<int>> res;
};
序列化二叉树
参考:https://www.cnblogs.com/silentteller/p/12115563.html
思路:
序列化:把树打印成一串字符
反序列化:把一串字符,重建成二叉树
简单地来看就是打印树和重建树
对于序列化:
a:全局变量,int型一维向量
res:保存结果,int型数组
1 根节点是否为空,是->a中压入‘#’字符
2根节点非空,对二叉树进行前序遍历,将a中元素复制到res;
res为动态数组,根据a的大小创建
3 res强制转换为字符型,返回res
对于反序列化:
1,将输入字符串数组 str 强制转换成int类型数组 p
2,处理p
怎么处理呢?
1 检查p第一个元素是否为#,是->指针后移,返回null,退出递归;【这里的指针后移,是指到了左/右子树的叶节点,要继续移动指针,访问另一个子树】
2 否-> 新建一个树节点res,后移指针,树的左儿子 = 递归处理;树的右儿子 = 递归处理;
3返回
/*
struct TreeNode {
int val;
struct TreeNode *left;
struct TreeNode *right;
TreeNode(int x) :
val(x), left(NULL), right(NULL) {
}
};
*/
class Solution {
public:
char* Serialize(TreeNode *root) {//返回字符型数组类型
Dlr(root);对二叉树进行前序遍历将节点加入数组
//将vector数组a中的元素一一复制到动态整型数组res中
int *res = new int[a.size()];
for(int i = 0;i < a.size(); i++)
res[i] = a[i];//复制【易错:忘记复制了】
return (char*)res;//返回字符型数组类型
}
TreeNode* Deserialize(char *str) {
int *p = (int*) str;//强制转换,先将字符数组类型转为整型数组
return helper(p);//二叉树的节点的值就可以直接从整型数组中得到
}
TreeNode* helper(int *&str) {//【易错:&对p的引用改变p的值】
if(*str == 0xFFFFFFFF){//处理递归出口,如果遇到0xFFFFFFF,
str++;//则指针向后移动一个单位
return nullptr;//并退出递归【易错:返回类型写错了】
}
//否则
TreeNode* res = new TreeNode(*str);//构造二叉树根节点【易错:竟然写成了str,str是下标啊】
str++;//继续移动指针
res->left = helper(str);
res->right = helper(str);//连接递归得到的左子树以及右子树
return res;//返回二叉树根节点【忘记返回了】
}
//前序遍历
void Dlr(TreeNode *root){
if(root == nullptr){//当节点为空的时候,
a.push_back(0xFFFFFFFF);//加入特殊数值0xFFFFFFF,
return;//并退出递归【结束循环,返回空】
}
a.push_back(root->val);//【压入的是节点的值,要分清楚压入的是啥】
Dlr(root->left);
Dlr(root->right);
}
private:
vector<int> a;
};