【面试高频题】难度 1/5,经典树的搜索(多语言)

题目描述

这是 LeetCode 上的 「109. 有序链表转换二叉搜索树」 ,难度为 「中等」

Tag : 「二叉树」、「树的搜索」、「分治」、「中序遍历」

给定一个单链表的头节点 head,其中的元素 按升序排序 ,将其转换为高度平衡的二叉搜索树。

本题中,一个高度平衡二叉树是指一个二叉树每个节点 的左右两个子树的高度差不超过

示例 1: alt

输入: head = [-10,-3,0,5,9]

输出: [0,-3,9,-10,null,5]

解释: 一个可能的答案是[0,-3,9,-10,null,5],它表示所示的高度平衡的二叉搜索树。

示例 2:

输入: head = []

输出: []

提示:

  • head 中的节点数在 范围内

递归分治

与上一题 108. 将有序数组转换为二叉搜索树 类似,但链表相对于数组,无法 找到构建当前 BST 根节点的“中点”下标。

一个仍可确保 时间复杂度(瓶颈在于需要创建 个节点),但需要 空间复杂度的做法是:对链表进行一次遍历,转成数组后套用上一题的做法。

一个不使用 空间复杂度的做法,需要每次遍历来找“中点”下标:起始我们先对 head 进行一次遍历,得到链表长度 ,随后仍然利用递归分治的思路进行构造,每次对入参的左右端点找“中点”,先通过直接结算的方式定位到偏移量 ,然后再通过从入参节点 head 出发往前走 的做法找到“中点”节点。

该做法每个节点的访问次数为在递归过程中被 所覆盖的次数,我们知道一个节点数量为 的平衡 BST 树高为 ,因此整体复杂度为

Java 代码:

class Solution {
    public TreeNode sortedListToBST(ListNode head) {
        int n = 0;
        ListNode cur = head;
        while (cur != null && ++n >= 0) cur = cur.next;
        return build(head, 0, n - 1);
    }
    TreeNode build(ListNode head, int l, int r) {
        if (l > r) return null;
        int mid = l + r >> 1, t = mid - l;
        ListNode cur = head;
        while (t-- > 0) cur = cur.next;
        TreeNode ans = new TreeNode(cur.val);
        ans.left = build(head, l, mid - 1);
        ans.right = build(cur.next, mid + 1, r);
        return ans;
    }
}

Python 代码:

class Solution:
    def sortedListToBST(self, head: Optional[ListNode]) -> Optional[TreeNode]:
        n = 0
        cur = head
        while cur:
            n += 1
            cur = cur.next
        return self.build(head, 0, n - 1)
        
    def build(self, head: ListNode, l: int, r: int) -> TreeNode:
        if l > r:
            return None
        mid = l + r >> 1
        t = mid - l
        cur = head
        while t > 0:
            cur = cur.next
            t -= 1
        ans = TreeNode(cur.val)
        ans.left = self.build(head, l, mid - 1)
        ans.right = self.build(cur.next, mid + 1, r)
        return ans

C++ 代码:

class Solution {
public:
    TreeNode* sortedListToBST(ListNode* head) {
        int n = 0;
        ListNode* cur = head;
        while (cur && ++n >= 0) cur = cur->next;
        return build(head, 0, n - 1);
    }
    TreeNode* build(ListNode* head, int l, int r) {
        if (l > r) return nullptr;
        int mid = l + r >> 1, t = mid - l;
        ListNode* cur = head;
        while (t-- > 0) cur = cur->next;
        TreeNode* ans = new TreeNode(cur->val);
        ans->left = build(head, l, mid - 1);
        ans->right = build(cur->next, mid + 1, r);
        return ans;
    }
};

TypeScript 代码:

function sortedListToBST(head: ListNode | null): TreeNode | null {
    const build = function (head: ListNode | null, l: number, r: number): TreeNode | null {
        if (l > r) return null;
        let mid = l + r >> 1, t = mid - l;
        let cur = head;
        while (t-- > 0) cur = cur.next;
        const ans = new TreeNode(cur!.val);
        ans.left = build(head, l, mid - 1);
        ans.right = build(cur!.next, mid + 1, r);
        return ans;
    }
    let n = 0;
    let cur = head;
    while (cur != null && ++n >= 0) cur = cur.next;       
    return build(head, 0, n - 1);
}
  • 时间复杂度:
  • 空间复杂度:

递归分治 - 中序遍历

由于给定的 nums 本身严格有序,而 BST 的中序遍历亦是有序。因此我们可以一边遍历链表,一边对 BST 进行构造。

具体的,我们仍然先对链表进行遍历,拿到链表长度 n。递归构造过程中传入左右端点 lr,含义为使用链表中 部分节点,但不再在每次递归中传入当前头结点,而是使用全局变量 head 来记录。

递归构造过程中,计算“中点”位置 ,并根据如下流程进行构造:

  1. 使用 构建左子树,使用变量 left 保存当前左子树的根节点
  2. 构建完左子树后,全局变量 head 必然来到了“中点”位置,用其构建根节点 ans,并将根节点与此前构造的 left 关联。同时让链表节点 head 后移
  3. 使用 构建右子树,并将其挂载到根节点 ans

如此一来,即可确保「链表遍历」和「BST 构造」的同步性。

Java 代码:

class Solution {
    ListNode head;
    public TreeNode sortedListToBST(ListNode _head) {
        head = _head;
        int n = 0;
        ListNode cur = head;
        while (cur != null && ++n >= 0) cur = cur.next;
        return build(0, n - 1);
    }
    TreeNode build(int l, int r) {
        if (l > r) return null;
        int mid = l + r >> 1;
        TreeNode left = build(l, mid - 1); 
        TreeNode ans = new TreeNode(head.val);
        head = head.next;
        ans.left = left;
        ans.right = build(mid + 1, r);
        return ans;
    }
}

Python 代码:

class Solution:
    def sortedListToBST(self, head: Optional[ListNode]) -> Optional[TreeNode]:
        self.head = head
        n = 0
        cur = self.head
        while cur is not None:
            n += 1
            cur = cur.next
        return self.build(0, n - 1)

    def build(self, l: int, r: int) -> TreeNode:
        if l > r:
            return None
        mid = l + r >> 1
        left = self.build(l, mid - 1)
        ans = TreeNode(self.head.val)
        ans.left = left
        self.head = self.head.next
        ans.right = self.build(mid + 1, r)
        return ans

C++ 代码:

class Solution {
public:
    ListNode* head;
    TreeNode* sortedListToBST(ListNode* _head) {
        head = _head;
        int n = 0;
        ListNode* cur = head;
        while (cur && n++ >= 0) cur = cur->next;
        return build(0, n - 1);
    }
    TreeNode* build(int l, int r) {
        if (l > r) return nullptr;
        int mid = l + r >> 1;
        TreeNode* left = build(l, mid - 1);
        TreeNode* ans = new TreeNode(head->val);
        ans->left = left;
        head = head->next;
        ans->right = build(mid + 1, r);
        return ans;
    }
};

TypeScript 代码:

function sortedListToBST(_head: ListNode | null): TreeNode | null {
    const build =function(l: number, r: number): TreeNode | null {
        if (l > r) return null;
        const mid = l + r >> 1;
        const left = build(l, mid - 1);
        const ans = new TreeNode(head.val);
        ans.left = left;
        head = head.next;
        ans.right = build(mid + 1, r);
        return ans;
    }
    let head = _head;
    let n = 0, cur = head;
    while (cur && n++ >= 0) cur = cur.next;
    return build(0, n - 1);
}
  • 时间复杂度:
  • 空间复杂度:

最后

这是我们「刷穿 LeetCode」系列文章的第 No.109 篇,系列开始于 2021/01/01,截止于起始日 LeetCode 上共有 1916 道题目,部分是有锁题,我们将先把所有不带锁的题目刷完。

在这个系列文章里面,除了讲解解题思路以外,还会尽可能给出最为简洁的代码。如果涉及通解还会相应的代码模板。

为了方便各位同学能够电脑上进行调试和提交代码,我建立了相关的仓库:https://github.com/SharingSource/LogicStack-LeetCode 。

在仓库地址里,你可以看到系列文章的题解链接、系列文章的相应代码、LeetCode 原题链接和其他优选题解。

更多更全更热门的「笔试/面试」相关资料可访问排版精美的 合集新基地 🎉🎉

本文由 mdnice 多平台发布

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

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

基于SSM的校园音乐平台系统

基于SSM的校园音乐平台系统~ 开发语言:Java数据库:MySQL技术:SpringSpringMVCMyBatisVue工具:IDEA/Ecilpse、Navicat、Maven 系统展示 主页 登录界面 管理员界面 歌手管理 歌曲管理 摘要 校园音乐平台系统(Campus Mu…

基于知识图谱建模、全文检索的智能知识管理库(源码)

一、项目介绍 一款全源码,可二开,可基于云部署、私有部署的企业级知识库云平台,一款让企业知识变为实打实的数字财富的系统,应用在需要进行文档整理、分类、归集、检索、分析的场景。 知识图谱提供了一种从海量文本和图像中抽取结…

【C++】:初阶模板

朋友们、伙计们,我们又见面了,本期来给大家解读一下有关Linux的基础知识点,如果看完之后对你有一定的启发,那么请留下你的三连,祝大家心想事成! C 语 言 专 栏:C语言:从入门到精通 数…

【算法-动态规划】最长公共子串

💝💝💝欢迎来到我的博客,很高兴能够在这里和您见面!希望您在这里可以感受到一份轻松愉快的氛围,不仅可以获得有趣的内容和知识,也可以畅所欲言、分享您的想法和见解。 推荐:kuan 的首页,持续学…

磁盘非跨盘访问算法实现

1. 背景说明 本算法基于已将磁盘分布合并并排序为升序线性表。实现示例为:磁盘扇区大小:32(可自定义),待拆分磁盘内存: [0 - 50],[60 - 100](可增加)。示意图如下&#x…

RISC-V 特权级架构

特权级别 级别的数值越大,特权级越高,掌控硬件的能力越强,在CPU硬件层面,M模式必须存在,其它模式可以不存在 执行环境调用 ecall ,这是一种很特殊的陷入类的指令, 相邻两特权级软件之间的接口正…

用于物体识别和跟踪的下游任务自监督学习-2-(计算机视觉中的距离度量+损失函数)

2.4 计算机视觉中的距离度量 在深度学习和计算机视觉中,距离度量通常用于比较图像、视频或其他数据的特征或嵌入。根据具体任务和数据属性,可以使用不同类型的距离度量。下面介绍了深度学习和计算机视觉中使用的一些常见类型的距离度量。 余弦相似性距…

后端使用aop和redis实现防抖

在公司的代码开发中遇到了,快速点击就会重复提交的bug,刚好最近在优化代码于是想着在后端实现防抖 1.整体思路 2.创建防抖的注解 防抖我一般放在存储方法上,在程序运行时使用,于是我的防抖注解是method类型 Target(ElementType.METHOD) Ret…

【Zookeeper专题】Zookeeper选举Leader源码解析

目录 前言阅读建议课程内容一、ZK Leader选举流程回顾二、源码流程图三、Leader选举模型图 学习总结 前言 为什么要看源码?当然是装逼啊!哈哈 说实在博主之前看Spring源码之前没细想过这个问题,只是听大佬们说【Spring是一个非常优秀的源码】…

15. Java反射和注解

Java —— 反射和注解 1. 反射2. 注解 1. 反射 动态语言:变量的类型和属性可以在运行时动态确定,而不需要在编译时指定 常见动态语言:Python,JavaScript,Ruby,PHP,Perl;常见静态语言…

【云备份项目】

文章目录 [TOC](文章目录) 一、项目框架定义1.项目需求2. 服务端框架搭建思想3.客户端框架搭建思想 二、环境搭建1.gcc编译器升级2.安装第三方库 三、认识第三方库1.json库使用2.bundle库使用3.httplib库使用4.简单服务器搭建5.简单客户端搭建 四、文件使用工具类设计1.类的功能…

3. Windows下C++/MFC调用hiredis库操作redis示例

一、头文件目录 将之前下载和编译好的Redis目录拷贝到新建好的工程目录下面,再点击测试工程的右键/属性,点击C/常规,附加包含目录添加以下路径,注意如果原先有多个路径,在末尾处添加分号后再粘贴: 点击C/常…

树莓派玩转openwrt软路由:11.OpenWrt安装NodeRed

1、更新软件源 opkg update2、安装nodered docker run -it -p 1880:1880 --name mynodered nodered/node-red3、安装完整性测试 实现一个打印hello world的demo,每隔1秒打印一次

css 星星闪烁加载框

今天带来的是普灵普灵的loader闪烁加载框 效果如下 开源精神给我们带来了源码 ,源码如下 <!DOCTYPE html> <html lang"en"><head><meta charset"UTF-8"><meta name"viewport" content"widthdevice-width, in…

Spring framework Day10:JSR330注入注解

前言 JSR330是Java社区标准化进程&#xff08;Java Community Process&#xff0c;简称JCP&#xff09;中的一个规范&#xff0c;全名为"Dependency Injection for Java"&#xff0c;即Java的依赖注入规范。它定义了一组注解和相关的规范&#xff0c;用于实现依赖注…

python每日一练(7)

&#x1f308;write in front&#x1f308; &#x1f9f8;大家好&#xff0c;我是Aileen&#x1f9f8;.希望你看完之后&#xff0c;能对你有所帮助&#xff0c;不足请指正&#xff01;共同学习交流. &#x1f194;本文由Aileen_0v0&#x1f9f8; 原创 CSDN首发&#x1f412; 如…

Visual Studio 错误CS0006:未能找到元数据文件踩坑记录

前言 在写项目的时候&#xff0c;添加了个新的Nuget包&#xff0c;突然就不行&#xff0c;然后就是报错&#xff0c;找不到文件、 出现的原因是因为项目之间互相引用出现了问题&#xff0c;比如如下情况 先版本回退 如果有Git仓库 第一时间去看Git 文件比较&#xff0c;找到…

YOLOv5算法改进(11)— 主干网络介绍(MobileNetV3、ShuffleNetV2和GhostNet)

前言:Hello大家好,我是小哥谈。主干网络通常指的是深度学习中的主干模型,通常由多个卷积层和池化层组成,用于提取输入数据的特征。在训练过程中,主干网络的参数会被不断优化以提高模型的准确性。YOLOv5算法中的主干网络可以有多种替换方案,为了后面讲解的方便,本篇文章就…

自动驾驶学习笔记(三)——场景设计

#Apollo开发者# 学习课程的传送门如下&#xff0c;当您也准备学习自动驾驶时&#xff0c;可以和我一同前往&#xff1a; 《自动驾驶新人之旅》免费课程—> 传送门 《2023星火培训【感知专项营】》免费课程—>传送门 文章目录 前言 场景设计平台 场景地图 场景基本…