力扣每日一题109:有序链表转换二叉搜索树

news2025/1/10 11:18:25

题目

中等

给定一个单链表的头节点  head ,其中的元素 按升序排序 ,将其转换为 

平衡

 二叉搜索树。

示例 1:

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

示例 2:

输入: head = []
输出: []

提示:

  • head 中的节点数在[0, 2 * 104] 范围内
  • -105 <= Node.val <= 105

面试中遇到过这道题?

1/5

通过次数

161.6K

提交次数

211K

通过率

76.6%

思路

和有序数组转换为二叉搜索树的的思路一样,都是以中间值为根,然后递归建立左右子树。区别就是:如果是数组的话,直接用下标就能找到中间元素,如果是有序链表的话,用快慢指针寻找中间元素、或是知道链表长度情况下,根据遍历次数寻找中间元素。

链表和树的结点结构

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     ListNode *next;
 *     ListNode() : val(0), next(nullptr) {}
 *     ListNode(int x) : val(x), next(nullptr) {}
 *     ListNode(int x, ListNode *next) : val(x), next(next) {}
 * };
 */
/**
 * Definition for a binary tree node.
 * 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 *build(ListNode *head,int lo,int hi)
    {
        if(lo>hi) return NULL;
        //找中间位置
        int mid=(lo+hi)/2;
        ListNode *middle=head;
        for(int i=0;i<mid-lo;i++)
        {
            middle=middle->next;
        }
        //建根,递归建立左右子树
        TreeNode *root=new TreeNode(middle->val);
        root->left=build(head,lo,mid-1);
        root->right=build(middle->next,mid+1,hi);
        return root;
    }
    TreeNode* sortedListToBST(ListNode* head) {
        int n=0;
        ListNode *p=head;
        while(p)
        {
            n++;
            p=p->next;
        }
        return build(head,0,n-1);
    }
};

方法二:快慢指针寻找中间元素

使用快慢指针寻找中间元素是链表题目的基操。原理就是,设置一个快指针fast和一个慢指针slow,快指针的速度是慢指针的两倍,当快指针走到最后的时候,慢指针就到了中间位置。

class Solution {
public:
    ListNode* getMedian(ListNode* left, ListNode* right) {
        ListNode* fast = left;
        ListNode* slow = left;
        while (fast != right && fast->next != right) {
            fast = fast->next;
            fast = fast->next;
            slow = slow->next;
        }
        return slow;
    }

    TreeNode* buildTree(ListNode* left, ListNode* right) {
        if (left == right) {
            return nullptr;
        }
        ListNode* mid = getMedian(left, right);
        TreeNode* root = new TreeNode(mid->val);
        root->left = buildTree(left, mid);
        root->right = buildTree(mid->next, right);
        return root;
    }

    TreeNode* sortedListToBST(ListNode* head) {
        return buildTree(head, nullptr);
    }
};

方法三:分治+中序遍历优化

上面的两种方法都是先找到中间节点,再递归建立左右子树,属于先序遍历。这样,每次寻找中间节点时,就要logn的时间复杂度,总的时间复杂度变成了O(nlogn)。

如果我们可以用中序遍历,先建立左子树,左子树建完再建根,然后再建右子树,那么就省去了查找中间节点的时间,时间复杂度就变成了O(n)。

也就是说,我们没有必要“先”找到中间节点:我们可以先构建了左子树,建立结束后,指针自然指向中间结点。那么如何构建左子树呢?其实我们只需要确定子树的大小就可以。所以先用O(n)的时间计算链表长度,之后用中序遍历。当然,指针需要是“引用”,这样才能改变指针的指向,实现建好左子树后,指针自然指向中间结点。

下面是官方题解:

class Solution {
public:
    int getLength(ListNode* head) {
        int ret = 0;
        for (; head != nullptr; ++ret, head = head->next);
        return ret;
    }

    TreeNode* buildTree(ListNode*& head, int left, int right) {
        if(left>right) return NULL;
        int mid=(left+right)/2;
        TreeNode *root=new TreeNode();
        root->left=buildTree(head,left,mid-1);
        root->val=head->val;
        head=head->next;
        root->right=buildTree(head,mid+1,right);
        return root;
    }

    TreeNode* sortedListToBST(ListNode* head) {
        int length = getLength(head);
        return buildTree(head, 0, length - 1);
    }
};

时间复杂度:O(n),其中 n 是链表的长度。

设长度为 n 的链表构造二叉搜索树的时间为 T(n),递推式为 T(n)=2⋅T(n/2)+O(1),根据主定理,T(n)=O(n)。

空间复杂度:O(log⁡n),这里只计算除了返回答案之外的空间。平衡二叉树的高度为 O(log⁡n),即为递归过程中栈的最大深度,也就是需要的空间。

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

video标签,如何隐藏右下角三个点包含的功能?

// nodownload: 不要下载 // nofullscreen: 不要全屏 // noremoteplayback: 不要远程回放 // disablePictureInPicture: 不要画中画 <videocontrols disablePictureInPicture"true"controlslist"nodownload nofullscreen noremoteplayback" > </v…

Java基础教程 - 5 数组

更好的阅读体验&#xff1a;点这里 &#xff08; www.doubibiji.com &#xff09; 更好的阅读体验&#xff1a;点这里 &#xff08; www.doubibiji.com &#xff09; 更好的阅读体验&#xff1a;点这里 &#xff08; www.doubibiji.com &#xff09; 5 数组 前面我们保存数据…

Java毕设之学院党员管理系统的设计与实现

运行环境 环境说明: 开发语言:java 框架:springboot&#xff0c;vue JDK版本:JDK1.8 数据库:mysql5.7(推荐5.7&#xff0c;8.0也可以) 数据库工具:Navicat11 开发软件:idea/eclipse(推荐idea) Maven包:Maven3.3.9 系统实现 管理员功能实现 党员管理 管理员进入指定功能操作…

AI 不仅会画画,还能造车 | 最新快讯

本周的北京&#xff0c;正在上演一场深刻的变革。 汽车产业&#xff0c;这个曾经以工业制造为核心的行业&#xff0c;正迅速地被数字化浪潮所改变&#xff0c;汽车、电商、互联网、人工智能等领域的界限变得模糊。在这样的背景下&#xff0c;车企们纷纷开始打破传统&#xff0c…

【AI】深度学习框架的期望与现实 机器学习编译尚未兑现其早期的一些承诺……

深度学习框架的期望与现实 机器学习编译尚未兑现其早期的一些承诺…… 来自&#xff1a;Axelera AI 资深软件工程师 Matthew Barrett 原帖是linkedin帖子&#xff1a; https://linkedin.com/posts/matthew-barrett-a49929177_i-think-its-fair-to-say-that-ml-compilation-ac…

【driver2】设备读写,同步和互斥,ioctl,进程休眠,时间和延时,延缓

文章目录 1.实现设备读写&#xff1a;write函数中一个进程写没问题&#xff0c;两进程写&#xff1a;第一个进程运行到kzalloc时&#xff0c;第二个进程也执行了kzalloc&#xff0c;只第二个进程地址保存在c中&#xff0c;第一个进程分配内存空间地址丢失造成内存泄漏。第一个进…

【Three.js基础学习】14.Galaxy Generator

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 前言 课程知识点 1. 实现星际编辑器 2. 创建粒子 1000&#xff0c; 在随机位置 3. 创建材质 PointsMaterial 4. Points() 接收 5. 放到gui 中调试 但是会发现调整size 等 属…

4.任务创建和删除的API函数

一、简介 二、动态创建任务函数:xTaskCreate() 此函数用于使用动态的方式创建任务&#xff0c;任务的任务控制块以及任务的栈空间所需的内存&#xff0c;均由 FreeRTOS 从 FreeRTOS 管理的堆中分配&#xff0c;若使用此函数&#xff0c;需要在 FreeRTOSConfig.h 文件 中将宏 c…

5.6 qt day1

自由创建一个登录页面 #include "mywidget.h"MyWidget::MyWidget(QWidget *parent): QWidget(parent) {//设置窗口this->setWindowTitle("");//设置窗口图标this->setWindowIcon(QIcon("C:\\Users\\14123\\Desktop\\b3119313b07eca80842876219…

【go项目01_学习记录05】

学习记录 1 依赖管理 Go Modules1.1 弃用 $GOPATH1.2 Go Modules 日常使用1.2.1 初始化生成go.mod文件1.2.2 Go Proxy代理1.2.3 go.mod文件查看1.2.4 go.sum文件查看1.2.5 indirect 含义1.2.6 go mod tidy 命令1.2.7 清空 Go Modules 缓存1.2.8 下载依赖1.2.9 所有 Go Modules …

知到java笔记(4.1--继承的用法以及this和super的用法)

格式&#xff1a; 例子&#xff1a; get set获取父类的私有变量 private属性 this和super区别&#xff1a; this用法 super用法 例子

从固定到可变:利用Deformable Attention提升模型能力

1. 引言 本文将深入探讨注意力机制的内部细节&#xff0c;这是了解机器如何选择和处理信息的基础。但这还不是全部&#xff0c;我们还将探讨可变形注意力的创新理念&#xff0c;这是一种将适应性放在首位的动态方法。 闲话少说&#xff0c;我们直接开始吧&#xff01; 2. 注…

重学java 29.经典接口

光阴似箭&#xff0c;我好像跟不上 —— 24.5.6 一、java.lang.Comparable 我们知道基本数据类型的数据(除boolean类型外)需要比较大小的话&#xff0c;直接使用比较运算符即可&#xff0c;但是引用数据类型是不能直接使用比较运算符来比较大小的。那么&#xff0c;如何解决这个…

linux部署java1.8(jdk1.8)

两种方式&#xff1a; 方式一 1.输入查找命令&#xff1a; yum -y list java*2.输入安装命令&#xff1a; yum install -y java-1.8.0-openjdk.x86_643.测试是否已经安装&#xff1a; java -version方式二&#xff1a; 点击链接进入官网&#xff1a;https://www.oracle.com/…

【教程】极简Python接入免费语音识别API

转载请注明出处&#xff1a;小锋学长生活大爆炸[xfxuezhagn.cn] 如果本文帮助到了你&#xff0c;请不吝给个[点赞、收藏、关注]哦~ 安装库&#xff1a; pip install SpeechRecognition 使用方法&#xff1a; import speech_recognition as srr sr.Recognizer() harvard sr…

idea2023.2.5的控制台动态配置当前环境

一、idea2023.2.5的控制台动态配置当前环境 1.1、idea版本 1.2、配置方式 1.2.1、方式一 1.2.2、方式二 1.3、参考 https://blog.csdn.net/xiaoheihai666/article/details/127757658

使用idea编辑器回退git已经push的代码

直接上结果 选择想要回退的那次/多次提交历史, 右击, 选中 revert commit git自动产生一个Revert记录&#xff0c;然后我们会看到git自动将我第三次错误提交代码回退了&#xff0c;这个其实就相当于git帮我们手动回退了代码。 后续&#xff0c;只需要我们将本次改动push到远…

从零开始的软件测试学习之旅(五)web测试项目

这里写目录标题 功能型测试非功能性测试面试拓展项目与数据库关系 测试用例设计—基于TPshop前台下单流程 功能型测试 一.设计测试 a,需求分析 1.输入分析 分析项目中要求如:输入长度,类型要求,组成规则,是否为空,是否重复 2.交付分析 判断所有数据正确,有错误给出提示(优化…

锂电池SOH估计 | Matlab实现基于ALO-SVR模型的锂电池SOH估计

目录 预测效果基本介绍程序设计参考资料 预测效果 基本介绍 锂电池SOH估计 | Matlab实现基于ALO-SVR模型的锂电池SOH估计 蚁狮优化支持向量机锂电池健康状态SOH估计&#xff1b; 具体流程如下&#xff1b; 1、分析锂离子电池老化数据集&#xff0c;从中选取具有代表电池性能衰减…

2024第2届华东国际宠物用品展(苏州)

第2届华东宠物展&#xff08;苏州&#xff09;2024.06.28-30 苏州国际博览中心 中国宠业新锐品牌展 400海外展商 20000平方展出面积 3000知名品牌 60000优质观众 参展组委会咨询&#xff1a;深宠展邹春宇 关于展会 第2届华东国际宠物用品展览会(苏州)暨中国宠业新锐品牌…