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;
}


相关文章
|
3月前
|
Unix Shell Linux
LeetCode刷题 Shell编程四则 | 194. 转置文件 192. 统计词频 193. 有效电话号码 195. 第十行
本文提供了几个Linux shell脚本编程问题的解决方案,包括转置文件内容、统计词频、验证有效电话号码和提取文件的第十行,每个问题都给出了至少一种实现方法。
LeetCode刷题 Shell编程四则 | 194. 转置文件 192. 统计词频 193. 有效电话号码 195. 第十行
|
4月前
|
Python
【Leetcode刷题Python】剑指 Offer 32 - III. 从上到下打印二叉树 III
本文介绍了两种Python实现方法,用于按照之字形顺序打印二叉树的层次遍历结果,实现了在奇数层正序、偶数层反序打印节点的功能。
61 6
|
4月前
|
Python
【Leetcode刷题Python】剑指 Offer 26. 树的子结构
这篇文章提供了解决LeetCode上"剑指Offer 26. 树的子结构"问题的Python代码实现和解析,判断一棵树B是否是另一棵树A的子结构。
54 4
|
4月前
|
搜索推荐 索引 Python
【Leetcode刷题Python】牛客. 数组中未出现的最小正整数
本文介绍了牛客网题目"数组中未出现的最小正整数"的解法,提供了一种满足O(n)时间复杂度和O(1)空间复杂度要求的原地排序算法,并给出了Python实现代码。
124 2
|
1月前
|
机器学习/深度学习 人工智能 自然语言处理
280页PDF,全方位评估OpenAI o1,Leetcode刷题准确率竟这么高
【10月更文挑战第24天】近年来,OpenAI的o1模型在大型语言模型(LLMs)中脱颖而出,展现出卓越的推理能力和知识整合能力。基于Transformer架构,o1模型采用了链式思维和强化学习等先进技术,显著提升了其在编程竞赛、医学影像报告生成、数学问题解决、自然语言推理和芯片设计等领域的表现。本文将全面评估o1模型的性能及其对AI研究和应用的潜在影响。
40 1
|
3月前
|
数据采集 负载均衡 安全
LeetCode刷题 多线程编程九则 | 1188. 设计有限阻塞队列 1242. 多线程网页爬虫 1279. 红绿灯路口
本文提供了多个多线程编程问题的解决方案,包括设计有限阻塞队列、多线程网页爬虫、红绿灯路口等,每个问题都给出了至少一种实现方法,涵盖了互斥锁、条件变量、信号量等线程同步机制的使用。
LeetCode刷题 多线程编程九则 | 1188. 设计有限阻塞队列 1242. 多线程网页爬虫 1279. 红绿灯路口
|
4月前
|
索引 Python
【Leetcode刷题Python】从列表list中创建一颗二叉树
本文介绍了如何使用Python递归函数从列表中创建二叉树,其中每个节点的左右子节点索引分别是当前节点索引的2倍加1和2倍加2。
70 7
|
4月前
|
Python
【Leetcode刷题Python】剑指 Offer 22. 链表中倒数第k个节点
Leetcode题目"剑指 Offer 22. 链表中倒数第k个节点"的Python解决方案,使用双指针法找到并返回链表中倒数第k个节点。
54 5
|
4月前
|
Python
【Leetcode刷题Python】剑指 Offer 30. 包含min函数的栈
本文提供了实现一个包含min函数的栈的Python代码,确保min、push和pop操作的时间复杂度为O(1)。
31 4
|
4月前
|
Python
【Leetcode刷题Python】剑指 Offer 21. 调整数组顺序使奇数位于偶数前面
Leetcode题目"剑指 Offer 21. 调整数组顺序使奇数位于偶数前面"的两种Python解决方案,一种是使用双端队列调整数组顺序,另一种是使用双指针法将奇数移到数组前半部分,偶数移到后半部分。
29 4