{"id":111771,"date":"2025-11-06T07:00:00","date_gmt":"2025-11-06T15:00:00","guid":{"rendered":"https:\/\/devblogs.microsoft.com\/oldnewthing\/?p=111771"},"modified":"2025-11-06T09:29:17","modified_gmt":"2025-11-06T17:29:17","slug":"20251106-00","status":"publish","type":"post","link":"https:\/\/devblogs.microsoft.com\/oldnewthing\/20251106-00\/?p=111771","title":{"rendered":"Non-recursively deleting a binary tree in constant space: Synthesizing the parent pointer"},"content":{"rendered":"<p>Last time, we looked at a way to <a title=\"Non-recursively deleting a binary tree in constant space: Traversal with parent pointers\" href=\"https:\/\/devblogs.microsoft.com\/oldnewthing\/20251105-00\/?p=111765\"> delete a binary tree non-recursively, assuming that we had parent pointers<\/a>. But nodes in a classical binary tree does not remember their parents, so we&#8217;ll have to use some other trick. Recursion is the traditional mechanism for remembering the parent of the node under study, but our goal here is to do it without recursion.<\/p>\n<p>The trick is to realize that each child pointer in the binary tree is traversed only once. Therefore, once you have traversed the pointer, it is superfluous and can be reused. Since the left child is traversed first, we can repurpose it to be the parent pointer.<\/p>\n<pre>struct Node\r\n{\r\n    Node* left;\r\n    Node* right;\r\n    Data d;\r\n};\r\n\r\nvoid DeleteTree(Node* node)\r\n{\r\n    Node* parent = nullptr;\r\n    while (node) {\r\n        \/\/ Go left as far as you can.\r\n        while (node-&gt;left) {\r\n            <span style=\"border: solid 1px currentcolor; border-bottom: none;\">\/\/ Move down and remember the old parent in the \"left\" pointer.<\/span>\r\n            <span style=\"border: solid 1px currentcolor; border-top: none;\">node = std::exchange(node-&gt;left, std::exchange(parent, node)); <\/span>\r\n        }\r\n\r\n        <span style=\"border: solid 1px currentcolor; border-bottom: none;\">\/\/ Remember the parent in the \"left\" pointer.<\/span>\r\n        <span style=\"border: solid 1px currentcolor; border-top: none;\">node-&gt;left = parent;                         <\/span>\r\n\r\n        \/\/ If you can't go left, try going right one step,\r\n        \/\/ and then continue the leftward descent.\r\n        if (node-&gt;right) {\r\n            <span style=\"border: solid 1px currentcolor;\">parent = node;<\/span>\r\n            node = node-&gt;right;\r\n            continue;\r\n        }\r\n\r\n        \/\/ At the bottom. Delete the node and head back up.\r\n        while (node) {\r\n            delete node;\r\n            if (!parent) {\r\n                return; \/\/ all done\r\n            }\r\n            \/\/ If we came from the left child,\r\n            \/\/ then go to the right child if there is one.\r\n            <span style=\"border: dashed 1px currentcolor; border-bottom: none;\">if (?????????????????????????????????????) {<\/span>\r\n            <span style=\"border: dashed 1px currentcolor; border-top: none;\">    node = parent-&gt;right;                   <\/span>\r\n                break;\r\n            } else {\r\n                \/\/ No more children, so keep walking up.\r\n                node = parent;\r\n                <span style=\"border: solid 1px currentcolor;\">parent = node-&gt;left;<\/span>\r\n            }\r\n        }\r\n    }\r\n}\r\n<\/pre>\n<p>We solved the problem of keeping track of where the parent node is, but we created a new one: When we move up the tree, how do we know whether we came up from the left child or up from the right child?<\/p>\n<p>Well, we know that the right child is undamaged, so instead of saying &#8220;If we are not the left child, then we are the right child&#8221;, we can test the other child: &#8220;If we are not the right child, then we are the left child.&#8221;<\/p>\n<pre>    if (node != parent-&gt;right &amp;&amp; parent-&gt;right)\r\n<\/pre>\n<p><a href=\"https:\/\/devblogs.microsoft.com\/oldnewthing\/20251105-00\/?p=111765&amp;commentid=143416#comment-143416\"> Commenter Sebastian Redl noted that comparing pointers to deleted objects is undefined behavior<\/a>, so we&#8217;ll have to tweak the code.<\/p>\n<pre>        \/\/ At the bottom. Delete the node and head back up.\r\n        while (node) {\r\n            <span style=\"border: solid 1px currentcolor;\">bool from_left = node != parent-&gt;right;<\/span>\r\n            delete node;\r\n            if (!parent) {\r\n                return; \/\/ all done\r\n            }\r\n            \/\/ If we came from the left child,\r\n            \/\/ then go to the right child if there is one.\r\n            if (<span style=\"border: solid 1px currentcolor;\">from_left<\/span> &amp;&amp; parent-&gt;right) {\r\n                node = parent-&gt;right;\r\n                break;\r\n            } else {\r\n                \/\/ No more children, so keep walking up.\r\n                node = parent;\r\n                parent = node-&gt;left;\r\n            }\r\n        }\r\n\r\n<\/pre>\n<p>This code is tricky to write because you have to keep track of the point at which the <code>left<\/code> changes to <code>parent<\/code> for each node in the tree.<\/p>\n<p><a title=\"Free a binary tree\" href=\"https:\/\/codegolf.stackexchange.com\/questions\/478\/free-a-binary-tree\/489#489\"> The solution on the Code Golf Stack Exchange<\/a> is a little different: It uses the <code>right<\/code> pointer to record the &#8220;child node to process next&#8221;, so it is the original <code>right<\/code> when the code traverses into the left child, and it turns to <code>null<\/code> when the code traverses into the right child. The details are different but the basic idea is the same.<\/p>\n<p>As I noted last time, this was not the algorithm I came up with when I was presented with this challenge. We&#8217;ll look at that algorithm next time. (Spoiler alert: I like my algorithm better.)<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Making one as you go.<\/p>\n","protected":false},"author":1069,"featured_media":111744,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"footnotes":""},"categories":[1],"tags":[25],"class_list":["post-111771","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-oldnewthing","tag-code"],"acf":[],"blog_post_summary":"<p>Making one as you go.<\/p>\n","_links":{"self":[{"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/111771","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/users\/1069"}],"replies":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/comments?post=111771"}],"version-history":[{"count":0,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/111771\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/media\/111744"}],"wp:attachment":[{"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/media?parent=111771"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/categories?post=111771"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/tags?post=111771"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}