二叉树_题目32:450. 删除二叉搜索树中的节点

61 阅读4分钟

题目32:450. 删除二叉搜索树中的节点

450. 删除二叉搜索树中的节点

同类题目

前置知识

二叉搜索树(Binary Search Tree) :也叫做二叉查找树、有序二叉树或者排序二叉树。是指一棵空树或者具有下列性质的二叉树:

  • 如果任意节点的左子树不为空,则左子树上所有节点的值均小于它的根节点的值。
  • 如果任意节点的右子树不为空,则右子树上所有节点的值均大于它的根节点的值。
  • 任意节点的左子树、右子树均为二叉搜索树。

题目描述

给定一个二叉搜索树的根节点 root 和一个值 key,删除二叉搜索树中的 key 对应的节点,并保证二叉搜索树的性质不变。返回二叉搜索树(有可能被更新)的根节点的引用。

一般来说,删除节点可分为两个步骤:

  1. 首先找到需要删除的节点;
  2. 如果找到了,删除它。

示例 1:

 输入:root = [5,3,6,2,4,null,7], key = 3
 输出:[5,4,6,2,null,null,7]
 解释:给定需要删除的节点值是 3,所以我们首先找到 3 这个节点,然后删除它。
 一个正确的答案是 [5,4,6,2,null,null,7], 如下图所示。
 另一个正确答案是 [5,2,6,null,4,null,7]。

示例 2:

 输入: root = [5,3,6,2,4,null,7], key = 0
 输出: [5,3,6,2,4,null,7]
 解释: 二叉树不包含值为 0 的节点

示例 3:

 输入: root = [], key = 0
 输出: []

提示:

  • 节点数的范围 [0, 104].
  • -105 <= Node.val <= 105
  • 节点值唯一
  • root 是合法的二叉搜索树
  • -105 <= key <= 105

进阶: 要求算法时间复杂度为 O(h),h 为树的高度。

思路分析

递归

如何在搜索二叉树中删除一个节点

  • 若被删除节点的左子树为空,使用右子树代替被删除节点的位置。(这个情况包括了左右子树为空的情况)
  • 若被删除节点的右子树为空,使用左子树代替被删除节点的位置。
  • 若被删除节点的左右子树均不为空,则根据二叉搜索树的中序遍历有序性,删除该节点时,可以使用其直接前驱(或直接后继)代替被删除节点的位置。

直接前驱:在中序遍历中,节点p 的直接前驱为其左子树的最右侧的叶子节点。

直接后继:在中序遍历中,节点p的直接后继为其右子树的最左侧的叶子节点。

程序代码

方法一

 // https://leetcode.cn/problems/delete-node-in-a-bst/
 // 450. 删除二叉搜索树中的节点
 ​
 #include<iostream>
 #include<vector>
 using namespace std;
 ​
 struct TreeNode {
     int val;
     TreeNode *left;
     TreeNode *right;
     TreeNode() : val(0), left(nullptr), right(nullptr) {}
     TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
     TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {}
 };
 ​
 ​
 class Solution {
 public:
     TreeNode* deleteNode(TreeNode* root, int key) {
         if(root == nullptr)
         {
             return nullptr;
         }
 ​
         // 寻找节点
         if(key > root->val)
         {
             root->right = deleteNode(root->right,key);
         }else if(key < root->val)
         {
             root->left = deleteNode(root->left,key);
         }else
         {
             // 找到了要删除的节点
             // 0) 下面这个包含了左右子树为空的情况
             // 1) 要删除节点的左孩子为空,则使用右子树代替当前位置,返回右子树
             if(root->left == nullptr)
             {
                 return root->right;
             }
             // 2) 要删除节点的右孩子为空,则使用左子树代替当前位置,返回左子树
             else if(root->right == nullptr)
             {
                 return root->left;
             }else
             // 3) 要删除节点左右都不为空,找到右子树的最左侧,把左子树挂上
             {
                 TreeNode *cur = root->right;
                 // 这里使用左子树进行判断,因为最后要使用cur 进行关联root的左子树
                 while(cur->left)
                 {
                     cur = cur->left;
                 }
                 cur->left = root->left;
                 return root->right;
             }  
         }
 ​
         return root;
     }
 };
 ​

复杂度分析

时间复杂度O(N),一共有 N个节点,最坏情况下,每个节点都要遍历一次。

空间复杂度O(N),递归深度最差情况下退化为N

思路分析

迭代

区别:在找到要删除的这个节点使用迭代的方式 ,找到之后删除的方法一致

程序代码

方法二

 class Solution {
 public:
 ​
     TreeNode* deleteOneNode(TreeNode *root)
     {
         if(root->left == nullptr)
         {
             return root->right;
         }else if(root->right == nullptr)
         {
             return root->left;
         }else
         {
             TreeNode *cur = root->right;
             while(cur->left)
             {
                 cur = cur->left;
             }
             cur->left = root->left;
             return root->right;
         }
 ​
 ​
     }
 ​
     TreeNode* deleteNode(TreeNode* root, int key) {
         if(root == nullptr)
         {
             return nullptr;
         }
         // 开始找要删除的节点
         TreeNode * cur = root;
         // 记录其父节点,删除时要使用父节点进行删除
         TreeNode *pre = nullptr;
         while(cur != nullptr)
         {
             if(cur->val == key)
             {
                 break;
             }
             if(cur->val > key)
             {
                 pre = cur;
                 cur = cur->left;
             }else
             {
                 pre = cur;
                 cur = cur->right;
             }
         }
 ​
         // 说明只有根节点
         if(pre == nullptr)
         {
             return deleteOneNode(cur);
         }
 ​
         // 要删除的 key 在左孩子
         if(pre->left != nullptr && pre->left->val == key){
             pre->left = deleteOneNode(cur);
         }
         // 要删除的 key 在右孩子
         if(pre->right != nullptr && pre->right->val == key){
             pre->right = deleteOneNode(cur);
         }
         return root;
     }
 };

复杂度分析

时间复杂度O(N),一共有 N个节点,最坏情况下,每个节点都要遍历一次。

空间复杂度O(1)

更多相关知识:github.com/pingguo1987…