带有父节点指针的二叉树找前驱节点和后继节点
题目:给一个正常的二叉树节点类型加一个指向父节点的指针parent。找其中某一个节点的前驱节点和后继节点
*分析:所为前驱节点和后继节点就是找中序遍历。
答:找出一个结点出现的所有可能性并进行操作。可分为下面几种出现的情况
1. 如果该结点无左右子树,判读该结点是否有父节点?
1.1 如果有父节点,则判断该节点为父节点的左、右子树?
1.1.1 如果为左子树则该父节点就是其后继节点
1.1.2 如果为右子树,那么向上找,找出第一个节点有左孩子的那个节点就为其后继节点
1.2 如果没有父节点,则这个节点即没有父节点也无左右孩子节点,则为根节点,只需返回null或者返回自己。
2. 如果该节点有左孩子,无右孩子,如果此节点是其父节点的左孩子那么返回父节点,如果此节点是父节点的右孩子则返回该节点左孩子的最右孩子
3. 如果该节点有右孩子,有无左孩子都可以。即只需返回其右节点的最左节点,就是其后继节点
在这里其实有两种方法去写,其实最简单的就是直接递归写中序遍历的代码,然后在根据序列找出前驱和后继,这种方法应该是很多同学第一时间就想到的,也包括我自己。但是如果考虑到算法效率的话,这种写法肯定不是一种时间复杂度低的方法。所有我们注意到题目给我们一个父节点指针,所以肯定有其他方法来写。所以我给大家介绍一下我自己写的第二种方法。
//给一个二叉树节点加一个指向父节点的指针parent
//所谓找后继结点或者是前驱结点就是找中序遍历序列
//找该节点的后继结点
/*
* 分析:找出一个结点出现的所有可能性并进行操作。可分为下面几种出现的情况
* 1. 如果该结点无左右子树,判读该结点是否有父节点?
* 1.1 如果有父节点,则判断该节点为父节点的左、右子树?
* 1.1.1 如果为左子树则该父节点就是其后继节点
* 1.1.2 如果为右子树,那么向上找,找出第一个节点有左孩子的那个节点就为其后继节点
* 1.2 如果没有父节点,则这个节点即没有父节点也无左右孩子节点,则为根节点,只需返回null或者返回自己。
* 2. 如果该节点有左孩子,无右孩子,如果此节点是其父节点的左孩子那么返回父节点,如果此节点是父节点的右孩子则返回该节点左孩子的最右孩子
* 3. 如果该节点有右孩子,有无左孩子都可以。即只需返回其右节点的最左节点,就是其后继节点
*
* 总结出这几种可能后,就将返回节点相同的一起写,这样可以优化一下代码。
*
*
* */
public class GetLaterNode {
static class Node {
public int value;
Node leftChild;
Node rightChild;
Node parent;//
public Node(int data, Node lc, Node rc,Node p) {
this.value = data;
this.leftChild = lc;
this.rightChild = rc;
this.parent = p;
}
}
public static void main(String[] args) {
//初始化一颗树父指针会单独赋值
Node node4 = new Node(4, null, null,null);
Node node5 = new Node(5, null, null,null);
Node node2 = new Node(2, node4, node5,null);
Node node3 = new Node(3, null, null,null);
Node node1 = new Node(1, node2, node3,null);
//给父指针赋值
node4.parent = node2;
node5.parent = node2;
node2.parent = node1;
node1.parent = null;
node3.parent = node1;
//如果打印本身 就代表无后继或者前驱
System.out.println(getLaterNode(node3).value);//打印后继结点
System.out.println(getFrontNode(node3).value);//打印后继节点
}
//找后继节点
public static Node getLaterNode(Node node) {
if (node == null)
return node;
//定义parent = node.parent
Node parent = node.parent;
// 如果有右子树找到右子树最左节点为该子树的后继结点
if (node.rightChild != null) {
return getMostLeft(node.rightChild);
}
// 如果左右子树都无
else if(node.rightChild==null && node.leftChild==null){
if(parent!=null&&parent.leftChild==node){
return parent;
}
else if(parent!=null&&parent.rightChild==node){
//保存变量,如果while没有查到那个合适的元素,不能返回空指针异常。
Node node1 = node;
while (parent != null && parent.rightChild == node) {
node = parent;
parent = node.parent;
}
return parent==null ? node1 : parent;
}
}
else {// 如果无右子树那就看是否有父节点,向上找,找出第一个节点有左孩子那个节点
while (parent != null && parent.rightChild == node) {
node = parent;
parent = node.parent;
}
return node;
}
return node;
}
//找最左节点
public static Node getMostLeft(Node node) {
if (node == null)
return node;// 返回自己表示没有后继
while (node.leftChild != null) {
node = node.leftChild;
}
return node;
}
// 找前驱结点
/*
* 分析:与后继节点的分析基本类似
*
* */
public static Node getFrontNode(Node node) {
Node parent = node.parent;
if (node == null)
return node;
else if (node.leftChild != null && node.rightChild == null)
return getMostRight(node.leftChild);// 当左孩子不为空,右孩子为空时 应该找左孩子的最右结点
else if (node.leftChild == null && node.rightChild != null)
return node;// 返回自己 表示没有前驱
else if (node.leftChild != null && node.rightChild != null)
return node.leftChild;
else if (node.leftChild == null && node.rightChild ==null){
Node node1 =node;//避免后面while循环找不到那个节点,返回空的情况;
//如果该节点为父亲节点的左孩子,向上找第一个节点有右孩子的那个节点
while(parent != null && parent.rightChild != node) {
node = parent;
parent = node.parent;
}
return parent==null ? node1 : parent;
}
return node;
}
// 找左孩子的最右结点
public static Node getMostRight(Node node) {
if (node == null)
return node;
while (node.rightChild != null) {
node = node.rightChild;
}
return node;
}
}