[LeetCode] 二叉树和链表转化题总结

写在前面

二叉树和链表转化的题主要分为两类,即将二叉树转化成单链表或者双向链表,和单链表转化成二叉树,而这里的二叉树往往指的是BST-tree,而单链表的序列往往是BST-tree的中序序列,往往像这类题,需要用到树的递归遍历来做,我形象的称它为「 借力打力」,就是说,按照它的函数形式递归解题即可,比如二叉树转单链表的题(题144),使树flat的方式有多,比如单向牵引(题144),双向牵引后root节点更新为最左的节点成为链表头节点(这个是《剑指offer》书上的题),转化过程如下图。
在这里插入图片描述

114. 二叉树展开为链表

原题链接

    1					
   / \
  2   5
 / \   \
3   4   6
1
 \
  2
   \
    3
     \
      4
       \
        5
         \
          6

解题思路: 看懂题目样例基本就看懂了题目所谓的in-place转化的大致过程,其特征是,将将左子树和右子树分别flat,然后将左子树插入到root与右子树的中间空挡。起初思路走岔了,按照《剑指offer》那题解,进入last指针,但是后来发现直接在原题形式上解是最方便的,last指针由flat之后的链表扫描到末尾节点求得,然后在用last参与链接的拼接。下面也把《剑指offer》那题贴出来解一下,对比着看。

/**
 * 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:
    void flatten(TreeNode* root) {
        if (root == nullptr) return;
        flatten(root->left);
        flatten(root->right);
        TreeNode *last = root->left;
        if (last == nullptr) return;
        while (last->right) last = last->right;
        TreeNode *tmp = root->right;
        root->right = root->left;
        root->left = nullptr;
        last->right = tmp;
    }
};
    

剑指 Offer 36. 二叉搜索树与双向链表

原题链接

解题思路: 此题因为是转换之后的是双向链表,所以我选择附设两个指针pre和last分别指向转换之后的链表的头节点和尾节点,不过依稀记得《剑指offer》上给出的解答是附设了一个指针last。本题的思路比较直接,先分别对左右子树进行转换,并带回相应的pre和last指针,然后利用左右子树的pre和last指针调整root节点位置并进行链表的拼接,完成之后要更新root节点对应的链表pre和last的位置(相当于带回转换的BST的pre和last),这里在处理题114时作者对叶子节点左单独的判断,i.e.,当为叶子节点时,pre和last直接指向root(叶子节点),否则则根据节点左、右子树的相应pre和last更新之,但是在处理此题的时,可以直接先将pre和last指向root节点(先默认假设其为叶子节点),然后在根据左、右子树是否存在,相应更新之。同种方法本质是相同的,但是,针对叶子节点需要做单独判断,因为这个是pre和last指针更新的起点(否则它们会直接为空)。

/*
// Definition for a Node.
class Node {
public:
    int val;
    Node* left;
    Node* right;

    Node() {}

    Node(int _val) {
        val = _val;
        left = NULL;
        right = NULL;
    }

    Node(int _val, Node* _left, Node* _right) {
        val = _val;
        left = _left;
        right = _right;
    }
};
*/
class Solution {
public:
    Node* treeToDoublyList(Node* root) {
        Node *pre = NULL, *last = NULL;
        helper(root, pre, last);
        if (pre) pre->left = last;
        if (last) last->right = pre;
        return pre;
    }
    void helper(Node *root, Node* &pre, Node* &last) {
        if (root == NULL) {
            last = NULL;
            return;
        }
        Node *preLeft = NULL, *lastLeft = NULL;
        Node *preRight = NULL, *lastRight = NULL;
        helper(root->left, preLeft, lastLeft);
        helper(root->right, preRight, lastRight);
        root->left = lastLeft;
        if (lastLeft) lastLeft->right = root;
        root->right = preRight;
        if (preRight) preRight->left = root;
        pre = last = root;
        if (root->left) pre = preLeft;
        if (root->right) last = lastRight;
    }
};

————————————————————

总结树转链表题的通用解法:

  • 用两个附设指针pre和last掐住(或者说箍住、限定住)链表的范围(或者说头尾),转换完毕后,再利用左、右子树对应的两组pre和last指针进行链表拼接、root节点前、后项指针的修改。

————————————————————

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值