二叉树基础(遍历)

news/2024/6/18 19:16:41 标签: 数据结构, 算法, 二叉树, 1024程序员节

目录

 二叉树以数组形式的顺序存储结构

获取节点的双亲

 删除二叉树

二叉树遍历

前序遍历(递归)

前序遍历(非递归)

中序遍历(递归)

中序遍历(非递归)

后序遍历(递归)

后序遍历(非递归)

模板

前序遍历迭代法

中序遍历迭代法

后序遍历迭代法

层次遍历

更新后序遍历非递归(更简便理解)



 二叉树以数组形式的顺序存储结构



二叉树的数组存储及运算_zhangwang010的博客-CSDN博客_二叉树的数组存储

在整个二叉树当中,最重要的是遍历(遍历的应用)、建树(前中后序层次建树)、搜索

首先是定义二叉树

template <class T> 
struct BinTreeNode{
    T data;
    BinTreeNode<T> *leftChild,*rightChild;
    BinTreeNode():leftChild(NULL),rightChild(NULL){}
    BinTreeNode(T x,BinTreeNode<T> *l=NULL,BinTreeNode<T> *r=NULL)
        :data(x),leftChild(l),rightChild(r){}
}

获取节点的双亲

template <class T>
BinTreeNode<T> *BinaryTree<T>::
Parent (BinTreeNode <T> *subTree, BinTreeNode <T> *t) {
    if (subTree == NULL) return NULL;
     if (subTree->leftChild == t || subTree->rightChild == t ) 
          return subTree;	//找到, 返回父结点地址
     BinTreeNode <T> *p;
     if ((p = Parent (subTree->leftChild, t)) != NULL)  
          return p;	         //递归在左子树中搜索
     else 
          return Parent (subTree->rightChild, t);
 				         //递归在右子树中搜索
};

 删除二叉树

template<class T> 
void BinaryTree<T>::
destroy (BinTreeNode<T> * subTree) {
//私有函数: 删除根为subTree的子树
     if (subTree != NULL) {
          destroy (subTree->leftChild);     //删除左子树
 	      destroy (subTree->rightChild);   //删除右子树
 	      delete subTree; 			 //删除根结点
	 }
};

二叉树遍历

不会吧不会吧,前中后序的遍历还不知道!!?(b站有大把视频) 

因为二叉树遍历非常重要,又有两种遍历方式(递归与非递归)

递归难在理解,虽然递归的代码实现很简单,但是千万不能死记硬背,要明白递归机制。

非递归难在代码实现。。。(理解也挺麻烦的),然后我找到一个大佬的博客,写的还不错,推荐看这个遍历之前点开一下这个链接二叉树的三种遍历非递归实现 - aspirant - 博客园 (cnblogs.com)

前序遍历(递归)

144. 二叉树的前序遍历 - 力扣(LeetCode) (leetcode-cn.com)

template <class T>
void BinaryTree<T>::PreOrder (BinTreeNode<T> * subTree, void (*visit) (BinTreeNode<T> *t)) {
     if (subTree != NULL) {
		visit (subTree);		//访问根结点
		PreOrder (subTree->leftChild, visit);
 		 				//遍历左子树
		PreOrder (subTree->rightChild, visit);
  						//遍历右子树
	 }
};

前序遍历(非递归)

/**
 * Definition for a binary tree node.
 * struct TreeNode {
 *     int val;
 *     TreeNode *left;
 *     TreeNode *right;
 *     TreeNode() : val(0), left(nullptr), right(nullptr) {}
 *     TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
 *     TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {}
 * };
 */
class Solution {
public:
    vector<int> preorderTraversal(TreeNode* root) {
        vector<int> ans;
        stack<TreeNode*> s;//存树节点的栈
        s.push(root);
        while(!s.empty()){
            TreeNode* r=s.top();//取节点
            s.pop();//弹出栈的节点
            if(!r) continue;//当根节点为空时,跳过
            ans.push_back(r->val);//根据栈的先进后访问,然后根据先序遍历:根-左-右
            s.push(r->right);
            s.push(r->left);     
        }
        return ans;
    }
};

 图解

中序遍历(递归)

template <class T>
void BinaryTree<T>::InOrder (BinTreeNode<T> * subTree, void (*visit) (BinTreeNode<T> *t)) {
     if (subTree != NULL) {
          InOrder (subTree->leftChild, visit); 
                                                   //遍历左子树
          visit (subTree);		//访问根结点
          InOrder (subTree->rightChild, visit);
                		                    //遍历右子树
	 }
};

中序遍历(非递归)

94. 二叉树的中序遍历 - 力扣(LeetCode) (leetcode-cn.com)

/**
 * Definition for a binary tree node.
 * struct TreeNode {
 *     int val;
 *     TreeNode *left;
 *     TreeNode *right;
 *     TreeNode() : val(0), left(nullptr), right(nullptr) {}
 *     TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
 *     TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {}
 * };
 */
class Solution {
public:
    vector<int> inorderTraversal(TreeNode* root) {
    vector<int> res;
    stack<TreeNode*> st;
    TreeNode* p = root;

    // 左链入栈循环
    // 这个循环你会发现和后面的的循环代码有一部分重复,你可以进行合并。
    // 但是这样你就需要在后面循环条件多加一个判断 while (p || !st.empty()),代码也会变得稍微没那么直观。你可以参考一下版本二。
    // 为了方便学习还是分开考虑为宜。优化的事情在你学习完后再考虑。
    while (p) {
        st.push(p);
        p = p->left;
    }
    while (!st.empty()) {
        TreeNode * node = st.top();
        st.pop();
        res.push_back(node->val);
        p = node->right;
        // 一旦弹出一个节点,继续做“左链入栈”操作
        while (p) {
            st.push(p);
            p = p->left;
        }
    }   
    return res;
    }
};

牢记“左链入栈”

后序遍历(递归)

 145. 二叉树的后序遍历 - 力扣(LeetCode) (leetcode-cn.com)

template <class T>
void BinaryTree<T>::PreOrder (BinTreeNode<T> * subTree, void (*visit) (BinTreeNode<T> *t)) {
     if (subTree != NULL) {
		visit (subTree);		//访问根结点
		PreOrder (subTree->leftChild, visit);
 		 				//遍历左子树
		PreOrder (subTree->rightChild, visit);
  						//遍历右子树
	 }
};

后序遍历(非递归)

根据后序遍历的:左-右-根

再由图可知

/**
 * Definition for a binary tree node.
 * struct TreeNode {
 *     int val;
 *     TreeNode *left;
 *     TreeNode *right;
 *     TreeNode() : val(0), left(nullptr), right(nullptr) {}
 *     TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
 *     TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {}
 * };
 */
class Solution {
public:
    vector<int> postorderTraversal(TreeNode* root) {
        
        vector<int> res;
        stack<TreeNode*> s;
        TreeNode* p = root;
        TreeNode* r = nullptr;//作用是保存上一次执行的节点

        while(!s.empty() || p){//遍历循环
            if(p)
            {            //走左子树下去,一直保存
                s.push(p);
                p = p->left;
            }
            else
            {
              p = s.top();
                if(p->right == nullptr || p->right == r)
                {                    //判断是否为无右子树节点或者是右子树的父节点
                  res.push_back(p->val);
                  s.pop();                   //注意弹出栈的时机(相对于前中序)
                  r = p;//保留这一次的节点,使得下一次判断新的节点是否为这个节点的父节点
                  p = nullptr;              //置空返回上一层节点
                }
                else                         //这个else要注意
                {
                    p = p->right;    //进入右子树
                }   
            }
        }
        return res;
    }
};

迭代法一般都会有模板

还是推荐理解了过后再用模板(总体思路和上面代码一样),切勿死记硬背

模板

while( 栈非空 || p 非空)
{
if( p 非空)
{

}
else
{

}
}

前序遍历迭代法

class Solution {
public:
    vector<int> preorderTraversal(TreeNode* root) {

        vector<int> res;
        stack<TreeNode*> s;
        TreeNode* p = root;

        while(!s.empty() || p){
            if(p){
                res.push_back(p->val);
                s.push(p);
                p = p->left;
            }else {
                p = s.top();
                s.pop();
                p = p->right;                       
            }
        }

        return res;
    }
};

中序遍历迭代法

class Solution {    
public:
    vector<int> inorderTraversal(TreeNode* root) {

        vector<int> res;
        stack<TreeNode*> s;
        TreeNode* p = root;

        while(!s.empty() || p){
            if(p){
                s.push(p);
                p = p->left;
            }else{
                p = s.top();
                s.pop();
                res.push_back(p->val);
                p = p->right;
            }
        }

        return res;
    }
};

后序遍历迭代法

class Solution {
public:
    vector<int> postorderTraversal(TreeNode* root) {
        
        vector<int> res;
        stack<TreeNode*> s;
        TreeNode* p = root;
        TreeNode* r = nullptr;

        while(!s.empty() || p){
            if(p){
                s.push(p);
                p = p->left;
            }else{
                p = s.top();
                if(p->right == nullptr || p->right == r){
                    res.push_back(p->val);
                    s.pop();
                    r = p;
                    p = nullptr;
                }else 
                    p = p->right;
            }
        }
        return res;
    }
};

层次遍历

102. 二叉树的层序遍历 - 力扣(LeetCode) (leetcode-cn.com)

 

 

/**
 * Definition for a binary tree node.
 * struct TreeNode {
 *     int val;
 *     TreeNode *left;
 *     TreeNode *right;
 *     TreeNode() : val(0), left(nullptr), right(nullptr) {}
 *     TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
 *     TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {}
 * };
 */
class Solution {
public:
    vector<vector<int>> levelOrder(TreeNode* root) {
        queue<TreeNode*> Q;
        if(root!=NULL){
            Q.push(root);//Q.enqueue(root);
        }
        vector<vector<int>> a;
        while(!Q.empty()){
            vector<int> tmp;
            int len=Q.size();
            for(int i=0;i<len;i++){
                TreeNode* x=Q.front();
                Q.pop();//Q.dequeue();
                tmp.push_back(x->val);
                if(x->left!=NULL){
                    Q.push(x->left);
                }
                if(x->right!=NULL){
                    Q.push(x->right);
                }
            }
        a.push_back(tmp);
        }
        return a;
    }
};

更新后序遍历非递归(更简便理解)

发现上面的那些模板太难理解,怎么说呢,只适合记忆临时抱佛脚的

推荐这个,也是在leetcode看到的题解

双栈,其实一个栈来存储后序遍历的顺序,然后根据栈的特性进行遍历!

/**
 * Definition for a binary tree node.
 * struct TreeNode {
 *     int val;
 *     TreeNode *left;
 *     TreeNode *right;
 *     TreeNode() : val(0), left(nullptr), right(nullptr) {}
 *     TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
 *     TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {}
 * };
 */
class Solution {
public:
    vector<int> postorderTraversal(TreeNode* root) {
        
        vector<int> res;
        stack<TreeNode*> s;
        stack<TreeNode*> r;//存储栈
        s.push(root);
        while(!s.empty()){
            TreeNode *p=s.top();
            s.pop();

            if(!p) continue;  //节点为空就不要存入栈中了

            r.push(p);    //存入栈中!

            if(p->left!=NULL){   //根据后序遍历的遍历顺序:左-右-根
                s.push(p->left);
            }
            if(p->right!=NULL){
                s.push(p->right);
            }
        }

        while(!r.empty()){//开始输出后序遍历
            TreeNode *op=r.top();
            res.push_back(op->val);//当然这里也可以换成cout输出
            r.pop();
        }
        return res;
    }
};


http://www.niftyadmin.cn/n/1132512.html

相关文章

谈谈企业的数据工作!——企业的数据分析能力金字塔

写在前面 笔者写这篇文章的初衷源于两个故事&#xff1a; 故事一&#xff1a;一位在互联网行业做数据库架构多年的同事一起吃饭&#xff0c;问起我现在在说什么&#xff0c;我说自己在做医疗方面数据分析&#xff0c;同事笑&#xff0c;说&#xff1a;你有很多资源啊&#xff0…

29.typedef

本文目录一、typedef作用简介二、typedef与指针三、typedef与结构体三、typedef与指向结构体的指针四、typedef与枚举类型五、typedef与指向函数的指针六、typedef与#define说明&#xff1a;这个C语言专题&#xff0c;是学习iOS开发的前奏。也为了让有面向对象语言开发经验的程…

web性能优化

2019独角兽企业重金招聘Python工程师标准>>> 1.减少http请求&#xff0c;图片&#xff0c;css&#xff0c;字体&#xff0c;JavaScript&#xff0c;flash&#xff0c;等等网络文件&#xff0c;都会增加 http 请求数&#xff0c;减少这些信息的数量能提高Web页面响应…

敏捷BI与数据驱动机制

数据驱动是一种文化 大数据这件事&#xff0c;整体上还是说的多一些&#xff0c;做的稍微少一点。大数据可以是荒凉高原上波澜壮阔的机房&#xff0c;也可以润物细无声般融入到日常生活和工作。换句话说&#xff0c;大数据应该是一种文化。 在个人层面&#xff0c;很多人对数字…

php中特殊字符回车、换行

在php中过滤回车、换行 \r 回车 \n 换行 $str str_replace(array("\n","\r"),"",$str);//过滤换行回车

CentOS系统不能识别NTFS、exFAT格式

解决办法&#xff1a;挂载NTFS格式(以64位系统为例)1、下载rpmforge的rpm文件包32位系统wget http://pkgs.repoforge.org/rpmforge-release/rpmforge-release-0.5.2-1.el6.rf.i686.rpm64位系统wget http://pkgs.repoforge.org/rpmforge-release/rpmforge-release-0.5.2-2.el6.r…

你给我这么多报表,让我如何是好

文 | 王佳东fr 本文出自&#xff1a;知乎专栏《撩撩数据吧》——关于数据分析 数据是日积月累的&#xff0c;一个有点规模的企业&#xff0c;都有大量的报表在excel中&#xff0c;时间长了&#xff0c;感觉到excel制作报表的各种痛点&#xff1a;什么数据收集麻烦啊、各个系统…

二叉树(递归应用+建树)

由上一个部分可知二叉树基础&#xff0c;现在看看二叉树的一些应用&#xff1b; 目录 计算root为根的二叉树结点个数(了解后序遍历的基础) 求树的高度或深度(后序遍历) 如何把前、中、后序递归算法的最后一条递归调用语句消除呢&#xff1f;教材习题248的25题 寻找树的先序…