二叉树(八):二叉树的全部路径

一、二叉树的全部路径

257、二叉树的全部路径
给定一个二叉树,返回所有从根节点到叶子节点的路径。

1、递归方法获取二叉树的全部路径(推荐)

1)递归方法

要获取二叉树的全部路径:

  • 我们要使用前序遍历,这样才能获取根->孩子节点的访问顺序。
  • 同时要使用回溯算法,在访问一个左孩子后回退到根节点再访问由孩子。

其算法可视化如图:
二叉树(八):二叉树的全部路径_第1张图片
递归三要素:

  • 递归返回值为空,递归参数为当前节点,用于存储当前节点路径的path,用来存放全部路径的res
  • 单层递归逻辑:将当前节点添加到当前节点路径中,并继续递归当前节点的左右孩子节点,以把孩子节点也加入都当前路径中,并在递归填在左右孩子节点后回溯到当前节点
  • 递归结束条件:如果当前节点为叶子节点,将当前节点路径添加到结果中

2 )递归方法代码

class Solution {
public:
	//递归返回值和参数,注意这里的path和res都是引用的
    void travelpath(TreeNode* cur, vector<int>& path, vector<string>& res){

		//单层递归逻辑,添加当前节点到路径中
        path.push_back(cur->val);
        
        //递归终止条件
        if(cur->left == nullptr && cur->right == nullptr){
			//递归终止处理,将路径添加到结果中
            string path_cur = "";
            for(int i = 0; i < path.size() - 1; i++){
                path_cur += to_string(path[i]) + "->";
            }
            path_cur += to_string(path[path.size() - 1]);
            res.push_back(path_cur);
            return;
        }

		//单层递归逻辑,如果当前节点存在左孩子
        if(cur->left){
        	//递归访问左孩子及下面的路径
            travelpath(cur->left, path, res);
            //递归访问完成后,回溯到当前节点
            path.pop_back();
        }
        
        //单层递归逻辑,如果当前节点存在右孩子
        if(cur->right){
        	//递归访问右孩子及下面的路径
            travelpath(cur->right, path, res);
            //递归访问完成后,回溯到当前节点
            path.pop_back();
        }
    }

    vector<string> binaryTreePaths(TreeNode* root) {
    	//初始化
        vector<int> path;
        vector<string> res;
        
        //空树处理
        if(root == nullptr) return res;

		//递归查询全部路径
        travelpath(root, path, res);
        return res;
    }
};

2、递归中隐藏回溯

我们对1中的递归回溯算法进行精简后如下

class Solution {
private:
	//递归函数,注意这里面path不是引用,而是值传递
    void traversal(TreeNode* cur, string path, vector<string>& result) {
        path += to_string(cur->val); // 中
        if (cur->left == NULL && cur->right == NULL) {
            result.push_back(path);
            return;
        }

		//假设调用前path值为path1
        if (cur->left) traversal(cur->left, path + "->", result); // 左  回溯就隐藏在这里 在递归函数中path值发生了变化

		//由于值传递不会影响函数外的变量,因此此时path的值仍为path1,从而实现回溯
        if (cur->right) traversal(cur->right, path + "->", result); // 右 回溯就隐藏在这里
    }

public:
    vector<string> binaryTreePaths(TreeNode* root) {
        vector<string> result;
        string path;
        if (root == NULL) return result;
        traversal(root, path, result);
        return result;
    }
};

递归隐藏回溯的方法

注意这里面的path我们使用的是值传递,这导致:在调用下一层递归函数前,我们的path值为path1,在下一层递归函数中path的值为path2,而调用完下一层递归函数后,我们的path值仍为path1,这是由于值传递时候函数内对值的修改不会影响函数外的变量,从而实现了回溯。

3、迭代方法

1)迭代方法

迭代方法就是访问每一个节点,并将每个节点的路径按照和节点一致的顺序压入另外一个栈中;当遇见叶子节点后,就将叶子节点的路径添加到结果中。需要用到两个栈,一个用于遍历节点,另一个用于存储节点的路径。

实际上就是前序遍历,只不过处理方式变成了将节点对应路径压入结果中。也只能使用前序遍历,因为要先判断根节点是否具有左右孩子才能继续访问左右孩子。

2)迭代代码

class Solution {
public:
    vector<string> binaryTreePaths(TreeNode* root) {

		//初始化
        vector<string> res;
        stack<TreeNode*> node_sta;
        stack<string> path_sta;
        //空树处理
        if(root == nullptr) return res;
        node_sta.push(root);
        path_sta.push(to_string(root->val));

		//当栈不为空时,继续遍历
        while(!node_sta.empty()){

			//取出栈顶的节点和其路径,将其作为当前根节点
            TreeNode* node = node_sta.top();
            string path = path_sta.top();

			//根节点处理
			//如果当前节点时叶子节点,将路径添加到结果中,再弹出,如果不是直接弹出
            if(node->left == nullptr && node->right == nullptr){
                res.push_back(path);
            }
            node_sta.pop();
            path_sta.pop();
            
			//左孩子处理,左孩子节点和其路径如栈
            if(node->left){
                node_sta.push(node->left);
                path_sta.push(path + "->" + to_string(node->left->val));
            }

			//右孩子处理,右孩子节点和其路径入栈
            if(node->right){
                node_sta.push(node->right);
                path_sta.push(path + "->" + to_string(node->right->val));
            }
        }
        return res;
    }
};

你可能感兴趣的:(数据结构与算法,#,二叉树,二叉树,数据结构)