LeetCode刷题day42

简介: LeetCode刷题day42

235. 二叉搜索树的最近公共祖先


题目描述


给定一个二叉搜索树, 找到该树中两个指定节点的最近公共祖先。


百度百科中最近公共祖先的定义为:“对于有根树 T 的两个结点 p、q,最近公共祖先表示为一个结点 x,满足 x 是 p、q 的祖先且 x 的深度尽可能大(一个节点也可以是它自己的祖先)。”


例如,给定如下二叉搜索树: root = [6,2,8,0,4,7,9,null,null,3,5]


24404052d0464a1eafe75437306fa77b.png


示例 1:


输入: root = [6,2,8,0,4,7,9,null,null,3,5], p = 2, q = 8
输出: 6


解释: 节点 2 和节点 8 的最近公共祖先是 6。

示例 2:


输入: root = [6,2,8,0,4,7,9,null,null,3,5], p = 2, q = 4
输出: 2

解释: 节点 2 和节点 4 的最近公共祖先是 2, 因为根据定义最近公共祖先节点可以为节点本身。


思路分析


做过二叉树:公共祖先问题的同学应该知道,利用回溯从低向上搜索,遇到一个节点的左子树有p,右子树有q,那么当前节点就是最近公共祖先.

但是本题是二叉搜索树,是有序的,其处理思路可以更简单.

只要从上到下遍历的时候,cur节点的数值在[p,q]区间中,就可说明该节点cur就是最近公共祖先了.


那么我们可以采用递归遍历(以先序遍历为例):

如图所示:p为节点3,q为节点5


7e830f549d0e4e20b0b82df0ba9cd246.png



可以看出直接按照指定的方向,就可以找到节点4,为最近公共祖先,而且不需要遍历整棵树,找到结果直接返回!


方法1:递归


递归三部曲:


参数和返回值:参数为递归的根节点以及两个节点p,q; 返回值为查找到的最近公共祖先


结束条件:当节点为空则结束即可.


每一层递归逻辑:如果cur->val 大于p->val和q->val,那么应该向左遍历,如果 cur->val小于p->val和q->val,则应该向右遍历. 如果在两者之间,则该节点即是最近公共祖先节点,直接返回该节点即可.


参考代码1

//递归法   主要是寻找区间 [p,q],root在这个区间之间,那么root就是公共祖先. 
TreeNode* lowestCommonAncestor(TreeNode* root, TreeNode* p, TreeNode* q) {
  if(root==NULL){
    return NULL;
  }
  //左子树
  if(root->val > p->val && root->val > q->val){//注意p,和q的大小是未知的,所以两个都要进行比较 
    TreeNode* left = lowestCommonAncestor(root->left,p,q);//遍历一条边的做法 
    if(left){//如果最后不为空则说明找到了,则直接进行返回 
      return left;
    } 
  } 
  if(root->val < p->val && root->val < q->val){
    TreeNode* right = lowestCommonAncestor(root->right,p,q);
    if(right){
      return right;
    }
  } 
  return root;//如果root->val 即不都大于p,q;又不都小于p,q; 则说明root肯定是位于p,q之间的 
}


方法2:迭代法


利用二叉搜索树的有序性来寻找即可.


参考代码2


TreeNode* lowestCommonAncestor(TreeNode* root, TreeNode* p, TreeNode* q) {
  while(root){
    if(root->val > p->val && root->val > q->val){//在左子树
      root = root->left;
    } else if(root->val < p->val && root->val < q->val){ //在右子树
      root = root->right;
    }else{ // root在区间中间 
      return root;
    }
  } 
  return NULL; 
} 


701.二叉搜索树中的插入操作


题目描述


给定二叉搜索树(BST)的根节点和要插入树中的值,将值插入二叉搜索树。 返回插入后二叉搜索树的根节点。 输入数据 保证 ,新值和原始二叉搜索树中的任意节点值都不同。

注意,可能存在多种有效的插入方式,只要树在插入后仍保持为二叉搜索树即可。 你可以返回 任意有效的结果 。


示例 1:


e803799b87b54c0d8be779a93fa28ca1.jpg


输入:root = [4,2,7,1,3], val = 5
输出:[4,2,7,1,3,5]

解释:另一个满足题目要求可以通过的树是:


571569fddf3a4545a97da7182ef487ae.jpg



思路分析


这个题虽然有多种答案,当时我们在解决时只需要 按照二叉搜索树的规则去遍历,遇到空节点插入节点即可.



893ea9a8753d4580adcc2c4cce25c266.gif



方法一:递归(很野的路子)


如果要向root的左子树遍历,但是root->left为空,则直接插入节点返回即可,如果不为空则继续遍历.


同理遍历右子树.


参考代码


//递归法
void traversal(TreeNode* root,int val) {
  if(val < root->val) { //递归法的野路子.. 
    if(!root->left) {//直接在本层判断左子树是否为空 
      root->left = new TreeNode(val);
    } else {
      traversal(root->left,val);
    }
  }
  if(val > root->val) {
    if(!root->right) {//在递归之前判断右子树是否为空.. 
      root->right = new TreeNode(val);
    } else {
      traversal(root->right,val);
    }
  }
  return;
}
TreeNode* insertIntoBST(TreeNode* root, int val) {
  if(root==NULL) {
    root = new TreeNode(val);
  }
  traversal(root,val);
  return root;
}

方法二:递归(不使用返回值)


插入节点,可以用俩辅助节点parent,cur节点.每次cur为空时,便是需要插入节点了,根绝parent->val和val的大小来进行插入的位置.


参考代码2


TreeNode* parent = new TreeNode(0);
void traversal(TreeNode* cur,int val) {
  if(cur == NULL){
    TreeNode* node = new TreeNode(val);
    if(val > parent->val){
      parent->right = node;
    }else{
      parent->left  = node;
    }
    return;
  }
  parent = cur;
  if(val < cur->val){
    traversal(cur->left,val);
  }else if(val > cur->val){
    traversal(cur->right,val);
  }
  return;
}
TreeNode* insertIntoBST(TreeNode* root, int val) {
  if(root==NULL){
    root = new TreeNode(0);
  } 
  traversal(root,val);
  return root;
}

方法三:递归


递归三要素:


  • 参数和返回值:参数为递归子树的根节点以及要插入的节点值.
  • 结束条件:当根节点为空时,插入节点,递归结束.
  • 单层递归逻辑: 如果val < root->val,向左递归. val > root->val,向右递归.


参考代码3

TreeNode* insertIntoBST(TreeNode* root, int val) {
  if(root==NULL){//如果节点为空了,则直接以val创建节点并返回 
    return new TreeNode(val);
  } 
  if(val < root->val){//向左子树寻找插入的位置. 
    root->left = insertIntoBST(root->left,val);
  }
  if(val > root->val){//向右边子树寻找插入的位置
    root->right = insertIntoBST(root->right,val);
  }
  return root;
}

方法四:迭代


思路和不用返回值的递归类似:先找到要插入的位置的父节点,然后根据大小进行插入.


参考代码4

//迭代法 
TreeNode* insertIntoBST(TreeNode* root, int val) {
  if(root==NULL){
    root = new TreeNode(val);
    return root;
  }
  TreeNode* pre=NULL;//插入节点一般就定义两个变量,不要在想着用野套路本次判断下一次然后插入节点...太混乱了. 
  TreeNode* cur = root;
  while(cur!=NULL){
    pre = cur;//更新上一个节点  必须在这里进行更新,因为下面cur就发生变化了 
    if(val < cur->val){//这里不可以分开成两个if哦!因为一旦分开,第二个if中的cur就和第一个if中的cur不同了,很可能出错. 
      cur = cur->left;
    }else if(val > cur->val){
      cur = cur->right;
    } 
  }
  //在pre上插入val节点
  if(val < pre->val){
    pre->left = new TreeNode(val);
  }else if(val > pre->val){
    pre->right = new TreeNode(val);
  } 
  return root;
} 


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


题目描述


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

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

首先找到需要删除的节点;

如果找到了,删除它。


示例 1:

65b538eea1f843e0b66f091c6c7584d3.jpg



输入: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],如下图所示



97ae343ea1454402abfbc9015cc329bc.jpg



示例 2:


输入: root = [5,3,6,2,4,null,7], key = 0
输出: [5,3,6,2,4,null,7]


解释: 二叉树不包含值为 0 的节点

示例 3:


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


方法一:递归

递归三部曲:


  • 确定参数和返回值:参数便是遍历的子树的根节点和要删除的节点值. 返回值是删除节点后子树的根节点.
  • 结束条件:如果root==null,则说明没找到要删除的节点,直接返回
  • 确定单层递归逻辑:


有以下三种情况:


第一种情况:没找到删除的节点,遍历到空节点直接返回.

第二种情况:找到了删除的节点,对于删除操作要分成四种情况,并且每次都要返回当前子树 的根节点给上一层


  • 左右孩子都为空(叶子节点),直接删除节点, 返回NULL为根节点
  • 删除节点的左孩子为空,右孩子不为空,删除节点,右孩子补位,返回右孩子为根节点
  • 删除节点的右孩子为空,左孩子不为空,删除节点,左孩子补位,返回左孩子为根节点
  • 左右孩子节点都不为空,则将删除节点的左子树头结点(左孩子)放到删除节点的右子树的最左面节点的左孩子上,返回删除节点右孩子为新的根节点。


第三种情况:暂时还没有找到要删除的节点则继续进行递归寻找

左右孩子都不为空的情况有点难以理解,看下面动画:


e22d09d66178434c936de3161ca0d33c.gif


参考代码

TreeNode* deleteNode(TreeNode* root, int key) {
  // 第一种情况:没找到删除的节点 ,遍历到空节点直接返回NULL
  if(root==NULL) {
    return root;
  }
  //第二种情况:找到了删除的节点,对于删除操作要分成四种情况,并且每次都要返回当前子树 的根节点给上一层.(因为删除节点后指向会变更)
  if(root->val == key) {
    if(!root->left && !root->right) { //1.当左右孩子都为空,直接删除节点,返回NULL
      delete root;
      return NULL;
    } else if(!root->right) {//2.当左孩子不为空,右孩子为空,删除节点左孩子补位.返回左孩子
      TreeNode* temp = root->left;
      delete root;
      return temp;
    } else if(!root->left) { //3.当右孩子不为空,左孩子为空,删除节点右孩子补位.返回右孩子
      TreeNode* temp = root->right;
      delete root;
      return temp;
    } else {//4.当左右孩子都不为空,则将删除节点的左子树 移到删除节点右子树的最左孩子位置上;并返回删除节点的右子树
      TreeNode *cur = root->right;
      while(cur->left) { //寻找删除节点的最左侧节点
        cur = cur->left;
      }
      cur->left = root->left;//移到删除节点右子树的最左孩子位置上
      TreeNode* temp = root->right;
      delete root;
      return temp;
    }
  }
  //第三种情况:暂时还没有找到要删除的节点
  if(key < root->val) {
    root->left = deleteNode(root->left,key);
  } else if(key > root->val) {
    root->right = deleteNode(root->right,key);
  }
  return root;
}


相关文章
|
1月前
|
Python
【Leetcode刷题Python】剑指 Offer 32 - III. 从上到下打印二叉树 III
本文介绍了两种Python实现方法,用于按照之字形顺序打印二叉树的层次遍历结果,实现了在奇数层正序、偶数层反序打印节点的功能。
38 6
|
1月前
|
Python
【Leetcode刷题Python】剑指 Offer 26. 树的子结构
这篇文章提供了解决LeetCode上"剑指Offer 26. 树的子结构"问题的Python代码实现和解析,判断一棵树B是否是另一棵树A的子结构。
33 4
|
1月前
|
搜索推荐 索引 Python
【Leetcode刷题Python】牛客. 数组中未出现的最小正整数
本文介绍了牛客网题目"数组中未出现的最小正整数"的解法,提供了一种满足O(n)时间复杂度和O(1)空间复杂度要求的原地排序算法,并给出了Python实现代码。
63 2
|
1月前
|
索引 Python
【Leetcode刷题Python】从列表list中创建一颗二叉树
本文介绍了如何使用Python递归函数从列表中创建二叉树,其中每个节点的左右子节点索引分别是当前节点索引的2倍加1和2倍加2。
32 7
|
1月前
|
Python
【Leetcode刷题Python】剑指 Offer 30. 包含min函数的栈
本文提供了实现一个包含min函数的栈的Python代码,确保min、push和pop操作的时间复杂度为O(1)。
16 4
|
1月前
|
Python
【Leetcode刷题Python】剑指 Offer 22. 链表中倒数第k个节点
Leetcode题目"剑指 Offer 22. 链表中倒数第k个节点"的Python解决方案,使用双指针法找到并返回链表中倒数第k个节点。
39 5
|
1月前
|
算法 Python
【Leetcode刷题Python】 LeetCode 2038. 如果相邻两个颜色均相同则删除当前颜色
本文介绍了LeetCode 2038题的解法,题目要求在一个由'A'和'B'组成的字符串中,按照特定规则轮流删除颜色片段,判断Alice是否能够获胜,并提供了Python的实现代码。
36 3
|
1月前
|
算法 Python
【Leetcode刷题Python】剑指 Offer 33. 二叉搜索树的后序遍历序列
本文提供了一种Python算法,用以判断给定整数数组是否为某二叉搜索树的后序遍历结果,通过识别根节点并递归验证左右子树的值是否满足二叉搜索树的性质。
14 3
|
1月前
|
Python
【Leetcode刷题Python】剑指 Offer 32 - II. 从上到下打印二叉树 II
本文提供了一种Python实现方法,用于层次遍历二叉树并按层打印结果,每层节点按从左到右的顺序排列,每层打印到一行。
27 3
|
1月前
|
Python
【Leetcode刷题Python】剑指 Offer 21. 调整数组顺序使奇数位于偶数前面
Leetcode题目"剑指 Offer 21. 调整数组顺序使奇数位于偶数前面"的两种Python解决方案,一种是使用双端队列调整数组顺序,另一种是使用双指针法将奇数移到数组前半部分,偶数移到后半部分。
19 4