recursive

须知

  1. 参数列表的那些参数是上层传到下层的,是下层需要知道的关于上层节点的信息;
  2. 返回值是下层传回上层的,是上层需要知道的下面所有节点的情况信息(例如,左子树的信息);
  3. 当参数中有引用时,注意此参数无副本,需要在整个流程中维护此参数的一致性,例如push_back()之后记得在return前pop_back()

1. 体会返回值和参数在递归中 如何使用

1.1 path sum
Given a binary tree and a sum, determine if the tree has a root-to-leaf path such that adding up all the values along the path equals the given sum.

For example:
Given the below binary tree and sum = 22,

              5
             / \
            4   8
           /   / \
          11  13  4
         /  \      \
        7    2      1

return true, as there exist a root-to-leaf path 5->4->11->2 which sum is 22.

class Solution {
private:
    bool visit(TreeNode *node, int sum, int now) {
        now += node->val;
        if (node->left == NULL && node->right == NULL)
            return sum == now;

        bool isleft = false, isright = false;

        if (node->left != NULL)
            isleft = visit(node->left, sum, now);

        if (node->right != NULL)
            isright = visit(node->right, sum, now);

        return isleft || isright;
    }
public:
    bool hasPathSum(TreeNode *root, int sum) {
        if (root == NULL) return false;
        return visit(root, sum, 0);
    }

};

此题中, 下层需要知道:上面路径上的节点到底sum是多少了;而上层则需要知道:子树有没有路径和为sum的

这么写也可以:(下面有没有和为sum - root->val的路径?)

class Solution {
private:
    bool visit(TreeNode *node, int sum) {
        sum -= node->val;
        if (node->left == NULL && node->right == NULL)
            return sum == 0;

        bool isleft = false, isright = false;

        if (node->left != NULL)
            isleft = visit(node->left, sum);

        if (node->right != NULL)
            isright = visit(node->right, sum);

        return isleft || isright;
    }
public:
    bool hasPathSum(TreeNode *root, int sum) {
        if (root == NULL) return false;
        return visit(root, sum);
    }

};

1.2 Balanced Binary Tree
返回值: 用来返回给上层调用。可以传递给上
层下层(例如子树)的信息。

有返回值版本

class Solution {
public:
    bool isBalanced(TreeNode *root) {
        bool isbal = true;
        int height = 0;
        visit(root, isbal);
        return isbal;
    }
    int visit(TreeNode *node, bool &isbal) {
        if (node == NULL) {
            isbal = true;
            return 0;
        }

        bool isballeft;
        int heightleft = visit(node->left, isballeft);

        bool isbalright;
        int heightright = visit(node->right, isbalright);

        int height = max(heightleft, heightright) + 1;

        if (isballeft && isbalright && abs(heightleft - heightright) <= 1)
            isbal = true;
        else
            isbal = false;

        return height;
    }
};

无返回值版本

class Solution {
public:
    bool isBalanced(TreeNode *root) {
        bool isbal = true;
        int height = 0;
        visit(root, isbal, height);
        return isbal;
    }
    void visit(TreeNode *node, bool &isbal, int &height) {
        if (node == NULL) {
            isbal = true;
            height = 0;
            return;
        }

        bool isballeft;
        int heightleft;
        visit(node->left, isballeft, heightleft);

        bool isbalright;
        int heightright;
        visit(node->right, isbalright, heightright);

        height = max(heightleft, heightright) + 1;

        if (isballeft && isbalright && abs(heightleft - heightright) <= 1)
            isbal = true;
        else
            isbal = false;
    }
};

2. 体会 传引用 在递归中 如何使用

传引用在整个程序运行中只有这一个东西,没有副本,所以它可以用来跨层传递信息。
2.1 Path Sum II
Given a binary tree and a sum, find all root-to-leaf paths where each path’s sum equals the given sum.
For example:
Given the below binary tree and sum = 22,

              5
             / \
            4   8
           /   / \
          11  13  4
         /  \    / \
        7    2  5   1

return

[
   [5,4,11,2],
   [5,8,4,5]
]

本题中,path在整个调用过程中只有一个,没有副本,在return之前都要pop_back()一下,把之前加入的节点去掉,从而维护上层节点path的一致性。另外把中途得到的结果存入rs。因为等程序运行完,path应该是空的。

class Solution {
public:
    vector<vector<int> > pathSum(TreeNode *root, int sum) {
        vector<int> path;
        vector<vector<int>> rs;
        if (root == NULL) return rs;
        visit(root, sum, path, rs);
        return rs;
    }

    void visit(TreeNode *node, int sum, vector<int> &path, vector<vector<int>> &rs) {
        sum -= node->val;
        path.push_back(node->val);

        if (node->left == NULL && node->right == NULL) {
            if (sum == 0) rs.push_back(path);
            path.pop_back();
            return;
        }

        if (node->left != NULL) 
            visit(node->left, sum, path, rs);
        if (node->right != NULL)
            visit(node->right, sum, path, rs);

        path.pop_back();
    }
};

或者:

class Solution {
public:
    vector<vector<int> > pathSum(TreeNode *root, int sum) {
        vector<vector<int>> rs;
        if (root == NULL) return rs;
        vector<int> tmp;
        visit(root, sum, tmp, rs);
        return rs;
    }

    void visit(TreeNode *root, int sum, vector<int> &tmp, vector<vector<int>> &rs)
    {
        sum -= root->val;
        tmp.push_back(root->val);
        if (root->left == NULL && root->right == NULL && sum == 0)
        {
            rs.push_back(tmp);
            return;
        }
        if (root->left)
        {
            visit(root->left, sum, tmp, rs);
            tmp.pop_back();
        }
        if (root->right)
        {
            visit(root->right, sum, tmp, rs);
            tmp.pop_back();
        }
    }
};

注意,第一个版本运行完,path是空的;而第二个版本运行完path.size() 是1。因为第一个版本每处理一个node,函数的所有出口都pop_back(); 而第二个版本,只有处理完左子树或右子树的时候才会pop_back()。
个人倾向于第一种写法。

2.2 Subset II (讲DFS的另一篇blog: http://blog.csdn.net/legend0011/article/details/41827697)
用tmp来维护当前path的内容,与2.1同理。

class Solution {
private:
    void dfs(vector<int> &S, vector<int> &tmp, vector<vector<int>> &rs, int i) {
        rs.push_back(tmp);
        for (int k = i; k < S.size(); ++k) {
            if (k > i && S[k] == S[k-1]) continue;  //去重
            tmp.push_back(S[k]);
            dfs(S, tmp, rs, k+1);
            tmp.pop_back();
        }
    }
public:
    vector<vector<int> > subsetsWithDup(vector<int> &S) {
        vector<int> tmp;
        vector<vector<int>> rs;
        sort(S.begin(), S.end());
        dfs(S, tmp, rs, 0);
        return rs;
    }
};

2.2 Convert Sorted List to Binary Search Tree
递归中 传引用起到的作用 >= 返回值的作用
也就是说返回值可以干的事,通过传引用都可以

中序遍历取list中的元素,每new一个TreeNode就往后挪一个。想想如何靠传 ListNode *&head 来达到如此的效果。体会传引用的作用。

class Solution {
private:
    TreeNode *buildHelper(ListNode *&head, int l, int r) {  //中序遍历的顺序 来取list中的元素。
        if (l > r) return NULL; 

        int mid = l + (r - l) / 2;
        TreeNode *left = buildHelper(head, l, mid - 1);
        TreeNode *root = new TreeNode(head->val);
        root->left = left;
        head = head->next;
        root->right = buildHelper(head, mid + 1, r);
        return root;
    }

public:
    TreeNode *sortedListToBST(ListNode *head) {
        int n = 0;
        for (ListNode *t = head; t != NULL; t = t->next) {
            n++;
        }

        return buildHelper(head, 0, n - 1);
    }
};

2.3

3. 子问题的定义方式

子问题的定义方式不只一种!
Symmetric Tree
Given a binary tree, check whether it is a mirror of itself (ie, symmetric around its center).

For example, this binary tree is symmetric:

    1
   / \
  2   2
 / \ / \
3  4 4  3

But the following is not:

    1
   / \
  2   2
   \   \
   3    3

子问题的定义方式为,左子树的左子树和右子树的右子树是否一致,左子树的右子树和右子树的左子树是否一致:

class Solution {
public:
    bool isSymmetric(TreeNode *root) {
        if (root == NULL) return true;

        return isgood(root->left, root->right);
    }
    bool isgood(TreeNode *l, TreeNode *r) {
        if (l == r) return true;  //both are NULL
        if (l == NULL && r != NULL) return false;
        if (l != NULL && r == NULL) return false;

        if (l->val == r->val)
            return isgood(l->left, r->right) && isgood(l->right, r->left);
        else
            return false;
    }
};
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值