递归二进制搜索树修剪.. x =更改(x)

时间:2013-06-10 06:00:49

标签: java

这是我的代码应该做的: enter image description here

问题请求我创建方法接受最小和最大整数作为参数,并从树中删除任何不在该范围内的元素,包括在内。

我最初编写的代码是

public void trim (int min, int max) {
    overallRoot = trim (overallRoot, min, max);
}

private IntTreeNode trim (IntTreeNode root, int min, int max) {
    if (root != null) {
        if(root.data < min && root.data > max) {
            root = null;
        }else {
            root.left = trim(root.left, min, max);
            root.right = trim (root.right, min, max);
        }
    } 
    return root;
}

我做了一点搜索,因为我的代码没有重建树,我找到了这段代码:

private IntTreeNode trim (IntTreeNode root, int min, int max) {
    if (root == null) {
        return root;
    }
    root.left = trim(root.left, min, max);
    root.right = trim (root.right, min, max);
    if(root.data < max && root.data> min) {
        return root;
    }else if (root.data < min) {
        return root.right;
    }else if (node.data > max) {
        return root.left;
    }
}

代码无法编译,因为它缺少一个return语句,因此,当我更改它以使其成为else时它只在某些情况下有效。我有点理解上面的代码,但它不是非常直观地编写,但是再一次......递归不是很直观。正如我的教授所说,“实现信仰的飞跃”。任何帮助将非常感激:)尝试在我的最后

做好

4 个答案:

答案 0 :(得分:2)

代码的问题在于,即使根节点在区间之外,其子节点也不必如此,但无论如何都要删除整个子树。

您找到的代码通过首先修剪子树然后查看根节点来解决此问题。如果它留在min,则必须删除根节点及其左子树,并且保留右子树(已经被修剪)。类似地,如果根是max的右边,那么正确的子树也是如此,并且(已经修剪过的)左子树是需要保留的。这将访问整个树,因此效率不高。

直接的改进只会访问我们打算使用的子树:

private IntTreeNode trim (IntTreeNode root, int min, int max) {
    if (root == null) {
        return root;
    }
    if(root.data < max && root.data> min) {
        root.left = trim(root.left, min, max);
        root.right = trim (root.right, min, max);
        return root;
    }else if (root.data < min) {
        return trim (root.right, min, max);
    }else if (node.data > max) {
        return trim(root.left, min, max);
    }
}

然而,这仍然不是最佳的,因为它重新访问[min,max]中的所有节点。

可能最好的方法是分两步进行修剪:首先修剪所有节点&lt; min,然后是所有节点&gt;最大:

IntTreeNode trimLeft(IntTreeNode root, int min) {
    if (root == null {
        return null;
    } else if (root.data < min) {
        return trimLeft(root.right, min);
    } else {
        root.left = trimLeft(root.left, min);
        return root;
    }
}

此方法的优势在于我们只访问minmax路径上的节点。如果搜索树是平衡的,那么这将是O(log n)。

无论您选择何种方法,都应该正确定义边缘情况root.data == minroot.data == max中发生的情况(您的原始代码和您找到的代码都是错误的。)

答案 1 :(得分:1)

您需要确保您的代码涵盖递归的所有案例。

private IntTreeNode trim (IntTreeNode root, int min, int max) {
    if (root == null) {
        return root;
    }
    root.left = trim(root.left, min, max);
    root.right = trim (root.right, min, max);
    if(root.data < max && root.data> min) {
        return root;
    }else if (root.data < min) {
        return root.right;
    }else if (node.data > max) {
        return root.left;
    }
}

所以让我们列出它正在检查的内容:

  • root为null
  • min&lt;当前值&lt;最大
  • 当前值&lt;分钟
  • max&lt;当前价值

它没有涵盖current value == mincurrent value == max个案例!你说它应该检查包含范围。这意味着min < current value < max应为min ≤ current value ≤ max,对吧?我认为那会解决它。

然而,就像你说的那样,代码不是很易读。我稍微改了一下:

private IntTreeNode trim (IntTreeNode root, int min, int max) {
    // Base case: leaves' children are null
    if (root == null)
        return root;
    // Case: current value too small - use trimmed right subtree
    if (root.data < min)
        return trim(root.right, min, max);
    // Case: current value too large - use trimmed left subtree
    else if (node.data > max)
        return trim(root.left, min, max);
    // Case: current value in range - trim both subtrees
    else if (min <= root.data && root.data <= max) {
        root.left = trim(root.left, min, max);
        root.right = trim (root.right, min, max);
        return root;
    }
    // Make sure we've covered all the cases
    // (this should be unreachable if our cases are complete)
    throw new RuntimeException("Unhandled case in trim!");
}

由于你没有在你最终修剪掉的子树上调用trim,所以效率会更高一些。我在最后一个案例中重复调用trim确实复制了一小段代码,有些人可能会对此提出异议,但我个人认为这很好。

(注意:我实际上没有测试过这些代码,所以它可能有语法错误,甚至没有编译,但它应该让你知道它应该如何工作。) < / p>


回复您的评论:

代码应该现在运行,因为我在方法的末尾添加了throw子句。

您的代码if (root != null)子句与第二版中的if (root == null) return root;子句基本相同。

if (root.data < max && root.data > min)正在检查该值是否在 min至max 独占的范围内。

如果当前节点的值不在min和max之间,则排除整个子树。您需要修复代码才能丢弃正确的子树,并执行包含性检查。


另外,我认为if (min <= root.data && root.data <= max)比你所拥有的更具可读性,因为它看起来更像你在更传统的数学定义中所写的内容:min ≤ root.data ≤ max。在我看来,保持不平等的标志朝着同一方向很好。

答案 2 :(得分:1)

这是适用于所有案例的代码,对于最终谷歌搜索此问题的人来说哈哈:

public void trim (int min, int max) {
    overallRoot = trim (overallRoot, min, max);
}

private IntTreeNode trim (IntTreeNode root, int min, int max) {
    if (root == null) {
        return root;
    }
    if(root.data <= max && root.data>= min) {
        root.left = trim(root.left, min, max);
        root.right = trim (root.right, min, max);
        return root;
    }else if (root.data < min) {
        return trim (root.right, min, max);
    }else if (root.data > max) {
        return trim(root.left, min, max);
    }else{
        return root;
    }
}

答案 3 :(得分:0)

public void trim(int min, int max) {
    overallRoot = trim(overallRoot, min, max);
}

private IntTreeNode trim(IntTreeNode root, int min, int max) {
    if (root != null) {
        root.left = trim(root.left, min, max);
        root.right = trim (root.right, min, max);
        if (root.data < min) {
            return root.right;
        } else if (root.data > max) {
            return root.left;
        }
    }
    return root;
}

非常直观。由于二叉树的构建方式是左边的值总是小于或等于根中的值而右边的值总是更大,如果root.data小于最小值,则返回右边的值branch(总是大于root.data)并且如果root.data大于给定的max,则返回左分支(总是小于或等于)。使用递归修剪整个树(左右两侧的二叉树):trim(root.left,min,max)trim(root.right,min max)。并返回root来替换overallRoot,它给出的树只包含给定min和max(包括)内的值。

更全面的结构:

public void trim(int min, int max) {
    overallRoot = trim(overallRoot, min, max);
}

private IntTreeNode trim(IntTreeNode root, int min, int max) {
    if (root != null) {
        if (root.data < min) {
            root = trim(root.right, min, max);
        } else if (root.data > max) {
            root = trim(root.left, min, max);
        } else {
            root.left  = trim(root.left,  min, max);
            root.right = trim(root.right, min, max);
        }
    }
    return root;
}