序列化二叉树

题目链接
方法1:层序遍历
知识点:队列
方法:队列
所谓层序遍历,就是从上到下依次遍历节点,将每个节点的左右儿子加入到队列中,然后依次出队和入队,遍历得到的顺序就是层序遍历,我们用队列来模拟,具体可以看代码的注释,这里先给出图示
图片说明
例如这棵树中,我们先将根节点[1]加入到队列中,这样就是
图片说明
然后我们弹出队头,查看队头连接的两个子节点,发现是2,3,于是分别加入队列
图片说明
此时我们继续弹出队头2,查看队头连接的两个子节点,发现无子节点,就不用加入队列,类似的,继续看3,我们发现其有4,5作为子节点,于是加入队列
图片说明
类似的,只要依次弹出队头,查看其连接的子节点,如果有就加入到队列中,否则就查看下一个,最后弹出队列的顺序就是层序序列了。
图片说明
这里是将二叉树序列化的代码

    char* Serialize(TreeNode *root) {    
        queue<TreeNode*> q;//创建一个队列
        q.push(root);//将根加入队列中
        string s;//用string来记录序列化后的二叉树
        while (!q.empty()) {
            TreeNode *node = q.front();
            q.pop();//将队头弹出
            if (node == NULL) {//如果发现是空节点,说明要加入'#'
                s.push_back('#');
            } else {
                s += to_string(node->val);//to_string函数可以将数值转换成字符串形式
                q.push(node->left);
                q.push(node->right);
                //然后将左右儿子加入到队列中去
            }
            s.push_back('!');//别忘了在每个值结束加入'!'
        }
        char *res = new char[s.size() + 1];
        strcpy(res, s.c_str());
        //最后要返回的是char数组类型,所以要将string转换为char数组
        return res;
    }

然后就是反序列化二叉树,我们只要将字符串反过来模拟一遍就可以重构二叉树了

    TreeNode* Deserialize(char *str) {
        string s(str);
        if (s[0] == '#') {
            //这是一个特殊情况判断,当原序列为空的时候,我们需要返回空指针
            return NULL;
        }
        queue<TreeNode*> q;
        TreeNode *root = new TreeNode(atoi(s.c_str()));//创造根节点
        s = s.substr(s.find('!') + 1);
        q.push(root);
        while (!q.empty() && !s.empty()) {
            TreeNode *node = q.front();
            q.pop();
            //加入左子树
            if (s[0] == '#') {
                node->left = NULL;
                s = s.substr(2);
            } else {
                node->left = new TreeNode(atoi(s.c_str()));
                q.push(node->left);
                s = s.substr(s.find('!') + 1);
            }
            //加入右子树
            if (s[0] == '#') {
                node->right = NULL;
                s = s.substr(2);
            } else {
                node->right = new TreeNode(atoi(s.c_str()));
                q.push(node->right);
                s = s.substr(s.find('!') + 1);
            }
        }
        return root;//返回根
    }

所以最终版本就是这样的
时间复杂度:由于每个点都被遍历到一次,所以时间复杂度是O(n)
空间复杂度:没有引入额外空间,空间复杂度是O(1)

/*
struct TreeNode {
    int val;
    struct TreeNode *left;
    struct TreeNode *right;
    TreeNode(int x) :
            val(x), left(NULL), right(NULL) {
    }
};
*/
class Solution {
public:
    char* Serialize(TreeNode *root) {    
        queue<TreeNode*> q;//创建一个队列
        q.push(root);//将根加入队列中
        string s;//用string来记录序列化后的二叉树
        while (!q.empty()) {
            TreeNode *node = q.front();
            q.pop();//将队头弹出
            if (node == NULL) {//如果发现是空节点,说明要加入'#'
                s.push_back('#');
            } else {
                s += to_string(node->val);//to_string函数可以将数值转换成字符串形式
                q.push(node->left);
                q.push(node->right);
                //然后将左右儿子加入到队列中去
            }
            s.push_back('!');//别忘了加入'!'
        }
        char *res = new char[s.size() + 1];
        strcpy(res, s.c_str());
        //最后要返回的是char数组类型,所以要将string转换为char数组
        return res;
    }
    TreeNode* Deserialize(char *str) {
        string s(str);
        if (s[0] == '#') {
            //这是一个特殊情况判断,当原序列为空的时候,我们需要返回空指针
            return NULL;
        }
        queue<TreeNode*> q;
        TreeNode *root = new TreeNode(atoi(s.c_str()));//创造根节点
        s = s.substr(s.find('!') + 1);
        q.push(root);
        while (!q.empty() && !s.empty()) {
            TreeNode *node = q.front();
            q.pop();
            //加入左子树
            if (s[0] == '#') {
                node->left = NULL;
                s = s.substr(2);
            } else {
                node->left = new TreeNode(atoi(s.c_str()));
                q.push(node->left);
                s = s.substr(s.find('!') + 1);
            }
            //加入右子树
            if (s[0] == '#') {
                node->right = NULL;
                s = s.substr(2);
            } else {
                node->right = new TreeNode(atoi(s.c_str()));
                q.push(node->right);
                s = s.substr(s.find('!') + 1);
            }
        }
        return root;//返回根
    }
};

方法2:先序遍历
知识点:递归
对于一棵树,先遍历根节点,然后再遍历其左子树和右子树,遍历顺序就是先序遍历了,递归遍历即可
时间复杂度:由于每个点都被遍历到一次,所以是O(n)
空间复杂度:没有引入额外空间,空间复杂度是O(1)

/*
struct TreeNode {
    int val;
    struct TreeNode *left;
    struct TreeNode *right;
    TreeNode(int x) :
            val(x), left(NULL), right(NULL) {
    }
};
*/
class Solution {
public:
    char* Serialize(TreeNode *root) {    
        if (root == NULL) return "#";//如果是空节点,就加上"#"
        string res = to_string(root->val);
        res.push_back('!');
        char *left = Serialize(root->left);
        char *right = Serialize(root->right);//递归遍历左右子节点
        //接下来都是将当前节点加入到序列当中的操作
        char *node = new char[strlen(left) + strlen(right) + res.size()];
        strcpy(node, res.c_str());
        strcat(node, left);
        strcat(node, right);
        return node;
    }
    TreeNode* remake(char *&s) {//这里用引用操作,方便传值
        if (*s == '#') {
            ++ s;
            return NULL;
        }
        int val = 0;
        while (*s != '!') {
            val = val * 10 + *s - '0';
            ++ s;
        }
        ++ s;
        TreeNode *node = new TreeNode(val);//构造当前节点
        node->left = remake(s);
        node->right = remake(s);
        return node;
    }
    TreeNode* Deserialize(char *str) {
        return remake(str);
    }
};