488, Morris in-order and pre-order traversal of binary trees

Insert picture description here

If you want to see more algorithm questions, you can scan the QR code above to follow my WeChat official account " Data Structure and Algorithms ". Up to now, I have updated more than 500 algorithm questions in the official account , some of which have been sorted into pdf documents. , As of now, there are more than 800 pages in total (and will continue to increase), you can reply to the keyword "pdf" in the official account to download.


Problem Description

I have introduced the front, middle and back of the binary tree, as well as the traversal methods such as DFS and BFS, and each has written recursive and non-recursive solutions. Some say that the front, middle and back traversal methods of the binary tree belong to a kind of DFS, in fact, it can also be understood in this way. For details of these traversal methods, see 373, Data Structure-6, Tree .


For the traversal of the binary tree, in addition to the several common ones described above, there are three traversal methods of Morris's front, middle and back. For the several traversal methods of the binary tree mentioned above, if the non-recursive method is used, we either need a stack or a queue to maintain the relationship between nodes. If you use Morris to traverse, you don’t need it. His implementation process is actually to use the pointers of the leaf nodes, because in general, the leaf nodes of the binary tree have no child nodes, that is to say, they point to empty, which always feels It's a bit wasteful. The implementation principle of Morris is actually to use the pointer of the leaf node. Morris's follow-up traversal method is a bit more complicated. I will talk about this later. Here we mainly look at Morris's pre-order and middle-order traversal methods.


Morris's in-order traversal

For Morris's in-order traversal, you can think of it as straightening the binary tree into a linked list. Let's take a look at his implementation steps:

Remember the current node as cur, and traverse from the root node.

1. Determine whether cur is empty, and stop traversing if it is empty.

2. If cur is not empty

1)如果cur没有左子节点,打印cur的值,然后让cur指向他的右子节点,即cur=cur.right

2)如果cur有左子节点,则从左子节点中找到最右边的结点pre。

   1))如果pre的右子节点为空,就让pre的右子节点指向cur,即pre.right=cur,然后cur指向他的左子节点,即cur=cur.left。

  2))如果pre的右子节点不为空,就让他指向空,即pre.right=null,然后输出cur节点的值,并将节点cur指向其右子节点,即cur=cur.right。

3. Repeat step 2 until the node cur is empty.

There are a lot of descriptions above, and if you understand, you can understand it at a glance, and if you don’t understand, you will definitely be fainted. Let me explain one by one.
1. First of all, the first one, stop traversing if cur is empty, there is nothing to say, of course it cannot be traversed if it is empty.
2. Cur is not empty, the left child node of cur is empty, print the value of the current node cur, and then let cur=cur.right, let's draw a picture to see
Insert picture description here

2. Cur is not empty, and his left child node is not empty. We need to find the rightmost child node pre in his left child node. In fact, it is the previous node of cur in the middle order traversal. We can draw two randomly. Looking at the tree, it is obvious to find the previous node that the current node cur traverses in the middle order.

Insert picture description here

Then judge whether the right child node of pre is empty. This step may be the most doubtful place for everyone. Does this still need to be judged? In fact, it is necessary. When searching for the first time, the right child node of pre must be empty. We just let him point to cur, that is, pre.right=cur. As shown below
Insert picture description here

Then let cur point to his left child node, as shown in the figure below

image

Although node 1 is not disconnected from its left child node, we can think that it is disconnected (in fact, it is not disconnected), we can imagine it like this

image

In the end, we can think of it as being transformed into a linked list (not actually).


If the right child node of pre is not empty, then it must be pointing to the cur node, which means that the left child node of the cur node has been traversed , only the current node cur value needs to be printed, and then cur point to the right child node, Start traversing the right child node with the same operation.


The text description is more convoluted, let’s just take an example and draw a picture to see

Insert picture description here
Insert picture description here
Insert picture description here
Insert picture description here
Insert picture description here
Insert picture description here

Understand the above principles, the code is much simpler

public List<Integer> inorderTraversal(TreeNode root) {
    
    
    List<Integer> res = new ArrayList<>();
    //首先把根节点赋值给cur
    TreeNode cur = root;
    //如果cur不为空就继续遍历
    while (cur != null) {
    
    
        if (cur.left == null) {
    
    
            //如果当前节点cur的左子节点为空,就访问当前节点cur,
            //接着让当前节点cur指向他的右子节点
            res.add(cur.val);
            cur = cur.right;
        } else {
    
    
            TreeNode pre = cur.left;
            //查找pre节点,注意这里有个判断就是pre的右子节点不能等于cur
            while (pre.right != null && pre.right != cur)
                pre = pre.right;
            //如果pre节点的右指针指向空,我们就让他指向当前节点cur,
            //然后当前节点cur指向他的左子节点
            if (pre.right == null) {
    
    
                pre.right = cur;
                cur = cur.left;
            } else {
    
    
                //如果pre节点的右指针不为空,那么他肯定是指向cur的,
                //表示cur的左子节点都遍历完了,我们需要让pre的右
                //指针指向null,目的是把树给还原,然后再访问当前节点
                //cur,最后再让当前节点cur指向他的右子节点。
                pre.right = null;
                res.add(cur.val);
                cur = cur.right;
            }
        }
    }
    return res;
}

Morris's preorder traversal

The way of pre-order traversal and middle-order traversal is the same, but the timing of accessing nodes is different. The picture is not drawing, there are comments in the code, you can see

public List<Integer> preorderTraversal(TreeNode root) {
    
    
    List<Integer> res = new ArrayList<>();
    //首先把根节点赋值给cur
    TreeNode cur = root;
    //如果cur不为空就继续遍历
    while (cur != null) {
    
    
        if (cur.left == null) {
    
    
            //如果当前节点cur的左子节点为空,就访问当前节点cur,
            //接着让当前节点cur指向他的右子节点
            res.add(cur.val);
            cur = cur.right;
        } else {
    
    
            TreeNode pre = cur.left;
            //查找pre节点,注意这里有个判断就是pre的右子节点不能等于cur
            while (pre.right != null && pre.right != cur)
                pre = pre.right;
            //如果pre节点的右指针指向空,我们就让他指向当前节点cur,
            //然后打印当前节点cur的值,最后再让当前节点cur指向他的左子节点
            if (pre.right == null) {
    
    
                pre.right = cur;
                res.add(cur.val);
                cur = cur.left;
            } else {
    
    
                //如果pre节点的右指针不为空,那么他肯定是指向cur的,
                //表示当前节点cur和他的的左子节点都遍历完了,直接
                //让他指向他的右子节点即可。
                pre.right = null;
                cur = cur.right;
            }
        }
    }
    return res;
}

to sum up

The above is Morris's middle-order and pre-order traversal. The subsequent traversal is a bit complicated, and there is a separate article later.

I have sorted some of the algorithm questions into pdf format. If it is not convenient to read, you can download and read. Reply to the keyword "pdf" in the official account to get the download address and password. Below is a screenshot of part of the catalog.

Guess you like

Origin blog.csdn.net/abcdef314159/article/details/112208458