代码随想录算法训练营Day21|二叉搜索树的最小绝对差、二叉搜索树中的众数、二叉树的最近公共祖先

二叉搜索树的最小绝对差

思路1:将二叉搜索树转化成一个有序数组,在有序数组上求两个数的最小值差。

只需要遍历有序数组,统计出最小值差

class Solution{
private:
  vector<int>vec;
  void traversal(TreeNode* root){
    if(root == NULL)return;
    traversal(root->left);
    vec.push_back(root->val);//将二叉搜索树转化成有序数组
    traversal(root->right);
  }
  public:
    int getMinimumDifference(TreeNode* root){
        vec.clear();
        traversal(root);
        if(vec.size()<2)return 0;
        int result = INT_MAX;
        for(int i = 1; i < vec.size();i++){
            result = min(result,vec[i] - vec[i - 1]);
        }
        return result;
    }
};

思路2:一遍用中序遍历,一边计算差值,需要用一个pre节点记录cur节点的前一个节点。

class Solution{
private:
  int result = INT_MAX;
  TreeNode* pre = NULL;
  void traversal(TreeNode* cur){
    if(cur == NULL)return;
    traversal(cur->left);
    if(pre != NULL){
        result = min(result,cur->val - pre->val);
    }
    pre = cur;//记录前一个
    traversal(cur->right);//右
  }
  public:
     int getMinimumDifference(TreeNode* root){
        traversal(root);
        return result;
     }
};

中序遍历的一种迭代法

class Solution{
public:
   int getMinimumDifference(TreeNode* root){
    stack<TreeNode*>st;
    TreeNode* cur = root;
    TreeNode* pre = NULL;
    int result = INT_MAX;
    while(cur != NULL || !st.empty()){
        if(cur != NULL){//指针访问节点到最底层
           st.push(cur);//将访问的节点放进栈
           cur = cur->left;
        }else{
            cur = st.top();
            st.pop();
            if(pre != NULL){
                result = min(result,cur->val - pre->val);
            }
            pre = cur;
            cur = cur->right;
        }
    }
    return result;
   }
};

二叉搜索树的特点“有序”!!!递归遍历中前后两个指针的记录!!!

二叉搜索树中的众数

1.不是二叉搜索树的话,把树遍历,用map统计频率,将频率进行排序,最后取前面高频的元素的集合。

>>遍历方法任选

>>对map里频率进行排序

>>取前面高频的元素

class Solution{
preivate:

void searchBST(TreeNode* cur,unordered_map<int,int>&map){//前序遍历
if(cur == NULL)return;
map[cur->val]++;//统计元素频率
search(cur->left,map);
search(cur->right,map);
return ;
}
bool static cmp(const pair<int,int>& a,const pair<int,int>& b){
    return a.second > b.second;
}
public:
   vector<int>findMode(TreeNode* root){
      unordered_map<int,int>map;
      vector<int>result;
      if(root == NULL)return result;
      searchBST(root,map);
      vector<pair<int,int>>vec(map.begin(),map.end());
      sort(vec.begin(),vec.end(),cmp);
      result.push_back(vec[0].first);
      for(int i = 1;i < vec.size(); i++){
        //取最高的放到result数组中
        if(vec[i].second == vec[0].second)result.push_back(vec[i].first);
        else break;
      }
      return result;
   }
}

2.是二叉搜索树,中序遍历是有序的

遍历过程中,相邻元素进行比较,把出现频率高的元素输出

可以使用前后双指针pre和cur对树进行操作。

而且初始化pre = NULL从第一个元素开始比较

maxCount和上一个最大频率相等,result.push_back放进结果集,如果大于上一个,result.clear清空结果集,前面的都清除,把这个再push_back(),

class Solution{
private:
   int maxCount = 0;
   TreeNode* pre = NULL;
   vector<int>result;
   void searchBST(TreeNode* cur){
    if(cur == NULL)return;

    searchBST(cur->left);

    if(pre == NULL){
        count = 1;
    }else if(pre->val == cur->val){
        count++;
    }else{
        count = 1;
    }
    pre = cur;

    if(count == maxCount){
        result.push_back(cur->val);
    }

    if(count > maxCount){
        maxCount = count;
        result.clear();//清空result
        result.push_back(cur->val);
    }

    searchBST(cur->right);
    return;
   }

   public:
      vector<int>findMode(TreeNode* root){
        count = 0;
        maxCount = 0;
        pre = NULL;
        result.clear();

        searchBST(root);
        return result;
      }
};

迭代法,把中序转成迭代,中间节点的处理逻辑完全一样。

class Solution{
public:
   vector<int>findMode(TreeNode* root){
       stack<TreeNode*>st;
       TreeNode* cur = root;
       TreeNode* pre = NULL;
       int maxCount = 0;//最大频率
       int count = 0;//统计频率
       vector<int>result;
       while(cur != NULL || !st.empty()){
          if(cur != NULL){  //指针来访问节点,访问到最底层
             st.push(cur);//将访问的节点放进栈
             cur = cur->left;
          }else{
            cur = st.top();
            st.pop();              //中
            if(pre == NULL){//第一个节点
                count = 1;
            }else if(pre->val == cur->val){//与前一个节点数值相同
                count++;
            }else{//与前一个节点不同
                count = 1;
            }
            if(count == maxCount){//如果和最大值相同,放进result
                result.push_back(cur->val);
            }

            if(count > maxCount){//如果计数大于最大值频率
                maxCount = count;//更新最大频率
                result.clear();//清空result,让之前result里的元素失效
                result.push_back(cur->val);
            }
            pre = cur;
            cur = cur->right;//右
          }

       }
       return result;
   }
};

二叉树的最近公共祖先

思路:一种情况是,该节点的左子树出现节点p,右子树出现节点q。另一种,节点p即为节点p和节点q的最近公共祖先。

二叉树节点数值是不重复的,所以p和q一定存在。节点本身p(q),它拥有一个子孙节点q(p).

第一种情况实现,顺带实现了情况二。

递归三部曲:

>>确定递归函数返回值及参数。

需要递归函数返回值,来告诉我们是否找到节点q或者p,那么返回值为bool类型就可以了。

但我们还要返回最近公共节点,可以利用上题目中返回值是TreeNode* ,那么如果遇到p或者q,就把q或者p返回,返回值不为空,就说明找到了q或者p.

TreeNode* lowestCommonAncestor(TreeNode* root,TreeNode* p,TreeNode* q)

>>确定终止条件

遇到空的话,因为树是空的了,所以返回空。遇到q,p,将其返回。这个返回值,后面在中节点的处理过程中会用到。

if(root == q || root == p || root == NULL)return root;

>>确定单层递归

值得注意的是,本题函数有返回值,是因为回溯的过程需要递归函数的返回值做判断,但本题我们依然要遍历树的所有节点。

在递归函数有返回值的情况下,如果要搜索一条边,递归函数返回值不为空的时候,立刻返回,如果搜索整个树,直接用一个变量left,right接住返回值,这个left,right后序还有逻辑处理的需要,也就是后序遍历中处理中间节点的逻辑(也就是回溯)。

搜索一条边:

if(递归函数(root->left))return ;
if(递归函数(root->right)) return ;

搜索整个树的写法:

left = 递归函数(root->left);//左
right = 递归函数(root->right);//右
left与right的逻辑处理;//中

用left和right接住左子树和右子树的返回值,代码如下:

TreeNode* left = lowestCommonAncestor(root->left,p,q);

TreeNode* right = lowestCommonAncestor(root->right,p,q);

如果left和right都不为空,说明此时root就是最近公共节点

如果left为空,right不为空,就返回right,说明目标节点是通过right返回的,反之亦然。

if(left == NULL && right != NULL)return right;

else if (left != NULL && right == NULL)return left;

else {//left == NULL && right == NULL

return NULL;

}

class Solution{
public:
   TreeNode* lowestCommonAncestor(TreeNode* root,TreeNode* p,TreeNode* q){
    if(root == q || root == p || root == NULL)return root;
    TreeNode* left = lowestCommonAncestor(root->left,p,q);
    TreeNode* right = lowestCommonAncestor(root->right,p,q);
    if(left != NULL && right != NULL)return root;

    if(left == NULL && right != NULL)return right;
    else if(left != NULL && right == NULL)return left;
    else{//left == NULL && right == NULL
        return NULL;
    }
   }
};

精简后:

class Solution{
public:
  TreeNode* lowestCommonAncestor(TreeNode* root,TreeNode* p,TreeNode* q){
    if(root == q || root == p || root == NULL)return root;
    TreeNode* left = lowestCommonAncestor(root->left,p,q);
    TreeNode* right = lowestCommonAncestor(root->right,p,q);
    if(left != NULL && right != NULL)return root;
    if(left == NULL)return right;
    return left;
  }
};

求最小公共祖先,需要从底层往上遍历,那么二叉树,只能通过后序遍历,实现从底向上的遍历方式。

在回溯过程中,必然要遍历整棵二叉树,即使已经找到结果了,依然要把其他节点遍历完,因为要使用递归函数的返回值,也就是代码中国的left和right做逻辑判断。

要理解如果返回值left为空,right不为空为什么要返回right,为什么可以用返回right传给上一层结果。

可以说这里每一步,都是有难度的,都需要对二叉树,递归和回溯有一定的理解。

 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/376424.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

H5 简约四色新科技风引导页源码

H5 简约四色新科技风引导页源码 源码介绍&#xff1a;一款四色切换自适应现代科技风动态背景的引导页源码&#xff0c;源码有主站按钮&#xff0c;分站按钮2个&#xff0c;QQ联系站长按钮一个。 下载地址&#xff1a; https://www.changyouzuhao.cn/11990.html

Windows 启动项无法打开 Aanconda 问题。pyqt noBinding

windows中点击Anaconda navigator 没有反应: ## 解决 (右键运行Anaconda prompt) 以管理员身份运行&#xff1a; 分别运行以下命令&#xff1a; conda update conda conda update anaconda-navigatorpip uninstall PyQt5 pip install PyQt5 pip install pyqtwebengine

5年前端仔的2023年终总结

突然发现已经有好几个月没有写过博客总结过什么&#xff0c;小小辩解一下&#xff0c;其实并不是笔者停止的学习和总结&#xff0c;随着在前端这个行业的逐年深入&#xff0c;渐渐的很多收获不再是像之前简单的技术点的确定性描述讲解了&#xff0c;而是某个领域的知识体系的串…

『运维备忘录』之 TAR 命令详解

运维人员不仅要熟悉操作系统、服务器、网络等只是&#xff0c;甚至对于开发相关的也要有所了解。很多运维工作者可能一时半会记不住那么多命令、代码、方法、原理或者用法等等。这里我将结合自身工作&#xff0c;持续给大家更新运维工作所需要接触到的知识点&#xff0c;希望大…

机器学习-线性回归法

线性回归算法 解决回归问题思想简单&#xff0c;实现容易许多强大的非线性模型的基础结果具有很好的可解释性蕴含机器学习中的很多重要思想 样本特征只有一个&#xff0c;称为&#xff1a;简单线性回归 通过分析问题&#xff0c;确定问题的损失函数或者效用函数 通过最优化…

jsp课程管理系统Myeclipse开发mysql数据库web结构java编程计算机网页项目

一、源码特点 JSP 课程管理系统是一套完善的java web信息管理系统&#xff0c;对理解JSP java编程开发语言有帮助&#xff0c;系统具有完整的源代码和数据库&#xff0c;系统主要采用B/S模式开发。开发环境为TOMCAT7.0,Myeclipse8.5开发&#xff0c;数据库为Mysql5.0&…

Termux配置安卓编译环境

前言 Termux安装后&#xff0c;就相当于把手机变成了一台Linux服务器&#xff0c;而且现在手机卡通常是能拿到ipv6公网地址的&#xff0c;所以&#xff0c;这个服务器能干啥&#xff1f; 编程搭建网站跑脚本 本文讲述的就是怎么在Termux搭建安卓编译环境&#xff0c;实现手机…

CV | Medical-SAM-Adapter论文详解及项目实现

******************************* &#x1f469;‍⚕️ 医学影像相关直达&#x1f468;‍⚕️******************************* CV | SAM在医学影像上的模型调研【20240207更新版】-CSDN博客 CV | Segment Anything论文详解及代码实现 本文主要讲解Medical-SAM-Adapter论文及项…

HTML 样式学习手记

HTML 样式学习手记 在探索网页设计的世界时&#xff0c;我发现HTML元素的样式调整真的是个很酷的环节。通过简单的属性设置&#xff0c;就能让文字换上五彩斑斓的颜色、变换各异的字体和大小。特别是那个style属性&#xff0c;感觉就像是一扇通往CSS魔法世界的大门。 代码小试…

【Python】虚拟环境miniconda安装(python3.7, python3.x)

背景 使用Python开发项目时&#xff0c;我们一般都需要安装环境&#xff0c;可能是在物理机上直接安装&#xff0c;也可能是在虚拟环境上安装&#xff0c;当前是怎么按照conda环境的示例&#xff0c;可以指定安装Python3.x的所有版本。 安装 首先&#xff0c;需要登录当前的…

零基础学Python之网络编程

1.什么是socket 官方定义&#xff1a; 套接字&#xff08;socket&#xff09;是一个抽象层&#xff0c;应用程序可以通过它发送或接收数据&#xff0c;可对其进行像对文件一样的打开、读写和关闭等操作。套接字允许应用程序将I/O插入到网络中&#xff0c;并与网络中的其他应用…

国产信创领跑者:暴雨信息的创新与实践

随着数字化转型的加速推进&#xff0c;信创产业作为数字经济发展的重要支柱&#xff0c;正日益受到社会各界的广泛关注。在这个大背景下&#xff0c;暴雨信息积极响应国家号召&#xff0c;全面适配国产化&#xff0c;推动信创产业的技术创新和应用拓展&#xff0c;成为了行业的…

AWS创建快照定期备份

备注&#xff1a;aws有快照定期备份工具&#xff0c;名字叫【生命周期管理器】 选择实例点击创建 点击下一步后设置备份频率等 然后点击创建即可

(Python)字典列表数据本地存储工具

前言 一个简单的实现简便 "列表字典" 数据存储本地。 适合不会SQL但又想实现数据存储本地的同学。 操作使用都非常简单。 文件只做了简单的加密处理&#xff0c;如果需要复杂加密的同学可以修改加密函数。 温馨提示&#xff1a; 1.使用前&#xff0c;在项目目录…

人工智能福利站,初识人工智能,图神经网络学习,第三课

&#x1f3c6;作者简介&#xff0c;普修罗双战士&#xff0c;一直追求不断学习和成长&#xff0c;在技术的道路上持续探索和实践。 &#x1f3c6;多年互联网行业从业经验&#xff0c;历任核心研发工程师&#xff0c;项目技术负责人。 &#x1f389;欢迎 &#x1f44d;点赞✍评论…

九思OA user-list-3g sql注入

【产品&&漏洞简述】 九思OA办公软件全面实现协同工作、公文、流程审批、知识管理、项目管理、综合办公、信息共享、移动办公 等应用功能&#xff0c;并能够与其他异构系统整合&#xff0c;打破信息孤岛&#xff0c;建立完整的有效的企业工作平台和移动办公软件。 九思…

(2)(2.14) SPL Satellite Telemetry

文章目录 前言 1 本地 Wi-Fi&#xff08;费用&#xff1a;30 美元以上&#xff0c;范围&#xff1a;室内&#xff09; 2 蜂窝电话&#xff08;费用&#xff1a;100 美元以上&#xff0c;范围&#xff1a;蜂窝电话覆盖区域&#xff09; 3 手机卫星&#xff08;费用&#xff…

ChatGPT学习第一周

&#x1f4d6; 学习目标 掌握ChatGPT基础知识 理解ChatGPT的基本功能和工作原理。认识到ChatGPT在日常生活和业务中的潜在应用。 了解AI和机器学习的基本概念 获取人工智能&#xff08;AI&#xff09;和机器学习&#xff08;ML&#xff09;的初步了解。理解这些技术是如何支撑…

介绍一个关于 JSON 可视化的网站

最近在看到一个比较好玩的网站&#xff0c;可以将 JSON以可视化的方式展现出现&#xff0c;比如存在一下JSON数据&#xff1a; {"id": "f3bbc3bc-9f34-4bf7-8a0f-7e6f6e6fbb9a","isActive": false,"age": 25,"name": "…

阿里云服务器Windows系统无法远程连接到服务器桌面怎么办,选择通过Workbench远程连接进入不是桌面,而是命令行界面

最近发现阿里云的Windows系统服务器&#xff0c;点击“远程连接”后&#xff0c;如果直接点击默认的“通过Workbench远程连接”。 并不能直接进入服务器桌面&#xff0c;而是进入了命令行界面&#xff08;我记得以前是可以的&#xff09; 那么如何进入Windows系统服务器桌面呢 …