6.二叉树.遍历

深度优先遍历

二叉树的递归遍历

递归算法的三个要素:

  • 确定递归函数的参数和返回值: 确定哪些参数是递归的过程中需要处理的,那么就在递归函数里加上这个参数, 并且还要明确每次递归的返回值是什么进而确定递归函数的返回类型。
  • 确定终止条件: 写完了递归算法, 运行的时候,经常会遇到栈溢出的错误,就是没写终止条件或者终止条件写的不对,操作系统也是用一个栈的结构来保存每一层递归的信息。
  • 确定单层递归的逻辑: 确定每一层递归需要处理的信息。在这里也就会重复调用自己来实现递归的过程。

以前序,中,后遍历为例子:

// 需要处理的参数是节点指针
void traversal(TreeNode* cur, vector<int>& vec) {
	// 若指针为空,则递归结束
    if (cur == NULL) return;
    vec.push_back(cur->val);    // 中
    traversal(cur->left, vec);  // 左
    traversal(cur->right, vec); // 右
}

void traversal(TreeNode* cur, vector<int>& vec) {
    if (cur == NULL) return;
    traversal(cur->left, vec);  // 左
    vec.push_back(cur->val);    // 中
    traversal(cur->right, vec); // 右
}

void traversal(TreeNode* cur, vector<int>& vec) {
    if (cur == NULL) return;
    traversal(cur->left, vec);  // 左
    traversal(cur->right, vec); // 右
    vec.push_back(cur->val);    // 中
}

二叉树的迭代遍历

递归的实现就是:每一次递归调用都会把函数的局部变量、参数值和返回地址等压入调用栈中,然后递归返回的时候,从栈顶弹出上一次递归的各项参数,所以这就是递归为什么可以返回上一层位置的原因。因此我们用栈也可以实现二叉树的前中后序遍历。
一般迭代法

  • 前序遍历:每次先处理的是中间节点,因此先将根节点压入栈中,没弹出一个节点,则将该节点的右,左子节点按顺序压入栈中(空节点不入栈)
  • 中序遍历:前序排列因为要访问的元素和要处理的元素顺序是一致的,都是中间节点;而中序遍历,中序遍历是左中右,先访问的是二叉树顶部的节点,然后一层一层向下访问,直到到达树左面的最底部,再开始处理节点。因此那么在使用迭代法写中序遍历,就需要借用指针的遍历来帮助访问节点栈则用来处理节点上的元素。基本原理是设计一个while循环,并将cur指针初始化指向根节点,当cur非空时,将cur压入栈中,然后将cur指向cur->left,进行下一次循环;当cur空时,将cur更改为栈的top(并将top弹出),并且在结果数组中压入更新后cur->val,随后将cur指向cur->right,进入下一次循环。
class Solution {
public:
	// 中序遍历
    vector<int> inorderTraversal(TreeNode* root) {
        vector<int> result;
        stack<TreeNode*> st;
        TreeNode* cur = root;
        while (cur != NULL || !st.empty()) {
            if (cur != NULL) { // 指针来访问节点,访问到最底层
                st.push(cur); // 将访问的节点放进栈
                cur = cur->left;                // 左
            } else {
                cur = st.top(); // 从栈里弹出的数据,就是要处理的数据(放进result数组里的数据)
                st.pop();
                result.push_back(cur->val);     // 中
                cur = cur->right;               // 右
            }
        }
        return result;
    }
};
  • 后序遍历:所以后序遍历只需要前序遍历的代码稍作修改就可以了,根据次序的不同,通过reversevector数组的元素反转即可。
    在这里插入图片描述

二叉树的统一迭代法

在2题中,无法同时解决访问节点(遍历节点)和处理节点(将元素放进结果集)不一致的情况。此时我们可以将访问的节点放入栈中,把要处理的节点也放入栈中但是要做标记——即要处理的节点放入栈之后,紧接着放入一个空指针作为标记。统一形式也会更好理解,while每次遍历的节点是栈顶的节点,根据不同的遍历次序种类,将cur、cur->lef、cur->right压入栈中,然后利用处理节点的条件cur为空时,进行赋值。

class Solution {
public:
	// 前序排列
    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;
    }
};

广度优先遍历

层序遍历又称为广度优先搜索,层序遍历一个二叉树。就是从左到右一层一层的去遍历二叉树,需要借用一个辅助数据结构即队列来实现,队列先进先出,符合一层一层遍历的逻辑,而用栈先进后出适合模拟深度优先遍历也就是递归的逻辑。

1.二叉树的层序遍历1
一般而言使用std::queue队列存储遍历到的元素,遵循先进先出的原则,当弹出front头部元素时,压入该节点的左,右非空节点,然后进行下一次遍历。

在这里插入代码片

2.二叉树的层序遍历2
给定一个二叉树,返回其节点值自底向上的层次遍历。 (即按从叶子节点所在层到根节点所在的层,逐层从左向右遍历)。这题答案是将二叉树的层序遍历1中使用std::reverse()函数,反转里面的元素即可。


3.二叉树的右视图
定一棵二叉树,想象自己站在它的右侧,按照从顶部到底部的顺序,返回从右侧所能看到的节点值,也就是返回每层最右边的元素即可。在层序遍历的基础上,使用数组存储每层最后一个元素即可。


4.二叉数的层平均值
给定一个非空二叉树, 返回一个由每层节点平均值组成的数组。在层序遍历的基础上,使用数组存储每层元素的平均值。


5.N叉树的层序遍历
给定一个 N 叉树,返回其节点值的层序遍历。 (即从左到右,逐层遍历)。
在这里插入图片描述
N叉树的层序遍历与二叉数的原理一致,在压入子字节点时需要使用一个for循环遍历所有的字节点接口。


6.在每个树行中找最大值
给定一个非空二叉树, 返回一个由每层节点最大值组成的数组。在层序遍历的基础上,使用数组存储每层元素的最大值,即在每层遍历时加入一个数值比较的环节。


7.填充每个节点的下一个右侧节点指针
给定一个完美二叉树,其所有叶子节点都在同一层,每个父节点都有两个子节点。填充它的每个 next 指针,让这个指针指向其下一个右侧节点。如果找不到下一个右侧节点,则将 next 指针设置为 NULL。

struct Node {
  int val;
  Node *left;
  Node *right;
  Node *next;
}

在这里插入图片描述
在层序遍历的基础上,在单层遍历的时候记录一下本层的头部节点,然后在遍历的时候让前一个节点指向本节点就可以了,类似双指针的方法,令L_p,R_p两个指针,R_p指针用于随层序遍历更新位置,L_p用于晚于R_p更新一步。

    Node* connect(Node* root) {
        queue<Node*> que;
        if (root != NULL) que.push(root);
        while (!que.empty()) {
            int size = que.size();  // 获取每层的元素个数
            // vector<int> vec;
            Node* nodePre;
            Node* node;
            for (int i = 0; i < size; i++) {
                if (i == 0) {
                    nodePre = que.front(); // 取出一层的头结点
                    que.pop();
                    node = nodePre;
                } else {
                    node = que.front();
                    que.pop();
                    nodePre->next = node; // 本层前一个节点next指向本节点
                    nodePre = nodePre->next;
                }
                if (node->left) que.push(node->left);
                if (node->right) que.push(node->right);
            }
            nodePre->next = NULL; // 本层最后一个节点指向NULL
        }
        return root;
    }

8.二叉树的最大深度
给定一个二叉树,找出其最大深度。二叉树的深度为根节点到最远叶子节点的最长路径上的节点数( 叶子节点是指没有子节点的节点
使用层序遍历法来解决正好,每次完成一层的遍历,便计数一次。

    int maxDepth(TreeNode* root) {
        if (root == NULL) return 0;
        int depth = 0;
        queue<TreeNode*> que;
        que.push(root);
        while(!que.empty()) {
            int size = que.size();
            depth++; // 记录深度
            for (int i = 0; i < size; i++) {
                TreeNode* node = que.front();
                que.pop();
                if (node->left) que.push(node->left);
                if (node->right) que.push(node->right);
            }
        }
        return depth;
    }

9.二叉树的最小深度
同样使用层序遍历,只有当左右孩子都为空的时候,才说明遍历的最低点了。如果其中一个孩子为空则不是最低点。这要遍历到该节点的左,右子节点都是nullptr,即可返回该层数。

   int minDepth(TreeNode* root) {
        if (root == NULL) return 0;
        int depth = 0;
        queue<TreeNode*> que;
        que.push(root);
        while(!que.empty()) {
            int size = que.size();
            depth++; // 记录最小深度
            for (int i = 0; i < size; i++) {
                TreeNode* node = que.front();
                que.pop();
                if (node->left) que.push(node->left);
                if (node->right) que.push(node->right);
                if (!node->left && !node->right) { // 当左右孩子都为空的时候,说明是最低点的一层了,退出
                    return depth;
                }
            }
        }
        return depth;
1. 二叉树的递归遍历二叉树的递归遍历是指通过递归方法遍历二叉树的各个节点,按照某种次序访问每个节点。常见的二叉树遍历方式有前序遍历、中序遍历和后序遍历。 2. 二叉树的非递归遍历二叉树的非递归遍历是指通过循环等非递归方法遍历二叉树的各个节点,按照某种次序访问每个节点。非递归遍历需要借助栈来实现,常见的二叉树遍历方式有前序遍历、中序遍历和后序遍历。 3. 二叉树的层次遍历二叉树的层次遍历是指按照从上到下、从左到右的顺序遍历每一层节点。常用的方法是使用队列来实现,首先将根节点入队列,然后依次出队列,并将其左右子节点入队列,直到队列为空。 4. 输出二叉树上所有叶节点: 二叉树上的叶节点是指没有子节点的节点。可以通过递归方式,对每个节点进行判断,如果该节点没有左右子节点,则将该节点输出。 5.二叉树的高度: 二叉树的高度是指从根节点到叶节点最长路径上经过的边数。可以通过递归方式求解,从左右子树中选取较大的一个加上根节点即可。 6. 二叉树层序生成算法二叉树层序生成算法是指按照从上到下、从左到右的顺序依次生成每个节点。可以使用队列来实现,首先将根节点入队列,然后依次出队列,并根据当前节点生成其左右子节点,将其入队列,直到生成完所有节点。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值