LeetCode426 transforms binary search tree into sorted two-way linked list & sword finger Offer 36 binary search tree and two-way linked list

subject


Problem solving

A "binary tree middle order traversal problem", which is unfamiliar to binary tree middle order traversal, I suggest moving first Middle order traversal of LeetCode94 binary tree.

Question 94 is to output node values in the form of an array in the order of traversal in the middle order. The difference in this problem is that it is necessary to build a circular two-way linked list structure. The left pointer is equivalent to the prev pointer, and the right pointer is equivalent to the next pointer. In addition, the head and tail should be connected (record the head and tail, and finally connect them).

Therefore, the task to be completed in the step of adding the node val value to the array becomes: the current node is marked as a, A.left = the last added node B (if not null), B.right = a; = > Thus, it is analyzed that the previous node needs to be recorded (just in line with the meaning of the node recorded by the tail pointer). When processing a, A.left, that is, its left subtree has been processed, so the left pointer can be changed, but the right subtree will be traversed after a (the nature of binary search tree), so the right pointer can only be changed when processing the next node.

In addition, pay special attention to the empty tree when connecting end to end.

Solution 1: recursion

// javascript
var treeToDoublyList = function(root) {
    if (root === null) return null;    // Binary search tree is empty
    let head = null, tail = null;      // Record the head and tail of the two-way linked list
    const dfs = (root) => {
    	if (root === null) return;
    	// First traverse the left subtree
        dfs(root.left);
        // After traversing the left subtree, the next one to be processed is root
        if (head === null) {           // If the header pointer is null, it indicates that root is the header node (the first node to be processed)
            head = root;
        }
        if (tail !== null) {           // Only when the tail pointer is not null: the right of the precursor node can be pointed to the current node when there is a precursor node (null has no right pointer)
            root.left = tail;
            tail.right = root;
        }
        tail = root;                   // Update tail pointer (to the last processed node)
        // Then traverse the right subtree
        dfs(root.right);   
    };
    dfs(root);
    // End to end. Since the tree is empty, it has been handled at the beginning, so neither head nor tail is null
    head.left = tail;
    tail.right = head;
    return head;
};

Solution 2: stack

Recursion implicitly calls the stack. Similarly, we can imitate the recursion order and explicitly present the stack: the recursion order is to find the left subtree first. If the left subtree is empty, return to the root node. After the root node is processed, process the right subtree of the root node, and return to the root node of a higher level after the right subtree is processed.

So the problem is that we need to record the parent node: otherwise we can't return! This is what the recursive stack does. Next, we directly use stack to complete this function, and the processing order is to first process the low-level root node and then the high-level root node, which is just in line with the characteristics of stack first in and then out.

// javascript
var treeToDoublyList = function(root) {
    const visit = (cur) => {
        if (head === null) {
            head = cur;
        }
        if (tail !== null) {
            cur.left = tail;
            tail.right = cur;
        }
        tail = cur;
    };
    if (root === null) return null;
    const stk = new Array();
    let head = null, tail = null;
    while (root !== null || stk.length > 0) {
    	// Depth first to find the left subtree, and record the root nodes in the stack in turn
        while (root !== null) {
            stk.push(root);
            root = root.left;
        }
        // The root passed in at the beginning = null
        // Or in the process of finding the left subtree, after processing the left subtree of the lowest layer, the root is updated to null
        root = stk.pop();
        visit(root);
        root = root.right;
    }
    head.left = tail;
    tail.right = head;
    return head;
};

Solution 3: order traversal in Morris

Note that the order of operations 1 and 2 in the code comments cannot be exchanged: at this time, the predecessor is the precursor node of root (tail points to the predecessor). When calling visit, the right pointer of the precursor node will point to root. If you change the predecessor.right to null after visit, it is equivalent to disconnecting the connection again.

// javascript
var treeToDoublyList = function(root) {
	// Encapsulate a visit function
    const visit = (cur) => {
        if (head === null) {
            head = cur;
        }
        if (tail !== null) {
            tail.right = cur;
            cur.left = tail;
        }
        tail = cur;
    };

    let head = null, tail = null;
    while (root !== null) {
        if (root.left !== null) {
            let predecessor = root.left;
            while (predecessor.right !== null && predecessor.right !== root) {
                predecessor = predecessor.right;
            }
            if (predecessor.right === null) {
                predecessor.right = root;
                root = root.left;
            } else {
            	// Because while has two exit conditions
            	// Else is also equivalent to else if (predecessor.right === root)
            	// The order of operations 1 and 2 cannot be exchanged
                predecessor.right = null;  // Operation 1
                visit(root);               // Operation 2
                root = root.right;
            }
        } else {
            visit(root);
            root = root.right;
        }
    }
    // End to end connection is equivalent to taking the head as the last node of the tail, and you can call visit again
    // And visit can handle empty trees
    // If you want to write head.left = tail as before; tail.right = head;
    // Be sure to deal with the special case of empty trees first
    visit(head);
    return head;
};

Posted by waffle72 on Thu, 04 Nov 2021 05:52:52 -0700