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

题目

中等

给定一个单链表的头节点  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),即为递归过程中栈的最大深度,也就是需要的空间。

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

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

相关文章

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

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

一款开源高性能AI应用框架

前言 LobeChat 是一个基于 Next.js 框架构建的 AI 会话应用&#xff0c;旨在提供一个 AI 生产力平台&#xff0c;使用户能够与 AI 进行自然语言交互。 LobeChat应用架构 LobeChat 的整体架构由前端、EdgeRuntime API、Agents 市场、插件市场和独立插件组成。这些组件相互协作&a…

css实现上下左右对勾选中状态角标

&#x1f365;左上角 &#x1f365;右上角 &#x1f365;左下角 &#x1f365;右下角: &#x1f365;左上角: .blueBackground {position: relative;border: 1px solid #91c7f3;background: #F0F8FF !important;&:after {content: "";position: absolute;top:…

7 人赚 960 亿美元,数字天才的首次独舞

巴菲特股东大会 一年一度的巴菲特股东大会如常召开&#xff0c;只不过这次坐在老爷子左手边的不再是老搭档查理芒格&#xff0c;而是钦点的未来继任者&#xff0c;格雷格阿贝尔。 随着芒格&#xff08;99岁&#xff09;的离开&#xff0c;巴菲特&#xff08;93岁&#xff09;也…

突破销量瓶颈:亚马逊,速卖通,国际站销量提升实战技巧

1、精心选品&#xff1a;选品是亚马逊销售的第一步&#xff0c;也是至关重要的一步。卖家应该进行市场调研&#xff0c;了解消费者的需求和喜好&#xff0c;选择有市场潜力的产品。要注意产品的差异化&#xff0c;避免与竞争对手的产品过于相似。 2、优化产品详情页&#xff1…

【SpringMVC 】什么是SpringMVC(二)?如何整合ssm框架以及使用mybatisPlus?

文章目录 SpringMVC第三章1、ssm整合1、基本步骤1-3步4-5步6步7步8-12步13步14-15步2、添加数据3、删除数据4、配置事务5、修改数据2、pageHelpe分页1、基本步骤第四章1、mybatisPlus1、基本步骤1-45-7892、基本方法的使用查询2、新ssm项目1、基本步骤1-5678-910-111213-15Spri…

✌粤嵌—2024/4/29—轮转数组

代码实现&#xff1a; // 逆置数组 void nizhi_array(int *nums, int l, int r) { // 左闭右闭if (l > r) {return;}int i l, j r;while (i < j) {int temp nums[i];nums[i] nums[j];nums[j] temp;i;j--;} }void rotate(int *nums, int numsSize, int k) {if (k >…

CSAPP | Chapter 1 | 计算机系统漫游

CSAPP | Chapter 1 | 计算机系统漫游 计算机系统由系统软件与硬件组成。 对于一个简单的 C 程序 hello.c 来说&#xff0c;即便它非常简单&#xff0c;但是为了让它运行&#xff0c;系统的每个主要组成部分都需要协调工作。 #include <stdio.h>int main() {printf(&quo…

AI适老化!10秒一张的AI姓氏头像,居然要卖9块9?中老年用户都说好!

看短视频的你&#xff0c;一定会刷到过这样的直播间&#xff1a; 现在大家明白了&#xff0c;这是一个做姓氏图像的直播间。我刚开始刷到的时候也觉得这种头像好看&#xff0c;高大上&#xff0c;也想做一个这样的图像&#xff0c;来当自己的微信头像。 做这样的图像需要排队刷…

迅饶科技 X2Modbus 网关 AddUser 任意用户添加漏洞复现

0x01 产品简介 X2Modbus是上海迅饶自动化科技有限公司Q开发的一款功能很强大的协议转换网关, 这里的X代表各家不同的通信协议, 2是T0的谐音表示转换, Modbus就是最终支持的标准协议是Modbus协议。用户可以根据现场设备的通信协议进行配置,转成标准的Modbus协议。在PC端仿真…

代码随想录算法训练营DAY43|C++动态规划Part5|1049.最后一块石头的重量II、494.目标和、474.一和零

文章目录 1049.最后一块石头的重量II思路CPP代码 ⭐️494.目标和回溯算法抽象成01背包问题CPP代码本题总结 474.一和零思路CPP代码 1049.最后一块石头的重量II 力扣题目链接 文章链接&#xff1a;1049.最后一块石头的重量II 视频链接&#xff1a;这个背包最多能装多少&#xff…

高中数学-三角函数之常见题型总结

相关公式 新教材&#xff0c;取消了和差化积与积化和差的三角函数题目 例题1 解析 根据条件tanθ -2&#xff0c;我们应该就要想到&#xff0c;把待求式的角向θ靠拢 所以要想到如何降角&#xff0c;将2θ化成θ&#xff0c;那么&#xff0c;想到的公式就是&#xff1a;正弦…

【C++第三阶段】Set Map容器 员工分组案例

以下内容仅为当前认识&#xff0c;可能有不足之处&#xff0c;欢迎讨论&#xff01; 文章目录 Set容器构造和赋值大小和交换插入和删除一次性迭代器&#xff08;可能迅速失效的迭代器&#xff09;长久保留的迭代器如何判断迭代器是否一次性 查找和统计set和multiset的区别pari对…

【notes2】并发,IO,内存

文章目录 1.线程/协程/异步&#xff1a;并发对应硬件资源是cpu&#xff0c;线程是操作系统如何利用cpu资源的一种抽象2.并发&#xff1a;cpu&#xff0c;线程2.1 可见性&#xff1a;volatile2.2 原子性&#xff08;读写原子&#xff09;&#xff1a;AtomicInteger/synchronized…

239 基于matlab的EKF(扩展卡尔曼滤波)_UKF(无迹卡尔曼滤波)_PF(粒子滤波)三种算法的估计结果比较

基于matlab的EKF(扩展卡尔曼滤波)_UKF(无迹卡尔曼滤波)_PF&#xff08;粒子滤波&#xff09;三种算法的估计结果比较&#xff0c;输出估计误差&#xff0c;并单独对粒子滤波进行估计及其置信区间可视化。程序已调通&#xff0c;可直接运行。 239 EKF(扩展卡尔曼滤波) - 小红书 …

Unity | Shader基础知识(第十三集:编写内置着色器阶段总结和表面着色器的补充介绍)

目录 前言 一、表面着色器的补充介绍 二、案例viewDir详解 1.viewDir是什么 2.viewDir的作用 3.使用viewDir写shader 前言 注意观察的小伙伴会发现&#xff0c;这组教程前半部分我们在编写着色器的时候&#xff0c;用的是顶点着色器和片元着色器的组合。 SubShader{CGPRO…

Java转Kotlin

Kotlin 是一种静态编程语言 2011JetBrains开始开发Kotlin&#xff0c;用于多平台应用&#xff08;能脱离虚拟机&#xff0c;直接编译成可以在win,mac,linux运行的二进制代码&#xff09; 2017获得谷歌官方支持 语法简洁&#xff08;减少了大量的样板代码&#xff0c;语法糖&…

【Python深度学习(第二版)(2)】深度学习之前:机器学习简史

文章目录 一. 深度学习的起源1. 概率建模--机器学习分类器2. 早期神经网络--反向传播算法的转折3. 核方法 -- 忽略神经网络4. 决策树、随机森林和梯度提升机5. 神经网络替代svm与决策树 二. 深度学习与机器学习有何不同 可以这样说&#xff0c;当前工业界所使用的大部分机器学习…

服务器端口怎么查,服务器端口查看方法详解

服务器端口是网络通信的关键组件&#xff0c;对于网络管理员和系统管理员来说&#xff0c;了解和掌握如何查看服务器端口是非常重要的。接下来介绍两种常用的方法来查看服务器端口。 方法一&#xff1a;使用命令提示符&#xff08;CMD&#xff09; 1. 首先&#xff0c;点击电脑…

JavaScript百炼成仙自学笔记——12

函数七重关之五&#xff08;自执行函数&#xff09; 什么时候用它&#xff1f; 很多时候&#xff0c;我们只想执行一个函数&#xff0c;却无所谓这个函数叫什么名字。那么这种情况下就可以考虑使用自执行函数。 {function(){console.log(123);} }(); 这就是一个简单的自执行的…