找最近公共祖先,将二叉搜索树转换为双向链表,根据先序和中序结果构造二叉树(不含空节点),将先序结果转为字符串

1.给定两个数,找到其公共的最近祖先

在这里插入图片描述
题目如上,需要注意的是,节点本身也可以是自己的祖先。
例如,7和2的公共祖先就是2
6和4的公共祖先就是5,而不是3,因为3不是最近的

首先的思路就是,通过遍历二叉树,如果通过一个节点,可以找到这两个值,说明这个节点就是一个公共祖先,但问题是,我们不知道这个节点是不是她的最近祖先。我们分析可以得出,只要这两个要找的节点分别位于:根节点,左子树,右子树中的任意两个,就认为他是最近的。可能这个思路是比较难想到的,我们想想,假设两个节点都在左子树,或者右子树,,那么这个祖先就肯定离这两个节点比较远的,所以才可以把这两个节点都包含在自己的左子树,或者右子树。

下来我们通过代码进一步分析:

//找最近公共父节点
    private TreeNode lca = null;
    public TreeNode lowestCommonAncestor(TreeNode root,TreeNode p,TreeNode q) {
        if (root == null) {
            return null;
        }
        findNode(root,p,q);
        return lca;
    }
    private boolean findNode (TreeNode root,TreeNode p,TreeNode q) {
        if (root == null) {
            return false;
        }
        int left = findNode(root.left,p,q)?1:0;
        int right = findNode(root.right,p,q)?1:0;
        int mid = (root == p||root == q)?1:0;
        if (left+right+mid == 2) {
            lca = root;
        }
       return (right+left+mid)>0;

    }

第一部还是判断数是否为空,为空的话是肯定找不到的,返回空,然后我们再通过一个辅助函数找到这个满足条件的节点,并返回。关于这个找节点函数,第一步判断是否为空,为空返回false,注意这个函数的返回值类型是boolean类型,是有原因的。最后我们通过递归看左右子树是否能找到两个节点,注意,这里呢,我们这里只要找到其中的一个,就认为找到了,然后通过三目表达式,返回1,没找到返回0,最后,我们要找到最近公共祖先,根据我上面讲的那个判断是否为最近的思路,可以转换为,只要left,right,mid这三个变量之和等于2,就说明肯定这三个位置两个为1,一个为0,这是这个节点就是最近的公共祖先,把这个节点赋给变量lca,最后我们返回值找到就是true,没找到就是false,我们可以直接返回这个三个变量之和大于0这个表达式的结果。

2.将二叉搜索树转换为双向链表

题目:输入一棵二叉搜索树,将该二叉搜索树转换成一个排序的双向链表。要求不能创建任何新的结点,只能调整树中结点指针的指向。

  public TreeNode Convert(TreeNode root) {
        if (root == null) {
            return null;
        }
        if (root.right == null&&root.left == null) {
            return root;
        }
        TreeNode left = Convert(root.left);
        TreeNode leftTail = left;
        while (leftTail!=null&&leftTail.right!=null) {
            leftTail = leftTail.right;
        }
        if (leftTail != null) {
            leftTail.right = root;
            root.left = leftTail;
        }
        TreeNode right = Convert(root.right);
        if (right!=null) {
            root.right = right;
            right.left = root;
        }
        return left != null?left:root;
    }

这个思路也是比较简单的,如果为空返回null,只有一个根节点,返回根节点本身,然后通过递归进行中序遍历,访问完左子树后,就已经把左子树创建完毕,然后需要把根节点插入到她的后面,给定一个leftTail变量指向左子树链表的头节点,然后开始遍历,知道为空,此时这个节点就指向了最后一个节点,这里要注意,leftTail不能为空,即left不能为空,这里我们就不需要管为空会怎么样了,只要非空就行,然后利用双向链表的尾插进行插入,然后遍历右子树,不为空时,直接把右子树插入带根节点的后面,最后返回整个链表就行了,要注意,left可能为空,我们就用三目表达式,如果不为空返回left,否则直接返回根节点。

3.根据先序和中序结果构造二叉树

在这里插入图片描述
要注意,我们这里的遍历结果是不含null的,所以我们不能根据其中一个直接构造二叉树。

private int index = 0;
    public TreeNode buildTree(int[] preorder,int[] inorder) {
        index = 0;
        return buildTreeHelper(preorder,inorder,0,inorder.length);

    }
    private TreeNode buildTreeHelper(int[] preorder,int[] inorder,
                                     int inorderLeft,int inorderRight) {
        if (inorderLeft>=inorderRight) {
            return null;
        }
        if (index>=preorder.length) {
            return null;
        }
        TreeNode newNode = new TreeNode(preorder[index]);
        int pos = find(inorder,inorderLeft,inorderRight,newNode.val);
        index++;
        newNode.left = buildTreeHelper(preorder,inorder,inorderLeft,pos);
        newNode.right = buildTreeHelper(preorder,inorder,pos+1,inorderRight);
        return newNode;
    }
    private int find(int[] inorder,int inorderLeft,int inorderRight,int val) {
        for (int i = inorderLeft;i<inorderRight;i++) {
            if (inorder[i] == val) {
                return i;
            }
        }
        return -1;
    }

我们可以根据先序结果,再结合中序而分析出,设index表示当前遍历先序的第几个值,然后通过树的构造函数返回值。再构造树的函数中,参数里,我们给了两个遍历结果的数组和中序的左边界和右边界,这个就是再每次递归中,当前节点再中序遍历结果中左子树和右子树的范围,如果左边界比右边界大了,说明为空,返回null,还有index比先序遍历数组大了,说明遍历完了,也返回null,然后把先序数组里第index个也就是头节点加入树中,然后再利用find函数再中序数组中找到这个数的下标pos,再index++,是为了下次再去中序中寻找节点,然后利用递归,我们发现,左子树范围是[inorderLeft,pos) ,右子树范围是[pos+1,inorderRight),根据这个不断递归连接节点,然后返回树的头节点。

4.根据二叉树创建字符串

在这里插入图片描述
在这里插入图片描述

猜你喜欢

转载自blog.csdn.net/AIJXB/article/details/105772750