剑指Offer-59-二叉树的下一个节点

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/dawn_after_dark/article/details/83214300

项目地址:https://github.com/SpecialYy/Sword-Means-Offer

问题

给定一个二叉树和其中的一个结点,请找出中序遍历顺序的下一个结点并且返回。注意,树中的结点不仅包含左右子结点,同时包含指向父结点的指针。

解析

这道题考察对二叉树和其遍历方式的理解。中序遍历为“左-根-右”的形式进行访问,所以这道题最笨的方法就是获得中序遍历序列,从而轻松即可获得给定节点的下一个节点。

思路一

递归版中序遍历,用一个list存放遍历的结果,最后遍历这个list从而找到指定节点的下一个节点。

	//方法一:纯递归中序遍历二叉树
    static List<TreeLinkNode> lists = null;

    public TreeLinkNode GetNext1(TreeLinkNode pNode) {
        TreeLinkNode root = getRootNode(pNode);
        inorderTraversal(root);
        int index = 0;
        for(int i = 0; i < lists.size(); i++) {
            if (lists.get(i) == pNode) {
                index = i + 1;
                break;
            }
        }
        return index < lists.size() ? lists.get(index) : null;
    }

    public TreeLinkNode getRootNode(TreeLinkNode pNode) {
        while(pNode != null) {
            if(pNode.next == null) {
                return pNode;
            }
            pNode = pNode.next;
        }
        return null;
    }

    public void inorderTraversal(TreeLinkNode node) {
        if(node != null) {
            inorderTraversal(node.left);
            lists.add(node);
            inorderTraversal(node.right);
        }
    }

思路二

考虑递归版的中序遍历对于高深度的树不友好,所以我令写了一版非递归版的中序遍历。

//方法二:变形的中序遍历,可中断
    static TreeLinkNode nextNode = null;
    static boolean flag = false;
    static boolean findFlag = false;

    public TreeLinkNode GetNext2(TreeLinkNode pNode) {
        TreeLinkNode root = getRootNode(pNode);
        nextNode = null;
        flag = false;
        findFlag = false;
        findNextNode2(root, pNode);
        return nextNode;
    }

    /**
     * 非递归中序遍历变形
     * @param node
     * @param pNode
     */
    public void findNextNode2(TreeLinkNode node, TreeLinkNode pNode) {
        Stack<TreeLinkNode> stack = new Stack<>();
        TreeLinkNode p = node;
        boolean flag = false;
        while(!stack.isEmpty() || p != null) {
            while(p != null) {
                stack.push(p);
                p = p.left;
            }
            p = stack.pop();
            if(flag) {
                nextNode = p;
                break;
            }
            if(p == pNode) {
                flag = true;
            }
            p = p.right;
        }
    }

思路三

思路三的做法则是标准解法,通过观察发现中序遍历中给定节点的下一个节点的规律。

观察上图,2的下一个节点为4,也就是说当指定节点有右孩子,那么在中序遍历里指定节点的下一个节点为右子树最左的节点。

5的下一个节点为3,也就说说当指定节点没有右孩子时且它是父节点的左孩子,那么在中序遍历里指定节点的下一个节点就是父节点。

4的下一个节点为1,也就说说当指定节点没有右孩子时且它不是父节点的左孩子,那么在中序遍历里指定节点的下一个节点就要向上查找,直到找到该指定节点所在的子树的根节点为其父节点的左孩子,那么该祖先即为指定节点的中序遍历中下一个节点。

//方法三-直接利用中序遍历的下一个节点的特点
    public TreeLinkNode GetNext3(TreeLinkNode pNode) {
        if(pNode == null) {
            return null;
        }
        if(pNode.right != null) {
            pNode = pNode.right;
            while(pNode.left != null) {
                pNode = pNode.left;
            }
            return pNode;
        } else {
            while(pNode.next != null) {
                if(pNode.next.left == pNode) {
                    return pNode.next;
                }
                pNode = pNode.next;
            }
        }
        return null;
    }

总结

实在没思路,可以直接从中序遍历入手,后续可以多观察,找规律。

猜你喜欢

转载自blog.csdn.net/dawn_after_dark/article/details/83214300