代码随想录 | day14|day15

二叉树开始

理论基础
迭代遍历树
我认为迭代遍历是用栈存储要利用的结点。以供需要时弹出
我这里的思路是普遍的写法,卡哥的是比较易懂的写法

前序遍历 ,

在遍历的时候,加入结果
这里有个关键:结点不为空时,总是向左孩子遍历,节点为空后,就弹出遍历过的元素,向其右孩子遍历,这是靠画图得出的。
核心代码

   // 这里有个关键:结点不为空时,总是向左孩子遍历,节点为空后,就弹出遍历过的元素,向其右孩子遍历,这是靠画图得出的。
        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 ; 
        }
    }
  • 17
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值