二叉树开始
理论基础
迭代遍历树
我认为迭代遍历是用栈存储要利用的结点。以供需要时弹出
我这里的思路是普遍的写法,卡哥的是比较易懂的写法
前序遍历 ,
在遍历的时候,加入结果
这里有个关键:结点不为空时,总是向左孩子遍历,节点为空后,就弹出遍历过的元素,向其右孩子遍历,这是靠画图得出的。
核心代码
// 这里有个关键:结点不为空时,总是向左孩子遍历,节点为空后,就弹出遍历过的元素,向其右孩子遍历,这是靠画图得出的。
while(1)
{
if(cur!= nullptr)
{
res.push_back(cur->val) ;
st.push(cur) ;
cur= cur->left ;
}
if(cur == nullptr)
{
if(!st.empty()) // 如果新迭代的节点是空并且栈是空的代表已经迭代完成 。
{
cur = st.top() ;
// res.push_back(cur->val) ;
st.pop() ;
cur = cur->right ;
}
else
{
break ;
}
}
}
中序遍历
在弹栈时,将弹栈元素加如结果。 这是与前序迭代遍历不一样的。
核心代码
while(1)
{
if(cur!= nullptr)
{
// res.push_back(cur->val) ;
st.push(cur) ;
cur= cur->left ;
}
if(cur == nullptr)
{
if(!st.empty()) // 如果新跌打的节点是空并且栈是空的代表已经便利完成 。
{
cur = st.top() ;
res.push_back(cur->val) ;
// 在弹栈时,将弹栈元素加如结果。 这是与前序迭代遍历不一样的。
// 核心代码、
st.pop() ;
cur = cur->right ;
}
else
{
break ;
}
}
}
完全代码
class Solution {
public:
stack<TreeNode*> st ;
vector<int> inorderTraversal(TreeNode* root) {
TreeNode * cur = root ;
vector<int> res ;
bool flag = false ;
while(1)
{
if(cur!= nullptr)
{
// res.push_back(cur->val) ;
st.push(cur) ;
cur= cur->left ;
}
if(cur == nullptr)
{
if(!st.empty()) // 如果新跌打的节点是空并且栈是空的代表已经便利完成 。
{
cur = st.top() ;
res.push_back(cur->val) ;
// 在弹栈时,将弹栈元素加如结果。 这是与前序迭代遍历不一样的。
// 核心代码、
st.pop() ;
cur = cur->right ;
}
else
{
break ;
}
}
}
return res ;
}
};
后序遍历
后序遍历是 左右根, 那既然直接做做不出来。是否可以想下可以由之前的理论有触类旁通的地方吗。 对 前序遍历是 根左右, 那么我改变一下遍历顺序为根右左。 之后倒序输出不就可以了吗。
所以核心代码就是把指针改了, 把总是倾向于左孩子遍历改为总是倾向于右孩子遍历。
核心代码
while(1)
{
if(cur!= nullptr)
{
st.push(cur) ;
res.push_back(cur->val ) ;
cur = cur->right ; // 总是倾向于右孩子遍历
}
else
{
if(! st.empty())
{
// st.push() ;
cur = st.top() ;
st.pop() ;
cur= cur->left ; // 指针改了 总体变为 : 根右左。
}
else
{
break ;
}
}
}
卡哥的同意迭代法是 把栈作为主要的数据结构,跟我的把栈作为要次要的不相同。我主要是指针迭代,指针指向正在遍历的结点来模拟。
卡哥的思路是,前序遍历: 跟左右, 那么为了维护这个遍历顺序,使用栈的话是先把跟加入,之后是右 ,最后是左。 每次出栈的元素,把 其右孩子,左孩子依次入栈。 最后还要把自己再入栈, 同时要加入NULL , 作为要获取结果的标志 。 所以左孩子和右孩子必须是有的时候再加入栈。 因此判断当前栈顶元素是NULL时,才弹出结果元素。 加入结果集。
卡哥代码
vector<int> preorderTraversal(TreeNode* root) {
vector<int> result;
stack<TreeNode*> st;
if (root != NULL) st.push(root);
while (!st.empty()) {
TreeNode* node = st.top();
if (node != NULL) {
st.pop();
if (node->right) st.push(node->right); // 右
if (node->left) st.push(node->left); // 左
st.push(node); // 中
st.push(NULL);
} else {
st.pop();
node = st.top();
st.pop();
result.push_back(node->val);
}
}
return result;
}
中序遍历
vector<int> inorderTraversal(TreeNode* root) {
vector<int> result;
stack<TreeNode*> st;
if (root != NULL) st.push(root);
while (!st.empty()) {
TreeNode* node = st.top();
if (node != NULL) {
st.pop(); // 将该节点弹出,避免重复操作,下面再将右中左节点添加到栈中
if (node->right) st.push(node->right); // 添加右节点(空节点不入栈)
st.push(node); // 添加中节点
st.push(NULL); // 中节点访问过,但是还没有处理,加入空节点做为标记。
if (node->left) st.push(node->left); // 添加左节点(空节点不入栈)
} else { // 只有遇到空节点的时候,才将下一个节点放进结果集
st.pop(); // 将空节点弹出
node = st.top(); // 重新取出栈中元素
st.pop();
result.push_back(node->val); // 加入到结果集
}
}
return result;
}
二叉树2
[ 层序遍历 ](https://leetcode.cn/problems/binary-tree-level-order-traversal/description/)
题意:是使用层序遍历二叉树
思路: 怎么确定一层的二叉树 , 根据这一层的结点是上一层的子节点确定。 所以 每次迭代是先把队列的元素的个数存储起来size,在一个while循环size – ; 之后pop一次把pop的元素的子节点加入元素。直到队列为空
核心代码
while(que.size())
{
int tsize = que.size() ;
while(tsize -- )
{
cur = que.front() ;
if(cur != nullptr)
{
retp.push_back(cur->val) ;
que.pop() ;
if(cur->left != nullptr)
{
que.push(cur->left) ;
}
if(cur->right != nullptr)
{
que.push(cur->right) ;
}
}
}
res.push_back(retp) ;
retp.clear() ;
}
是先序遍历 在递归体进行交换指针的操作
核心代码
void recurrence(TreeNode * cur)
{
if(cur == nullptr)
{
return ;
}
TreeNode * tmp = cur->left ;
cur->left = cur->right ;
cur->right = tmp ;
recurrence(cur->left) ;
recurrence(cur->right) ;
}
对称二叉树
递归三部曲:
1. 根据解决问题所需,需要几个参数, 参数的类型的确定
2.递归函数的返回值的类型的确定, 确定递归返回值对下一层的递归有什么用
2. 递归体根据每个相同的问题怎么解决来一步一步地编写。
对本题来说,迭代肯定很麻烦,所以使用递归 但是确定轴对称, 我举一个例子, 只有三个节点的树,轴对称如何递归确定。 对将两个孩子, 作为两个参数传入,之后在递归体比较。 因此递归参数确定, 而判断是否是轴对称,可否在递归的过程中判断呢,可以,那么如果不是轴对称,我就可以直接返回,所以返回类型是bool类型。
递归体中, 我们判断是否是轴对称,可以抽象为对靠近轴线的两个孩子进行下一步的判断是否对称,和远离轴线的两个孩子判断是否对称。
核心代码
bool judgeSymmetric(TreeNode* left , TreeNode * right) // 对两棵树进行处理 判断处理,所以参数需要分别传两个
{
if(left == nullptr && right != nullptr)
{
return false ;
}
else if(left !=nullptr && right == nullptr )
{
return false ;
}
else if(left == nullptr && right == nullptr)
{
return true ;
}
else {
if(left->val != right->val )
{
return false ;
}
bool outjudg = judgeSymmetric (left->left , right->right);
bool injudg = judgeSymmetric(left->right , right ->left) ;
bool alljudg = outjudg && injudg ;
return alljudg ;
}
}