A few solutions
Whitespace delimited serialization/deserialization of the pre-order traversal of the tree.  null values are represented as -1.\n\n---\n\nKotlin\n\nclass Codec()
Whitespace delimited serialization/deserialization of the pre-order traversal of the tree.  `null` values are represented as `-1`.\n\n---\n\n*Kotlin*\n```\nclass Codec() {\n    fun serialize(root: TreeNode?): String {\n        var A = mutableListOf<Int>()\n        fun go(node: TreeNode? = root) {\n            if (node == null) {\n                A.add(-1)\n                return\n            }\n            A.add(node?.`val`)\n            go(node?.left)\n            go(node?.right)\n        }\n        go()\n        return A.joinToString(" ")\n    }\n    fun deserialize(S: String): TreeNode? {\n        var A = ArrayDeque<Int>(S.split(" ").map{ it.toInt() })\n        fun go(): TreeNode? {\n            if (A.size == 0)\n                return null\n            var x = A.removeFirst().toInt()\n            var root = if (-1 < x) TreeNode(x) else null\n            root?.left = go()\n            root?.right = go()\n            return root\n        }\n        return go()\n    }\n}\n```\n\n*Javascript*\n```\nlet serialize = (root, q = []) => {\n    let go = root => {\n        if (!root) {\n            q.push(-1);\n            return;\n        }\n        q.push(root.val);\n        go(root.left);\n        go(root.right);\n    };\n    go(root);\n    return q.join(\' \');\n};\nlet deserialize = (data, q = data.split(\' \')) => {\n    let go = () => {\n        if (!q.length)\n            return;\n        let x = q.shift();\n        let root = -1 < x ? new TreeNode(x) : null;\n        if (root) root.left  = go();\n        if (root) root.right = go();\n        return root;\n    };\n    return go();\n};\n```\n\n*Python3*\n```\nclass Codec:\n    def serialize(self, root: TreeNode) -> str:\n        q = []\n        def go(root):\n            if not root:\n                q.append(-1)\n                return\n            q.append(root.val)\n            go(root.left)\n            go(root.right)\n        go(root)\n        return \' \'.join([str(x) for x in q])\n\n    def deserialize(self, data: str) -> TreeNode:\n        q = deque([int(s) for s in data.split(\' \')])\n        def go():\n            if not len(q):\n                return\n            x = q.popleft()\n            root = TreeNode(x) if -1 < x else None\n            if root: root.left  = go()\n            if root: root.right = go()\n            return root\n        return go()\n```\n\n*C++*\n```\nclass Codec {\npublic:\n    using fun1 = function<void(TreeNode*)>;\n    using fun2 = function<TreeNode*()>;\n    string serialize(TreeNode* root, vector<int> q = {}) {\n        fun1 go = [&](auto root) {\n            if (!root) {\n                q.push_back(-1);\n                return;\n            }\n            q.push_back(root->val);\n            go(root->left);\n            go(root->right);\n        };\n        go(root);\n        ostringstream stream; copy(q.begin(), q.end(), ostream_iterator<int>(stream, " "));\n        return stream.str();\n    }\n    TreeNode* deserialize(string data) {\n        istringstream stream{ data };\n        fun2 go = [&](int x = 0) -> TreeNode* {\n            if (!(stream >> x))\n                return nullptr;\n            auto root = -1 < x ? new TreeNode(x) : nullptr;\n            if (root) root->left  = go();\n            if (root) root->right = go();\n            return root;\n        };\n        return go();\n    }\n};\n```
[]