【算法23天:Day23】第六章二叉树 LeetCode 修剪二叉搜索树(669)

48 阅读2分钟

题目一:

image.png

解法一:(递归)

解题思路:

对根结点 root 进行深度优先遍历。对于当前访问的结点,如果结点为空结点,直接返回空结点;如果结点的值小于 low,那么说明该结点及它的左子树都不符合要求,我们返回对它的右结点进行修剪后的结果;如果结点的值大于 high,那么说明该结点及它的右子树都不符合要求,我们返回对它的左子树进行修剪后的结果;如果结点的值位于区间 [low,high],我们将结点的左结点设为对它的左子树修剪后的结果,右结点设为对它的右子树进行修剪后的结果。

var trimBST = function(root, low, high) {
    if (!root) {
        return null;
    }
    if (root.val < low) {
        return trimBST(root.right, low, high);
    } else if (root.val > high) {
        return trimBST(root.left, low, high);
    } else {
        root.left = trimBST(root.left, low, high);
        root.right = trimBST(root.right, low, high);
        return root;
    }
};

解法二:(迭代)

解题思路:

如果一个结点 node 符合要求,即它的值位于区间 [low,high],那么它的左子树与右子树应该如何修剪?

我们先讨论左子树的修剪:

  • nodenode 的左结点为空结点:不需要修剪

  • nodenode 的左结点非空:

如果它的左结点 left 的值小于 low,那么 left 以及 left 的左子树都不符合要求,我们将 node 的左结点设为 left 的右结点,然后再重新对 node 的左子树进行修剪。

如果它的左结点 left 的值大于等于 low,又因为 node 的值已经符合要求,所以 left 的右子树一定符合要求。基于此,我们只需要对 left 的左子树进行修剪。我们令 node 等于 left ,然后再重新对 node 的左子树进行修剪。

以上过程可以迭代处理。对于右子树的修剪同理。

我们对根结点进行判断,如果根结点不符合要求,我们将根结点设为对应的左结点或右结点,直到根结点符合要求,然后将根结点作为符合要求的结点,依次修剪它的左子树与右子树。

var trimBST = function(root, low, high) {
    while (root && (root.val < low || root.val > high)) {
        if (root.val < low) {
            root = root.right;
        } else {
            root = root.left;
        }
    }
    if (!root) {
        return null;
    }
    for (let node = root; node.left; ) {
        if (node.left.val < low) {
            node.left = node.left.right;
        } else {
            node = node.left;
        }
    }
    for (let node = root; node.right; ) {
        if (node.right.val > high) {
            node.right = node.right.left;
        } else {
            node = node.right;
        }
    }
    return root;
};

因为二叉搜索树的有序性,不需要使用栈模拟递归的过程。

在剪枝的时候,可以分为三步:

  • 将root移动到[L, R] 范围内,注意是左闭右闭区间
  • 剪枝左子树
  • 剪枝右子树

代码如下:

var trimBST = function(root, low, high) {
   if(root === null) {
       return null;
   }
  // 处理头结点,让root移动到[L, R] 范围内,注意是左闭右闭
   while(root !==null &&(root.val<low||root.val>high)) {
       if(root.val<low) {
           root = root.right;
       }else {
           root = root.left;
       }
   }
   let cur = root;
   // 此时root已经在[L, R] 范围内,处理左孩子元素小于L的情况
   while(cur!==null) {
       while(cur.left&&cur.left.val<low) {
           cur.left = cur.left.right;
       }
       cur = cur.left;
   }
   cur =  root;
   // 此时root已经在[L, R] 范围内,处理右孩子大于R的情况
   while(cur!==null) {
       while(cur.right&&cur.right.val>high) {
           cur.right = cur.right.left;
       }
       cur = cur.right;
   }
   return root;
};