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.根据二叉树创建字符串