【经典算法】LeetCode 108. 将有序数组转换为二叉搜索树(Java/C/Python3/Go实现含注释说明,Easy)

目录

  • 题目描述
  • 思路及实现
    • 方式一:递归中值法
      • 思路
      • 代码实现
        • Java版本
        • C语言版本
        • Python3版本
        • Golang版本
      • 复杂度分析
    • 方式二:迭代法
      • 思路
      • 代码实现
        • Java实现
        • Python实现
        • C++实现
        • Go版本
      • 复杂度分析
      • 总结
  • 总结
  • 相似题目

  • 标签(题目类型):树,二叉搜索树,数组

题目描述

给定一个有序整数数组,将其转换为一棵高度平衡的二叉搜索树。

原题:LeetCode 108. 将有序数组转换为二叉搜索树

思路及实现

方式一:递归中值法

思路

我们可以使用递归的方法来构建二叉搜索树。每次递归,我们选择数组的中间元素作为根节点,然后将数组分为左右两部分,分别构建左子树和右子树。

代码实现

Java版本
class TreeNode {int val;TreeNode left;TreeNode right;TreeNode(int x) {val = x;}
}public class Solution {public TreeNode sortedArrayToBST(int[] nums) {if (nums == null || nums.length == 0) {return null;}return buildBST(nums, 0, nums.length - 1);}private TreeNode buildBST(int[] nums, int start, int end) {if (start > end) {return null;}int mid = (start + end) / 2;TreeNode root = new TreeNode(nums[mid]);root.left = buildBST(nums, start, mid - 1);root.right = buildBST(nums, mid + 1, end);return root;}
}

说明:

  • TreeNode 是树节点的定义。
  • sortedArrayToBST 方法是入口点,首先检查输入数组的有效性,然后调用 buildBST 方法进行递归构建。
  • buildBST 方法是递归构建二叉搜索树的核心。它首先找到中间索引 mid,然后创建根节点,并递归地为左半部分和右半部分构建子树。
C语言版本
#include <stdlib.h>struct TreeNode {int val;struct TreeNode *left;struct TreeNode *right;
};struct TreeNode* buildBST(int* nums, int start, int end) {if (start > end) {return NULL;}int mid = (start + end) / 2;struct TreeNode* root = (struct TreeNode*)malloc(sizeof(struct TreeNode));root->val = nums[mid];root->left = buildBST(nums, start, mid - 1);root->right = buildBST(nums, mid + 1, end);return root;
}struct TreeNode* sortedArrayToBST(int* nums, int numsSize){if (nums == NULL || numsSize == 0) {return NULL;}return buildBST(nums, 0, numsSize - 1);
}

说明:

  • 这里使用 C 语言标准库中的 malloc 来动态分配内存。
  • 其余逻辑与 Java 版本类似。
Python3版本
class TreeNode:def __init__(self, val=0, left=None, right=None):self.val = valself.left = leftself.right = rightclass Solution:def sortedArrayToBST(self, nums: List[int]) -> TreeNode:if not nums:return Nonedef buildBST(start, end):if start > end:return Nonemid = (start + end) // 2root = TreeNode(nums[mid])root.left = buildBST(start, mid - 1)root.right = buildBST(mid + 1, end)return rootreturn buildBST(0, len(nums) - 1)

说明:

  • 使用 Python 类来定义树节点。
  • sortedArrayToBST 方法是入口点,它定义了一个内部递归函数 buildBST 来构建二叉搜索树。
Golang版本
package maintype TreeNode struct {Val   intLeft  *TreeNodeRight*TreeNode
}func sortedArrayToBST(nums []int) *TreeNode {if len(nums) == 0 {return nil}return buildBST(nums, 0, len(nums)-1)
}func buildBST(nums []int, start, end int) *TreeNode {if start > end {return nil}mid := (start + end) / 2root := &TreeNode{Val: nums[mid]}root.Left = buildBST(nums, start, mid-1)root.Right = buildBST(nums, mid+1, end)return root
}func main() {// 示例用法nums := []int{-10, -3, 0, 5, 9}root := sortedArrayToBST(nums)// 这里可以添加遍历树的代码来验证结果
}

说明:

  • 使用 Go 语言的结构体来定义树节点。
  • sortedArrayToBST 方法是入口点,调用 buildBST 方法来构建二叉搜索树。
  • buildBST 方法是递归函数,用于构建二叉搜索树。

复杂度分析

  • 时间复杂度:O(n),其中 n 是数组的长度。每个元素只被访问和处理一次。
  • 空间复杂度:O(log n),平均情况下。递归调用栈的深度取决于树的高度,而平衡二叉搜索树的高度是 log n(以 2 为底)。在最坏的情况下(当输入数组已经排序并且完全不平衡时,例如数组中没有重复元素且递增或递减),空间复杂度会退化到 O(n)。但由于题目要求转换为高度平衡的二叉搜索树,因此平均情况下是 O(log n)。

方式二:迭代法

思路

  1. 初始化一个空栈,并确定数组的中点作为根节点。
  2. 将根节点压入栈中,并标记为已处理(如果需要的话)。
  3. 创建一个循环,直到栈为空:
    • 弹出栈顶节点,并处理其左子树(如果存在):
      • 找到左子树的起始索引和结束索引。
      • 计算左子树的中点,并创建新的左子节点。
      • 将左子节点设置为当前弹出节点的左孩子。
      • 如果左子树非空,将左子节点压入栈中。
    • 处理当前节点的右子树(如果存在):
      • 找到右子树的起始索引和结束索引。
      • 计算右子树的中点,并创建新的右子节点。
      • 将右子节点设置为当前弹出节点的右孩子。
      • 如果右子树非空,将右子节点压入栈中。
  4. 当栈为空时,表示所有的节点都已经处理完毕,BST构建完成。

代码实现

迭代法构建二叉搜索树(BST)通常不如递归方法直观,但可以通过维护一个栈来模拟递归过程。以下是迭代法构建BST的思路以及使用Python、Java、C++和JavaScript四种语言的实现,包括注释和代码说明。

Java实现
class TreeNode {int val;TreeNode left;TreeNode right;TreeNode(int val) {this.val = val;}
}public class Solution {public TreeNode sortedArrayToBST(int[] nums) {if (nums == null || nums.length == 0) return null;Deque<Object[]> stack = new ArrayDeque<>();int start = 0, end = nums.length - 1;TreeNode root = new TreeNode(nums[(start + end) / 2]);stack.push(new Object[]{root, start, end});while (!stack.isEmpty()) {Object[] curr = stack.poll();TreeNode node = (TreeNode) curr[0];int s = (int) curr[1], e = (int) curr[2];int mid = s + (e - s) / 2;if (s < mid) {TreeNode leftChild = new TreeNode(nums[mid - 1]);node.left = leftChild;stack.push(new Object[]{leftChild, s, mid - 1});}if (mid + 1 < e) {TreeNode rightChild = new TreeNode(nums[mid + 1]);node.right = rightChild;stack.push(new Object[]{rightChild, mid + 1, e});}}return root;}
}
Python实现
class TreeNode:def __init__(self, val=0, left=None, right=None):self.val = valself.left = leftself.right = rightdef sortedArrayToBST(nums):if not nums:return Nonestack, start, end = [], 0, len(nums) - 1root = TreeNode(nums[start + (end - start) // 2])stack.append((root, start, end))while stack:node, start, end = stack.pop()mid = (start + end) // 2if start < mid:left_child = TreeNode(nums[mid - 1])  # 左子树的根(实际上是mid-1,因为mid是根)node.left = left_childstack.append((left_child, start, mid - 1))if mid + 1 < end:right_child = TreeNode(nums[mid + 1])  # 右子树的根(实际上是mid+1)node.right = right_childstack.append((right_child, mid + 1, end))return root# 示例
nums = [-10, -3, 0, 5, 9]
root = sortedArrayToBST(nums)
# 遍历BST的代码略
C++实现
#include <stack>
#include <vector>struct TreeNode {int val;TreeNode *left;TreeNode *right;TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
};TreeNode* sortedArrayToBST(std::vector<int>& nums) {if (nums.empty()) {return nullptr;}std::stack<std::pair<TreeNode*, std::pair<int, int>>> stk;int n = nums.size();TreeNode* root = new TreeNode(nums[n / 2]);stk.push({root, {0, n - 1}});while (!stk.empty()) {auto curr = stk.top();stk.pop();TreeNode* node = curr.first;int start = curr.second.first;int end = curr.second.second;int mid = start + (end - start) / 2;if (start < mid) {node->left = new TreeNode(nums[mid - 1]); // 左子树的根节点stk.push({node->left, {start, mid - 1}});}if (mid + 1 < end) {node->right = new TreeNode(nums[mid + 1]); // 右子树的根节点stk.push({node->right, {mid + 1, end}});}}return root;
}// 示例用法
int main() {std::vector<int> nums = {-10, -3, 0, 5, 9};TreeNode* root = sortedArrayToBST(nums);// 遍历BST的代码略return 0;
}
Go版本

当然,以下是Go语言版本的实现,其中包含了代码注释和说明:

package mainimport ("fmt"
)// TreeNode represents a node in a binary tree
type TreeNode struct {Val   intLeft  *TreeNodeRight *TreeNode
}// sortedArrayToBST converts a sorted array to a balanced binary search tree
func sortedArrayToBST(nums []int) *TreeNode {if len(nums) == 0 {return nil}// 定义辅助函数来构建BSTvar helper func(start, end int) *TreeNodehelper = func(start, end int) *TreeNode {if start > end {return nil}// 找到中间元素作为当前子树的根mid := (start + end) / 2root := &TreeNode{Val: nums[mid]}// 递归地构建左子树和右子树root.Left = helper(start, mid-1)root.Right = helper(mid+1, end)return root}// 调用辅助函数从整个数组构建BSTreturn helper(0, len(nums)-1)
}// inorderTraversal performs in-order traversal of a binary tree
func inorderTraversal(root *TreeNode) {if root == nil {return}inorderTraversal(root.Left)fmt.Println(root.Val)inorderTraversal(root.Right)
}func main() {nums := []int{-10, -3, 0, 5, 9}root := sortedArrayToBST(nums)// 验证BST是否构建正确,进行中序遍历inorderTraversal(root)
}

代码说明:

  1. TreeNode 结构体定义了一个二叉树的节点,包含一个整数值Val和两个指向左右子节点的指针LeftRight

  2. sortedArrayToBST 函数接受一个排序数组nums,并返回一个指向平衡二叉搜索树根的指针。该函数定义了一个内部辅助函数helper,它递归地构建BST。

  3. helper函数中,我们首先检查start是否大于end,如果是,则返回nil表示没有节点可构建。然后,我们找到中间元素mid作为当前子树的根,并创建对应的TreeNode。接下来,我们递归地调用helper来构建左子树和右子树,并将返回的根节点分别赋值给当前节点的LeftRight

  4. inorderTraversal 函数用于验证BST是否构建正确。它使用中序遍历的方式遍历BST,并打印出每个节点的值。由于BST的性质,中序遍历将得到一个升序的序列。

  5. main函数中,我们创建了一个排序数组nums,并调用sortedArrayToBST来构建BST。然后,我们调用inorderTraversal来遍历BST并打印其节点值。

复杂度分析

对于上述四种语言的实现,它们的复杂度分析是相同的:

  • 时间复杂度:O(n),其中n是数组的长度。每个元素只被访问和处理一次。
  • 空间复杂度:O(log n),平均情况下。这是因为栈的深度取决于BST的高度,对于平衡BST来说,其高度是O(log n)。然而,在最坏的情况下(当输入数组已经排序并且完全不平衡时),空间复杂度会退化到O(n)。但在这个问题中,我们总是从数组的中点开始构建BST,因此通常能得到一个相对平衡的BST,使得空间复杂度保持在O(log n)的平均水平。

总结

总结

以下是对于两种构建平衡二叉搜索树(BST)的方式的总结,包括它们的优点、缺点、时间复杂度和空间复杂度,以及其他特点:

方式优点缺点时间复杂度空间复杂度其他
递归中值法1. 代码简洁,逻辑清晰。1. 在最坏情况下,递归调用栈可能会占用额外的 O(n) 空间。O(n)平均 O(log n),最坏 O(n)1. 易于理解和实现。
迭代栈方法1. 避免了递归调用栈可能导致的空间问题。1. 相比递归方法,代码可能略显复杂。O(n)平均 O(log n),最坏 O(n)1. 更节省空间,适合处理大数据集。

说明

  • 递归中值法:该方法直接通过递归找到数组的中值作为根节点,然后递归地构建左子树和右子树。这种方法逻辑清晰,易于理解,但在最坏情况下(如数组已排序),递归调用栈可能会占用大量的空间。
  • 迭代栈方法:该方法使用栈来模拟递归过程,避免了递归调用栈的空间占用问题。但相比递归方法,代码可能略显复杂。在处理大数据集时,迭代栈方法通常更加节省空间。
  • 时间复杂度:对于两种方法,时间复杂度都是 O(n),因为每个元素都需要被处理一次。
  • 空间复杂度:对于递归中值法,空间复杂度取决于递归调用栈的深度,平均情况下为 O(log n),但在最坏情况下(如数组已排序)为 O(n)。对于迭代栈方法,空间复杂度同样是平均 O(log n),最坏 O(n),因为栈可能需要存储所有节点。但在实际应用中,迭代栈方法通常更加节省空间。
  • 其他:两种方法各有特点,递归中值法更易于理解和实现,而迭代栈方法更节省空间。在选择使用哪种方法时,需要根据具体的应用场景和需求来权衡。

相似题目

相似题目难度链接
leetcode 107. 二叉树的层次遍历 II中等力扣-107
leetcode 109. 有序链表转换二叉搜索树中等力扣-109
leetcode 110. 平衡二叉树简单力扣-110

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

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

相关文章

2024年五一数学建模C题完整解题思路代码

2024年第二十一届五一数学建模竞赛题目 C题 煤矿深部开采冲击地压危险预测 煤炭是中国的主要能源和重要的工业原料。然而&#xff0c;随着开采深度的增加&#xff0c;地应力增大&#xff0c;井下煤岩动力灾害风险越来越大&#xff0c;严重影响着煤矿的安全高效开采。在各类深…

路由器的构成

一、路由器简介 路由器是互联网中的关键设备&#xff1a; 连接不同的网络路由器是多个输入端口和多个输出端口的专用计算机&#xff0c;其任务是转发分组&#xff08;转发给下一跳路由器&#xff09;下一跳路由器也按照这种方法处理分组&#xff0c;直到该分组到达终点为止 …

Python项目开发实战:如何基于Keras的深度学习来预测国际旅行人数

注意:本文的下载教程,与以下文章的思路有相同点,也有不同点,最终目标只是让读者从多维度去熟练掌握本知识点。 下载教程:深度学习-基于Keras的Python项目开发实战_国际旅行人数预测_编程案例实例教程.pdf 在预测国际旅行人数这一问题上,我们可以利用深度学习技术,尤其是…

C++类和对象(下)(2)

一、友元 友元提供了一种突破封装的方式&#xff0c;有时提供了方便&#xff0c;但是友元会增加耦合度&#xff0c;破坏了封装&#xff0c;所以不建议经常使用友元。 友元分为&#xff1a; 友元函数和友元类。 1.1 友元函数 我们在尝试去重载operator<<时发现无法将op…

Pandas入门篇(二)-------Dataframe篇4(进阶)(Dataframe的进阶用法)(机器学习前置技术栈)

目录 概述一、复合索引&#xff08;一&#xff09;创建具有复合索引的 DataFrame1. 使用 set_index 方法&#xff1a;2.在创建 DataFrame 时直接指定索引&#xff1a; &#xff08;二&#xff09;使用复合索引进行数据选择和切片&#xff08;三&#xff09;重置索引&#xff08…

使用 Langchain、Langfuse、Nemo-gaurdrails、RAGAs构建 RAG 管道并进行监控和评估

原文地址:build-end-to-end-rag-pipeline-with-monitoring-and-evaluation-using-langchain-azure-ai-search 2024 年 4 月 21 日 介绍 使用现代的LLM框架,如Langchain或llamaindex,可以迅速搭建一个用于 RAG 的管道,通常只需编写大约5-6行代码。然而,若要构建一个适用于生…

【小浩算法 BST与其验证】

BST与其验证 前言我的思路思路一 中序遍历判断数组无重复递增思路二 递归边界最大值最小值的传递 我的代码测试用例1测试用例2 前言 BST是二叉树一个经典应用&#xff0c;我们常常将其用于数据的查找以及构建平衡二叉树等。今天我所做的题目是验证一颗二叉树是否为二叉搜索树&…

垃圾的flinkcdc

在 MySQL 中&#xff0c;创建表时使用反引号 将表名或字段名括起来的作用是&#xff1a; 保留字和关键字: 使用反引号可以避免使用MySQL的保留字和关键字作为表名或字段名时产生的冲突。比如&#xff0c;你可以创建一个名为 select 或 order 的表&#xff1a; sqlCopy Code C…

MATLAB实现果蝇算法优化BP神经网络预测分类(FOA-BP)

果蝇算法&#xff08;Fruit Fly Optimization Algorithm, FFOA&#xff09;是一种启发式优化算法&#xff0c;受果蝇觅食行为的启发。将其应用于优化BP神经网络&#xff0c;主要是为了寻找BP神经网络中的最佳权重和偏置值。以下是一个基本的流程&#xff1a; 初始化&#xff1a…

上班族小张的副业之路:下班后的水牛社赚钱故事

在快节奏的都市生活中&#xff0c;上班族小张每天忙碌于办公室与家庭之间&#xff0c;重复着朝九晚五的生活。然而&#xff0c;他内心总渴望寻找一种既能充实生活&#xff0c;又能增加收入的副业方式。直到有一天&#xff0c;他发现了水牛社——一个为他提供丰富副业资源和机会…

信息时代的智慧导航:高效搜索、信息筛选与信任构建的全面指南!

文章目录 一、高效搜索&#xff1a;快速定位目标信息的秘诀二、信息筛选&#xff1a;去伪存真&#xff0c;找到有价值的信息三、信任构建&#xff1a;深入了解与直接沟通《搜索之道&#xff1a;信息素养与终身学习的新引擎》亮点内容简介目录获取方式 随着科技的飞速发展&#…

jenkins汉化不完全问题解决

jenkins安装完Localization:Chinese(Simplified)中文语言包后&#xff0c;发现是出现汉化不完全或者部分汉化的情况&#xff0c;如下图&#xff1a; 解决方法&#xff1a; 启动命令中指定语言 -Duser.languageen_US.UTF-8 或者 -Duser.languageC.UTF-8原因分析&#xff1a;安…

网上招聘系统的设计与实现参考论文(论文 + 源码)

【免费】网上招聘系统的设计与实现.zip资源-CSDN文库https://download.csdn.net/download/JW_559/89251636 网上招聘系统的设计与实现 摘 要 随着时代的发展&#xff0c;中国的互联网技术愈加成熟&#xff0c;已经有越来越多的社会群体开始学会使用互联网技术&#xff0c;整个…

STM32定时器的OC比较和PWM

系列文章目录 STM32单片机系列专栏 C语言术语和结构总结专栏 文章目录 1. 输出比较(OC) 2. PWM 3. PWM的输出 3.1 高级定时器 3.2 通用定时器 4. PWM的输出结构 5. 代码示例 5.1 PWM.c 5.2 PWM.h 5.3 main.c 这篇文章解释了TIM定时器的内部时钟和外部时钟的使用&a…

头歌:Spark的安装与使用

第1关&#xff1a;Scala语言开发环境的部署 相关知识 Scala是一种函数式面向对象语言&#xff0c;它融汇了许多前所未有的特性&#xff0c;而同时又运行于JVM之上。随着开发者对Scala的兴趣日增&#xff0c;以及越来越多的工具支持&#xff0c;无疑Scala语言将成为你手上一件…

SpringBoot 循环引用解决办法

Spring 原生是允许并解决了单层循环引用的&#xff0c;但从 Spring Boot 2.6.0 开始&#xff0c;默认禁止 Bean 之间的循环引用&#xff0c;如果存在循环引用就会启动失败报错。 此时要解决循环引用问题有两个办法&#xff1a; 设置Spring允许循环引用&#xff08;推荐&#x…

《LeetCode力扣练习》代码随想录——贪心算法(买卖股票的最佳时机 II---Java)

《LeetCode力扣练习》代码随想录——贪心算法&#xff08;买卖股票的最佳时机 II—Java&#xff09; 刷题思路来源于 代码随想录 122. 买卖股票的最佳时机 II 贪心 class Solution {public int maxProfit(int[] prices) {int result 0;for (int i 1; i < prices.length; i…

<Linux> 常用基础指令

目录 Linux基础指令ls指令pwd指令cd指令touch指令mkdir指令rm指令cp指令mv指令cat指令tac指令grep指令echo指令zip/unzip指令uname指令 Linux基础指令 ls指令 功能&#xff1a;列出本目录下的文件及子目录 常用的选项&#xff1a; -a 列出所有文件&#xff0c;包括以.开头的…

漫谈:C语言 奇葩的指针定义规则

初级代码游戏的专栏介绍与文章目录-CSDN博客 我的github&#xff1a;codetoys&#xff0c;所有代码都将会位于ctfc库中。已经放入库中我会指出在库中的位置。 这些代码大部分以Linux为目标但部分代码是纯C的&#xff0c;可以在任何平台上使用。 C语言的语法很麻拐。 初学者的…

YOLOv9/YOLOv8算法改进【NO.128】 使用ICCV2023超轻量级且高效的动态上采样器( DySample)改进yolov8中的上采样

前 言 YOLO算法改进系列出到这&#xff0c;很多朋友问改进如何选择是最佳的&#xff0c;下面我就根据个人多年的写作发文章以及指导发文章的经验来看&#xff0c;按照优先顺序进行排序讲解YOLO算法改进方法的顺序选择。具体有需求的同学可以私信我沟通&#xff1a; 首推…