【数据结构】二叉树遍历

一、二叉树定义

C++定义:

struct TreeNode{
	int val;
	TreeNode *left;
	TreeNode *right;
	TreeNode(): val(0), left(NULL), right(NULL) {}
	TreeNode(int x) : val(x), left(NULL), right(NULL){}
	TreeNode(int x, TreeNode * left, TreeNode * right) : val(x), left(left), right(right){}
};

Python定义:

class  TreeNode:
	def __init__(self, value):
		self.value = value
		self.right = None
		self.left = None

Java定义:

public class TreeNode{
	int val;
	TreeNode left;
	TreeNode right;
	TreeNode() {}
	TreeNode(int val) {this.val = val;}
	TreeNode(int val, TreeNode left, TreeNode right){
		this.val = val;
		this.left = left;
		this.right = right;
	}
}

二、二叉树遍历

二叉树主要有两种遍历方式:

  1. 深度优先遍历,先走到叶子节点再往回走
    a> 前序遍历
    b>中序遍历
    c>后序遍历
    二叉树的前中后序遍历,其中的前中后指的是父节点的访问顺序;使用迭代法来进行前中后序遍历都是借助来实现的;
  2. 层序优先遍历,故名思意,一层一层遍历
    a>层次遍历

2.1 递归遍历

递归三要素,以前序遍历为例:

  1. 确定递归函数的参数和返回值

    确定哪些参数是递归的过程中需要处理的,那么就在递归函数里加上这个参数, 并且还要明确每次递归的返回值是什么进而确定递归函数的返回类型

    void preorder(TreeNode * cur, vector<int>&vec)
    
  2. 确定终止条件

    没有终止条件,肯定会出现栈溢出

    if (cur == NULL) return;
    
  3. 确定单层递归的逻辑

    确定每一层递归需要处理的信息,这里也就会重复调用自己来实现递归的过程

    vec.push_back(cur->val);
    traversal(cur->left, vec);
    traversal(cur->right, vec);
    

结合上述三步,二叉树的前序遍历如下:

class Solution{
	public:
		void traversal(TreeNode * cur, vector<int>& vec){
			if (cur==NULL){
				return;
			}
			vec.push_back(cur->val);
			traversal(cur->left, vec);
			traversal(cur->right, vec);
		}
		vector<int> preorderTraversal(TreeNode * root){
			vector<int> res;
			traversal(root, res);
			return res;

2.2 迭代遍历

2.2.1 迭代前序遍历

自然使用栈来解决迭代遍历;前序遍历的顺序是:父结点、左孩子、右孩子;那么使用栈来解决,先将父结点入栈,剩余入栈顺序就是右孩子、左孩子;
迭代实例
在这里插入图片描述

  1. 首先要初始化一个空栈,首先将根节点放入其中;
  2. 此后进入循环,每次的动作都是弹出当前的栈顶,并且随即访问该节点
  3. 而后检查刚被访问的节点是否拥有右孩子,有则令其入栈;
  4. 接着检查刚被访问的节点是否拥有左孩子,有则令其入栈;
  5. 循环进入下一步迭代
class Solution{
	pubilc:
		vector<int> preorder(TreeNode * root){
			stack<TreeNode*> st;
			vector<int> res;
			if (root == NULL) return res;
			st.push(root);
			while(!st.empty()){
				TreeNode* node = st.top();
				st.pop();
				res.push_back(node->val);
				if(node->right) st.push(node->right);
				if(node->left) st.push(node->left);
			}
			return res;
		}
};

2.2.2 迭代中序遍历

中序遍历和先序遍历的代码不能经过简单的改动就实现,这是因为,两个遍历首先访问的都是中间节点,但是先序遍历访问到中间节点就进行处理,也就是其访问顺序与处理顺序是一致;而对于中序遍历来说,其访问节点的顺序是左中右,访问到中间节点之后要不断迭代访问到其左子树的最左子结点,然后再对节点进行处理,这也就造成了中序遍历的访问顺序和处理顺序不一致
要完成二叉树的中序遍历,需要借助指针的遍历来访问节点栈则用来处理节点上的元素
在这里插入图片描述

  1. 首先从根节点出发沿着左分支下行,直到最深的节点,这也是全局首先被访问的节点
  2. 很自然的,要完成二叉树的中序遍历,在利用指针对左侧链进行遍历的过程中,使用栈结构存储左侧链上沿途节点,指针指向到左侧链最深的节点处的左孩子时(也就是NULL),将最后进入栈的元素弹出并处理,观察其是否有右孩子,若有则将指针指向该右孩子,并重复上面的”左侧链遍历”的方式;若没有右孩子,则栈继续弹出并访问节点元素。
  3. 使用栈结构完成中序遍历的实例如下图所示:
    在这里插入图片描述
/**
 * 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  * cur = root;
        while(cur != NULL || !st.empty()){
            if(cur != NULL){
                st.push(cur);
                cur = cur->left;
            }else{
                cur = st.top();
                st.pop();
                res.push_back(cur->val);
                cur = cur->right;
            }
        }
        return res;
    }
};

2.2.3 迭代后序遍历

先序遍历是中左右,后序遍历是左右中,要完成后序遍历就只需要调整先序遍历,使其编程中右左的遍历顺序,然后反转result数组,输出的结果就是左右中
中左右—改变中序遍历顺序–>中右左—反转result顺序–>左右中

class Solution {
public:
    vector<int> postorderTraversal(TreeNode* root) {
        stack<TreeNode*> st;
        vector<int> result;
        if (root == NULL) return result;
        st.push(root);
        while (!st.empty()) {
            TreeNode* node = st.top();
            st.pop();
            result.push_back(node->val);
            if (node->left) st.push(node->left); // 相对于前序遍历,这更改一下入栈顺序 (空节点不入栈)
            if (node->right) st.push(node->right); // 空节点不入栈
        }
        reverse(result.begin(), result.end()); // 将结果反转之后就是左右中的顺序了
        return result;
    }
};

2.3 层序遍历

层次遍历故名思意就是一层一层来进行遍历,以下图二叉树为例,其层次遍历结果为[F, B, G, A, D, I, C, E, H] ;层次遍历的实现很简单,需要使用到先进先出的队列以及一个变量size来记录每一层节点的个数。
在这里插入图片描述
代码如下:

/**
 * 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*> que;
        vector<vector<int>> res;

        if(root == NULL){
            return res;
        }

        que.push(root);

        while(!que.empty()){
            int size = que.size();
            vector<int> vec;

            for(int i = 0; i< size; i++){
                TreeNode* cur = que.front();
                que.pop();
                vec.push_back(cur->val);
                if(cur->left){
                    que.push(cur->left);
                }
                if(cur->right){
                    que.push(cur->right);
                }
            }
            res.push_back(vec);
        }
        return res;
    }
};

三、翻转二叉树

翻转二叉树使用前序遍历或者后序遍历,使用中序遍历比较麻烦,使用层序遍历也是可以的;
翻转二叉树就是在遍历每一个节点时,交换其左子树和右子树

  1. 层序遍历代码如下,在对每一层的每一个节点进行遍历时,交换其左孩子和右孩子:

    /**
     * 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:
        TreeNode* invertTree(TreeNode* root) {
            queue<TreeNode *> que;
            
            if(root==NULL){
                return root;
            }
            que.push(root);
            
            while(!que.empty()){
                int size = que.size();
                for(int i = 0; i < size; i++){
                    TreeNode * cur = que.front();
                    que.pop();
                    swap(cur->left, cur->right);
                    if(cur->left){
                        que.push(cur->left);
                    }
                    if(cur->right){
                        que.push(cur->right);
                    }
                }
            }
            return root;
    
        }
    };
    
  2. 前序迭代遍历:

    /**
     * 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:
        TreeNode* invertTree(TreeNode* root) {
            stack<TreeNode *> st;
            vector<int> res;
            if(root == NULL){
                return root;
            }
            st.push(root);
            
            while(!st.empty()){
                TreeNode * cur = st.top();
                st.pop();
                swap(cur->right, cur->left);
                if(cur->right){
                    st.push(cur->right);
                }
                if(cur->left){
                    st.push(cur->left);
                }
            }
            
            return root;
        }
    };
    
  3. 前序递归遍历:

    /**
     * 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:
        TreeNode* invertTree(TreeNode* root) {
            if(root == NULL){
                return root;
            }
            swap(root->left, root->right);
            invertTree(root->left);
            invertTree(root->right);
            return root;
    
        }
    };
    

四、参考

  1. 代码随想录-层序遍历
  2. 邓俊辉老师的数据结构
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
二叉树遍历特性主要包括三种遍历方式:先序遍历、中序遍历和后序遍历。先序遍历是指先访问根节点,然后按照先序遍历的顺序依次访问左子树和右子树。中序遍历是指先访问左子树,然后访问根节点,最后访问右子树。后序遍历是指先访问左子树,然后访问右子树,最后访问根节点。这三种遍历方式都是通过递归或者使用辅助数据结构(如栈或队列)来实现的。其中,递归是一种较为简洁的实现方式,但由于递归的栈帧消耗较大,所以使用非递归的方式来遍历二叉树也是非常有必要的。非递归遍历二叉树可以借助队列的先进先出的特性来实现。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* *2* [数据结构7:基本的二叉树遍历及题目](https://blog.csdn.net/m0_53607711/article/details/128331361)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 50%"] - *3* [数据结构实验 二叉树遍历方法](https://download.csdn.net/download/yuan7376313/3174711)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 50%"] [ .reference_list ]

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值