前驱节点
基本概念
一个节点的前驱节点,也就是中序遍历中该节点的前一个节点。举个例子:
这棵树的中序遍历顺序为:1,2,4,5,7,8,9,11(因为是二叉搜索树,所以中序遍历的顺序是递增)。
那么节点7的前驱是5,节点8的前驱是7。
查找思路
那么该如何查找一个节点的前驱呢,可以确定的是,中序遍历是先访问左子树中的所有节点,再访问根节点,那么由访问次序可知,前驱节点是左子树中最后一个被访问的节点,当然,前提是有左子树。那么又该如何找到左子树中最后访问的节点?显而易见,由中序遍历的次序是左中右可知,最后访问的节点一定是最右端的节点,也就是只需要一直往右走直到没有右节点即可。
比如,下面这棵树的中序遍历最后一个节点一定是30。
private Node<E> predecessor(Node<E> node){
Node<E> pre=node.left;
if(null!=pre){
while(null!=pre.right)
pre=pre.right;
return pre;
}
}
而当一个节点没有左子树,此时他的前驱节点就可能是父节点(祖父节点…)。如下图中的18节点。
此时18的前驱节点是10,因为18没有左节点,且18是10右子树中最左侧的节点,访问完10后,下一个访问的就是10右子树,而右子树又要先访问左节点,如此循环,之后访问的就是最左侧节点18。所以前驱节点的寻找方法是,不断地找父节点,直到当前节点是父节点的右节点。
private Node<E> predecessor(Node<E> node){//节点不提供外部的访问
if(node==null)return null;
//如果有左节点,则前驱是左子树中最右端的节点
Node<E> prev=node.left;
if(prev!=null){
while(prev.right!=null){
prev=prev.right;
}
return prev;
}
//此时没有左节点,则通过父节点寻找,且该节点必须是父节点右节点
//如果该节点是父节点的左节点则向上迭代
while(node.parent!=null&&node.parent.left==node){
node=node.parent;
}
//node.parent==null
//node.parent.right==node
//不管是那种情况,return即可
return node.parent;
}
后继节点
后继节点显然是中序遍历中当前节点的下一个节点。
查找思路也一模一样,只不过方向正好相反。在有右子树的情况下,后驱节点一定是右子树最左侧的节点。在没有右子树的情况下,后驱节点是其父节点(祖父…),通过不断迭代查找,且必须满足当前迭代到的节点是其父节点的左节点,比如下图中的7节点,其后继节点就是祖祖父节点10,且此时7在10的左子树中,也就是5是10的左节点。
/**
* 获取该节点的后继节点,即中序遍历的后节点
* @param node
* @return
*/
private Node<E> successor(Node<E> node){
Node<E> prev=node.right;
//当右节点不为空,寻找右子树中最左端的节点
if(null!=prev){
while(prev.left!=null){
prev=prev.left;
}
return prev;
}
//此时右节点为空,后继节点为父节点(祖父..),且父节点的左节点是当前节点
//去掉中间的作为父节点右节点的节点
while(null!=node.parent&&node==node.parent.right){
node=node.parent;
}
//null==node
//node==node.parent.right
return node.parent;
}