在BST中查找交换的节点

时间:2011-08-31 16:30:48

标签: c++ algorithm data-structures tree binary-search-tree

我正在尝试编写一个程序,可以检测并打印BST中已交换的两个节点。

在三层树中,我使用这种方法接近解决方案。

If (!AllSubTreeAreValid())
{
//Nodes swapped on same side of main root node
}
else
{
  int max = getMax(root->left);
  int min = getMin(root->right);
  if(max > root->data || min < root->data )
  {
     //Nodes swapped on different sides of main root node
     //Print max and min values

  }
else 
{
 //No node swappped
}
}

//Helper functions
int GetMaxEle(TreeNode* tree)
{
    while(tree->right!=NULL)
    {
        tree=tree->right;
    }
    return tree->info;
}

int GetMinEle(TreeNode* tree)
{
    while(tree->left!=NULL)
    {
        tree=tree->left;
    }
    return tree->info;
}

但是当我尝试使用四级树进行测试时,上述方法失败了。

             20

      15            30

   10    17       25     33

9  16  12  18  22  26  31  34

在根节点15的右子树中,12仍然更大(违规)。

在根节点15的左子树中,16仍然更大(违规)。

所以,16,12是上述BST中的交换元素。我如何通过该程序找到它们?

3 个答案:

答案 0 :(得分:8)

考虑这个问题的一种方法是使用树的顺序遍历将按排序顺序生成所有元素的事实。如果您在此步行期间可以检测到排序顺序的偏差,则可以尝试找到位于错误位置的两个元素。

让我们先看看如何为一个简单的排序数组做这个,然后使用我们的算法构建一些适用于树的东西。直观地说,如果我们从一个排序的数组开始然后交换两个(非等于!)元素,我们最终会得到数组中的一些元素不合适。例如,给定数组

1 2 3 4 5

如果我们交换2和4,我们最终得到这个数组:

1 4 3 2 5

我们如何检测到2和4在这里被交换?好吧,因为4是两个元素中较大的一个并且向下交换,它应该大于它周围的两个元素。同样,因为2被交换,它应该小于它周围的两个元素。由此,我们可以得出结论,交换了2和4。

但是,这并不总是正常工作。例如,假设我们交换1和4:

4 2 3 1 5

这里,2和1都小于它们的相邻元素,4和3都比它们大。由此我们可以看出,这四个中的两个以某种方式被交换了,但是我们不应该交换哪些。但是,如果我们取这些值中的最大值和最小值(分别为1和4),我们最终得到交换的对。

更一般地说,要查找序列中交换的元素,您需要找到

  • 阵列中最大的局部最大值。
  • 阵列中最小的局部最小值。

这两个元素不合适,应该互换。

现在,让我们考虑如何将其应用于树木。由于树的顺序遍历将产生具有两个元素乱序的排序序列,一个选项是走树,记录我们找到的元素的顺序序列,然后使用上述算法。例如,考虑一下您原来的BST:

              20
         /         \
      15             30
     /   \         /   \ 
   10    17      25     33
  / |   /  \    /  \    |  \
9  16  12  18  22  26  31  34

如果我们将其线性化为数组,我们得到

9 10 16 15 12 17 18 20 22 25 26 30 31 33 34

请注意,16大于其周围元素,并且12小于其周围元素。这立刻告诉我们交换了12和16。

因此,解决此问题的一个简单算法是对树进行顺序遍历,将其线性化为vectordeque之类的序列,然后扫描该序列以查找最大的局部最大值和最小的局部最小值。这将使用O(n)空间在O(n)时间内运行。更棘手但更节省空间的算法是一次只跟踪三个节点 - 当前节点,它的前任节点和它的后继节点 - 它将内存使用量减少到O(1)。

希望这有帮助!

答案 1 :(得分:0)

我猜你的getMin和getMax工作时假设树是BST,所以

T getMax(tree) {
  return tree -> right == null 
    ? tree -> value 
    : getMax(tree -> right);
}

(或带循环的等效代码)。 如果是这样,您的代码最多会检查树中的三个值。即使getMax和getMin遍历整个树以获得实际的最大/最小值,您仍然可以将测试基于两个比较。如果要检查树是否满足BST属性,则显然必须检查所有值。足以将每个节点与其父节点进行比较。

void CheckIsBst(Tree *tree) {
  if (tree -> left != null) {
    if (tree -> left -> value > tree -> value) {
      // print violation
    }
    CheckIsBst(tree -> left);   
  }
  // same with -> right, reversing < to > in the test
}

修改:这是错误的,请参阅评论。我相信这个没问题。

void checkIsBst(Tree *Tree, Tree *lowerBound, Tree *upperBound) {
  if(lowerBound!= null && lowerBound -> value > tree -> Value) {
    //violation
  }
  // same for upper bound, check with <
  if (tree -> left != null) {
    if (tree -> left -> value > tree -> value) {
      // print violation
     }
     CheckIsBst(tree -> left, lowerBound, tree);   
  }
  // same for right, reversing comparison 
  // setting lowerBound to tree instead of upperBound
}

从root用空边界调用

答案 2 :(得分:0)

如果您确定只有一个交换,那么 templatetypedef 完成的树遍历将起作用。否则,我建议根据您的初始代码提供解决方案:

int GetMax(TreeNode* tree) {
    int max_right, max_left, ret;

    ret = tree->data;
    if (tree->left != NULL) {
        max_left = GetMax(tree->left);
        if (max_left > ret)
            ret = max_left;
    }
    if (tree->right != NULL) {
        max_right = GetMax(tree->right);
        if (max_right > ret)
            ret = max_right;
    }

    return ret;
}

int GetMin(TreeNode* tree) {
    int min_right, min_left, ret;

    ret = tree->data;
    if (tree->left != NULL) {
        min_left = GetMin(tree->left);
        if (min_left < ret)
            ret = min_left;
    }
    if (tree->right != NULL) {
        min_right = GetMin(tree->right);
        if (min_right < ret)
            ret = min_right;
    }

    return ret;
}

void print_violations(TreeNode* tree) {
    if ((tree->left != NULL) && (tree->right != NULL)) {
        int max_left = GetMax(tree->left);
        int min_right = GetMin(tree->right);
        if (max_left > tree->data && min_right < tree->data) {
            printf("Need to swap %d with %d\n", max_left, min_right);
        }
    }
    if (tree->left != NULL)
        print_violations(tree->left);
    if (tree->right != NULL)
        print_violations(tree->right);
}

速度较慢,但​​会打印出识别出来的所有掉期。可以更改为打印所有违规(例如,如果(max_left&gt; tree-&gt; data)打印违规)。如果可以向TreeNode添加两个字段,并为该子树预先计算max和min,则可以提高性能。