华为OD机试真题——二维伞的雨滴效应(2025A卷:200分)Java/python/JavaScript/C/C++/GO最佳实现
2025 A卷 200分 题型
本专栏内全部题目均提供Java、python、JavaScript、C、C++、GO六种语言的最佳实现方式;
并且每种语言均涵盖详细的问题分析、解题思路、代码实现、代码详解、3个测试用例以及综合分析;
本文收录于专栏:《2025华为OD真题目录+全流程解析+备考攻略+经验分享》
华为OD机试真题《二维伞的雨滴效应》:
文章快捷目录
题目描述及说明
Java
python
JavaScript
C
GO
更多内容
题目名称:二维伞的雨滴效应
知识点:递归、二叉搜索树验证、逻辑处理
时间限制:1秒
空间限制:256MB
限定语言:不限
题目描述
普通的伞在二维平面世界中,左右两侧均有一条边,而两侧伞边最下面各有一个伞坠子。雨滴落到伞面后,逐步流到伞坠处,携带伞坠信息落到地面。现输入一串正整数数组序列(不含0,数组成员至少1个),要求:
- 判断该数组是否为二叉搜索树的前序遍历结果。若是,输出1;否则输出0。
- 若为二叉搜索树,输出左右伞坠信息(即树的最左端和最右端叶子节点值);若无左/右伞坠,则对应位置输出0。若非二叉搜索树,左右伞坠信息均返回0。
输入示例
8 3 1 6 4 7 10 14 13
输出示例
1 1 13
说明
1
表示是二叉搜索树前序遍历结果。1
为左伞坠值(最左叶子节点值)。13
为右伞坠值(最右叶子节点值)。
输入约束
- 输入数组任意两个数字互不相同。
- 数组长度最多1000,数值范围1~65535。
Java
问题分析
题目要求判断给定的正整数数组是否为二叉搜索树(BST)的前序遍历结果,并输出相应的结果。如果是BST,还需要找出最左和最右的叶子节点值(伞坠信息)。主要挑战在于如何高效验证前序遍历的正确性,并在验证的同时构建树结构以确定叶子节点。
解题思路
-
验证BST前序遍历:
- 递归检查数组是否符合BST前序遍历的结构。根节点后,找到第一个大于根的元素作为右子树的起点,确保右子树所有元素均大于根,左子树所有元素均小于根。
-
构建树结构:
- 如果验证通过,递归构建二叉树,以便后续查找叶子节点。
-
查找叶子节点:
- 前序遍历构建好的树,记录第一个和最后一个遇到的叶子节点值,即为最左和最右伞坠信息。
代码实现
import java.util.Scanner;public class Main {static int leftLeaf = 0;static int rightLeaf = 0;public static void main(String[] args) {Scanner scanner = new Scanner(System.in);String input = scanner.nextLine();String[] parts = input.split(" ");int[] preorder = new int[parts.length];for (int i = 0; i < parts.length; i++) {preorder[i] = Integer.parseInt(parts[i]);}boolean isBST = verifyBST(preorder, 0, preorder.length - 1);if (isBST) {TreeNode root = buildTree(preorder, 0, preorder.length - 1);findLeaves(root);System.out.println("1 " + leftLeaf + " " + rightLeaf);} else {System.out.println("0 0 0");}}private static boolean verifyBST(int[] preorder, int start, int end) {if (start > end) {return true;}int rootVal = preorder[start];int mid = end + 1;for (int i = start + 1; i <= end; i++) {if (preorder[i] > rootVal) {mid = i;break;}}for (int i = mid; i <= end; i++) {if (preorder[i] <= rootVal) {return false;}}boolean leftValid = verifyBST(preorder, start + 1, mid - 1);boolean rightValid = verifyBST(preorder, mid, end);return leftValid && rightValid;}private static TreeNode buildTree(int[] preorder, int start, int end) {if (start > end) {return null;}int rootVal = preorder[start];TreeNode root = new TreeNode(rootVal);if (start == end) {return root;}int mid = end + 1;for (int i = start + 1; i <= end; i++) {if (preorder[i] > rootVal) {mid = i;break;}}root.left = buildTree(preorder, start + 1, mid - 1);root.right = buildTree(preorder, mid, end);return root;}private static void findLeaves(TreeNode root) {if (root == null) {return;}if (root.left == null && root.right == null) {if (leftLeaf == 0) {leftLeaf = root.val;}rightLeaf = root.val;}findLeaves(root.left);findLeaves(root.right);}static class TreeNode {int val;TreeNode left;TreeNode right;TreeNode(int val) {this.val = val;}}
}
代码详解
-
主函数:
- 读取输入并转换为整型数组。
- 调用
verifyBST
验证数组是否为BST前序遍历。 - 验证通过后构建树,查找叶子节点并输出结果。
-
verifyBST函数:
- 递归检查数组区间是否符合BST结构。确定右子树起点,验证右子树所有元素大于根,递归检查左右子树。
-
buildTree函数:
- 递归构建树结构,确定左右子树区间并构建节点。
-
findLeaves函数:
- 前序遍历树,记录第一个和最后一个叶子节点值。
示例测试
示例1:
输入:8 3 1 6 4 7 10 14 13
输出:1 1 13
说明:验证通过,最左叶子为1,最右为13。
示例2:
输入:5 3 2 4 6 7
输出:1 2 7
说明:验证通过,最左叶子为2,最右为7。
示例3:
输入:5 3 6 2
输出:0 0 0
说明:右子树中出现2,验证失败。
综合分析
该方案通过递归高效地验证前序遍历的正确性,并在验证过程中确定树的结构。虽然时间复杂度为O(n²),但对于题目约束(n≤1000)足够高效。构建树后,通过前序遍历快速找到叶子节点,确保了正确性和简洁性。递归深度可能引起栈溢出,但在合理约束下表现良好。
python
问题分析
题目要求判断给定的正整数数组是否为二叉搜索树(BST)的前序遍历结果,并输出相应的结果。如果是BST,还需要找出最左和最右的叶子节点值(伞坠信息)。主要挑战在于如何高效验证前序遍历的正确性,并在验证的同时构建树结构以确定叶子节点。
解题思路
-
验证BST前序遍历:
- 递归检查数组是否符合BST前序遍历的结构。根节点后,找到第一个大于根的元素作为右子树的起点,确保右子树所有元素均大于根,左子树所有元素均小于根。
-
构建树结构:
- 如果验证通过,递归构建二叉树,以便后续查找叶子节点。
-
查找叶子节点:
- 左伞坠是左子树的最左叶子节点,右伞坠是右子树的最右叶子节点。若左/右子树不存在,则对应伞坠为0。
代码实现
class TreeNode:def __init__(self, val):self.val = valself.left = Noneself.right = Nonedef verify_bst(preorder, start, end):if start > end:return Trueroot_val = preorder[start]mid = end + 1for i in range(start + 1, end + 1):if preorder[i] > root_val:mid = ibreakfor i in range(mid, end + 1):if preorder[i] <= root_val:return Falseleft_valid = verify_bst(preorder, start + 1, mid - 1)right_valid = verify_bst(preorder, mid, end)return left_valid and right_validdef build_tree(preorder, start, end):if start > end:return Noneroot_val = preorder[start]root = TreeNode(root_val)if start == end:return rootmid = end + 1for i in range(start + 1, end + 1):if preorder[i] > root_val:mid = ibreakroot.left = build_tree(preorder, start + 1, mid - 1)root.right = build_tree(preorder, mid, end)return rootdef get_left_most(node):if not node:return 0current = nodewhile current.left or current.right:if current.left:current = current.leftelse:current = current.rightreturn current.valdef get_right_most(node):if not node:return 0current = nodewhile current.right or current.left:if current.right:current = current.rightelse:current = current.leftreturn current.val# 主程序
preorder = list(map(int, input().split()))
is_bst = verify_bst(preorder, 0, len(preorder) - 1)if is_bst:root = build_tree(preorder, 0, len(preorder) - 1)left_leaf = get_left_most(root.left) if root.left else 0right_leaf = get_right_most(root.right) if root.right else 0print(f"1 {left_leaf} {right_leaf}")
else:print("0 0 0")
代码详解
-
TreeNode类:
- 定义树的节点结构,包含值和左右子节点。
-
verify_bst函数:
- 递归检查数组是否为BST前序遍历。确定右子树起点,验证右子树所有元素大于根,递归检查左右子树。
-
build_tree函数:
- 递归构建树结构,确定左右子树区间并构建节点。
-
get_left_most和get_right_most函数:
- 分别找到左子树的最左叶子节点和右子树的最右叶子节点。若子树不存在,返回0。
-
主程序:
- 读取输入并验证,构建树后获取左右伞坠信息。
示例测试
示例1:
输入:8 3 1 6 4 7 10 14 13
输出:1 1 13
说明:验证通过,左伞坠为1,右伞坠为13。
示例2:
输入:5
输出:1 0 0
说明:验证通过,但无左右子树,伞坠为0。
示例3:
输入:5 6 7
输出:1 0 7
说明:验证通过,左伞坠为0,右伞坠为7。
综合分析
该方案通过递归高效验证前序遍历的正确性,并在验证过程中确定树的结构。时间复杂度为O(n²),适用于题目约束(n≤1000)。构建树后,通过特定路径查找最左和最右叶子节点,确保了正确性。处理了多种边界情况,如无左右子树或仅有一个节点的情况,确保输出符合题目要求。
JavaScript
问题分析
题目要求判断给定的正整数数组是否为二叉搜索树(BST)的前序遍历结果,并输出相应的结果。如果是BST,还需要找出最左和最右的叶子节点值(伞坠信息)。主要挑战在于如何高效验证前序遍历的正确性,并在验证的同时构建树结构以确定叶子节点。
解题思路
-
验证BST前序遍历:
- 递归检查数组是否符合BST前序遍历的结构。根节点后,找到第一个大于根的元素作为右子树的起点,确保右子树所有元素均大于根,左子树所有元素均小于根。
-
构建树结构:
- 如果验证通过,递归构建二叉树,以便后续查找叶子节点。
-
查找叶子节点:
- 左伞坠是左子树的最左叶子节点,右伞坠是右子树的最右叶子节点。若左/右子树不存在,则对应伞坠为0。
代码实现
class TreeNode {constructor(val) {this.val = val;this.left = null;this.right = null;}
}function verifyBST(preorder, start, end) {if (start > end) return true;const rootVal = preorder[start];let mid = end + 1; // 初始化为 end+1,表示右子树不存在// 找到第一个大于根的元素,作为右子树的起点for (let i = start + 1; i <= end; i++) {if (preorder[i] > rootVal) {mid = i;break;}}// 检查右子树是否全大于根for (let i = mid; i <= end; i++) {if (preorder[i] <= rootVal) {return false;}}// 递归验证左子树和右子树const leftValid = verifyBST(preorder, start + 1, mid - 1);const rightValid = verifyBST(preorder, mid, end);return leftValid && rightValid;
}function buildTree(preorder, start, end) {if (start > end) return null;const rootVal = preorder[start];const root = new TreeNode(rootVal);if (start === end) return root; // 叶子节点let mid = end + 1;// 找到右子树的起点for (let i = start + 1; i <= end; i++) {if (preorder[i] > rootVal) {mid = i;break;}}root.left = buildTree(preorder, start + 1, mid - 1);root.right = buildTree(preorder, mid, end);return root;
}function getLeftLeaf(node) {if (!node) return 0;let current = node;// 不断向左或向右寻找最左叶子while (current.left || current.right) {if (current.left) {current = current.left;} else {current = current.right;}}return current.val;
}function getRightLeaf(node) {if (!node) return 0;let current = node;// 不断向右或向左寻找最右叶子while (current.right || current.left) {if (current.right) {current = current.right;} else {current = current.left;}}return current.val;
}// 主程序
const input = require('fs').readFileSync(0, 'utf-8').trim().split(/\s+/);
const preorder = input.map(Number);if (verifyBST(preorder, 0, preorder.length - 1)) {const root = buildTree(preorder, 0, preorder.length - 1);const left = root.left ? getLeftLeaf(root.left) : 0;const right = root.right ? getRightLeaf(root.right) : 0;console.log(`1 ${left} ${right}`);
} else {console.log('0 0 0');
}
代码详解
-
TreeNode类:
- 定义树的节点结构,包含值和左右子节点。
-
verifyBST函数:
- 第1行:若当前区间无效(
start > end
),直接返回true
。 - 第3-8行:找到第一个大于根节点的元素,作为右子树的起点。
- 第10-13行:检查右子树所有元素是否均大于根。
- 第15-17行:递归验证左子树和右子树。
- 第1行:若当前区间无效(
-
buildTree函数:
- 第1-2行:若区间无效,返回
null
。 - 第4-5行:创建根节点,若区间长度为1(叶子节点),直接返回。
- 第7-12行:找到右子树起点,递归构建左右子树。
- 第1-2行:若区间无效,返回
-
getLeftLeaf和getRightLeaf函数:
- 核心逻辑:沿着左(或右)路径向下,直到找到叶子节点。
- 循环条件:只要当前节点有子节点,继续遍历。
- 返回值:最终找到的叶子节点的值。
-
主程序:
- 输入处理:从标准输入读取数据,并转换为数字数组。
- 验证与构建树:若验证通过,构建树并查找叶子节点。
- 输出结果:根据验证结果输出对应信息。
示例测试
-
输入示例1:
8 3 1 6 4 7 10 14 13
输出:
1 1 13
解释:
- 数组是BST的前序遍历。
- 最左叶子节点是
1
,最右叶子节点是13
。
-
输入示例2:
5
输出:
1 0 0
解释:
- 单节点树,无左右子树,伞坠均为0。
-
输入示例3:
5 3 6 2
输出:
0 0 0
解释:
- 右子树中存在
2
(小于根节点5
),验证失败。
- 右子树中存在
综合分析
-
时间复杂度:
- 验证函数:递归分割数组,最坏时间复杂度为 (O(n^2)),但实际表现较好。
- 构建树与查找叶子:复杂度为 (O(n)),总体复杂度由验证函数主导。
-
空间复杂度:
- 递归栈深度为树的高度,最坏为 (O(n))(退化成链表)。
-
优势:
- 递归逻辑清晰,直接体现BST前序遍历的性质。
- 叶子查找通过路径遍历,无需全局变量或复杂遍历。
-
适用场景:
- 输入规模较小(题目约束数组长度最多1000),递归深度可控。
- 逻辑简洁,适合快速验证问题。
C++
问题分析
题目要求判断一个数组是否为二叉搜索树(BST)的前序遍历结果,并输出左右伞坠(最左和最右叶子节点值)。若数组非BST前序,输出0。核心挑战在于验证前序的有效性及高效构建树结构。
解题思路
-
验证前序合法性:
- 递归分割:根节点后,找到第一个大于根的元素作为右子树起点,确保右子树所有元素大于根,左子树元素均小于根。
- 递归验证:对左右子树递归执行相同操作。
-
构建二叉树:
- 递归构建,确定左右子树区间,生成树节点。
-
查找伞坠:
- 左伞坠:左子树的最左叶子(优先左子,若无则向右)。
- 右伞坠:右子树的最右叶子(优先右子,若无则向左)。
代码实现
#include <iostream>
#include <vector>
#include <sstream>
#include <string>using namespace std;struct TreeNode {int val;TreeNode* left;TreeNode* right;TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
};class Solution {
public:bool verifyBST(vector<int>& preorder, int start, int end) {if (start > end) return true;int rootVal = preorder[start];int mid = end + 1; // 右子树的起始位置(默认无右子树)// 找到第一个大于根的元素作为右子树起点for (int i = start + 1; i <= end; ++i) {if (preorder[i] > rootVal) {mid = i;break;}}// 检查右子树是否全大于根for (int i = mid; i <= end; ++i) {if (preorder[i] <= rootVal) return false;}// 递归验证左右子树return verifyBST(preorder, start + 1, mid - 1) && verifyBST(preorder, mid, end);}TreeNode* buildTree(vector<int>& preorder, int start, int end) {if (start > end) return nullptr;TreeNode* root = new TreeNode(preorder[start]);if (start == end) return root; // 叶子节点int mid = end + 1;for (int i = start + 1; i <= end; ++i) {if (preorder[i] > root->val) {mid = i;break;}}root->left = buildTree(preorder, start + 1, mid - 1);root->right = buildTree(preorder, mid, end);return root;}int getLeftLeaf(TreeNode* node) {if (!node) return 0;TreeNode* cur = node;while (cur->left || cur->right) {if (cur->left) cur = cur->left;else cur = cur->right;}return cur->val;}int getRightLeaf(TreeNode* node) {if (!node) return 0;TreeNode* cur = node;while (cur->right || cur->left) {if (cur->right) cur = cur->right;else cur = cur->left;}return cur->val;}
};int main() {string line;getline(cin, line);istringstream iss(line);vector<int> preorder;int num;while (iss >> num) preorder.push_back(num);Solution sol;bool isBST = sol.verifyBST(preorder, 0, preorder.size() - 1);if (isBST) {TreeNode* root = sol.buildTree(preorder, 0, preorder.size() - 1);int left = root->left ? sol.getLeftLeaf(root->left) : 0;int right = root->right ? sol.getRightLeaf(root->right) : 0;cout << "1 " << left << " " << right << endl;} else {cout << "0 0 0" << endl;}return 0;
}
代码详解
-
TreeNode结构体:
- 定义树节点,包含值、左右子节点指针。
-
verifyBST函数:
- 终止条件:区间无效时返回
true
。 - 确定右子树起点:遍历找到第一个大于根的元素。
- 检查右子树合法性:确保右子树元素均大于根。
- 递归验证:分别验证左右子树是否为合法BST前序。
- 终止条件:区间无效时返回
-
buildTree函数:
- 递归构建树节点,划分左右子树区间,生成子树。
-
getLeftLeaf/getRightLeaf函数:
- 通过循环找到最左/右叶子节点,优先沿左/右路径,若无则转向。
-
主函数:
- 读取输入并解析为整数数组。
- 验证是否为BST前序,构建树并查找伞坠,输出结果。
示例测试
示例1:
输入:
8 3 1 6 4 7 10 14 13
输出:
1 1 13
解析:
- 数组是BST前序,左伞坠在左子树最左叶子(1),右伞坠在右子树最右叶子(13)。
示例2:
输入:
5
输出:
1 0 0
解析:
- 单节点树,无左右子树,伞坠均为0。
示例3:
输入:
5 6 7
输出:
1 0 7
解析:
- 右子树存在,右伞坠是7,左子树不存在,左伞坠为0。
综合分析
-
时间复杂度:
- 验证函数:最坏情况下(如链表结构)时间复杂度为 (O(n^2))。
- 构建树和查找叶子:时间复杂度 (O(n)),总体复杂度由验证主导。
-
空间复杂度:
- 递归调用栈深度为树的高度,最坏情况(链表)为 (O(n))。
-
优势:
- 直接利用BST前序特性递归验证,逻辑清晰。
- 避免全局变量,通过函数参数传递状态,结构简洁。
-
适用场景:
- 处理规模较小(数组长度≤1000)时效率可接受。
- 递归实现直观,适合快速验证和树构建。
C
问题分析
题目要求判断一个数组是否为二叉搜索树(BST)的前序遍历结果,并输出左右伞坠(最左和最右叶子节点值)。若数组非BST前序,输出0。核心挑战在于验证前序的有效性及高效构建树结构。
解题思路
-
验证前序合法性:
- 递归分割:根节点后,找到第一个大于根的元素作为右子树起点,确保右子树所有元素大于根,左子树元素均小于根。
- 递归验证:对左右子树递归执行相同操作。
-
构建二叉树:
- 递归构建,确定左右子树区间,生成树节点。
-
查找伞坠:
- 左伞坠:左子树的最左叶子(优先左子,若无则向右)。
- 右伞坠:右子树的最右叶子(优先右子,若无则向左)。
代码实现
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>// 树节点结构体
typedef struct TreeNode {int val;struct TreeNode *left;struct TreeNode *right;
} TreeNode;// 验证前序合法性
bool verifyBST(int *preorder, int start, int end) {if (start > end) return true;int rootVal = preorder[start];int mid = end + 1; // 右子树起点(默认无右子树)// 找到第一个大于根的元素for (int i = start + 1; i <= end; i++) {if (preorder[i] > rootVal) {mid = i;break;}}// 检查右子树是否全大于根for (int i = mid; i <= end; i++) {if (preorder[i] <= rootVal) return false;}// 递归验证左右子树return verifyBST(preorder, start + 1, mid - 1) &&verifyBST(preorder, mid, end);
}// 构建二叉树
TreeNode *buildTree(int *preorder, int start, int end) {if (start > end) return NULL;TreeNode *root = (TreeNode *)malloc(sizeof(TreeNode));root->val = preorder[start];root->left = root->right = NULL;if (start == end) return root; // 叶子节点int mid = end + 1;for (int i = start + 1; i <= end; i++) {if (preorder[i] > root->val) {mid = i;break;}}root->left = buildTree(preorder, start + 1, mid - 1);root->right = buildTree(preorder, mid, end);return root;
}// 找左伞坠
int getLeftLeaf(TreeNode *node) {if (!node) return 0;TreeNode *cur = node;while (cur->left || cur->right) {if (cur->left) cur = cur->left;else cur = cur->right;}return cur->val;
}// 找右伞坠
int getRightLeaf(TreeNode *node) {if (!node) return 0;TreeNode *cur = node;while (cur->right || cur->left) {if (cur->right) cur = cur->right;else cur = cur->left;}return cur->val;
}// 输入处理:将字符串转换为数组
void parseInput(char *input, int **preorder, int *size) {int capacity = 10;*preorder = (int *)malloc(capacity * sizeof(int));*size = 0;char *token = strtok(input, " ");while (token) {if (*size >= capacity) {capacity *= 2;*preorder = realloc(*preorder, capacity * sizeof(int));}(*preorder)[(*size)++] = atoi(token);token = strtok(NULL, " ");}
}int main() {char input[10000];fgets(input, sizeof(input), stdin);int *preorder = NULL;int size = 0;parseInput(input, &preorder, &size);if (verifyBST(preorder, 0, size - 1)) {TreeNode *root = buildTree(preorder, 0, size - 1);int left = root->left ? getLeftLeaf(root->left) : 0;int right = root->right ? getRightLeaf(root->right) : 0;printf("1 %d %d\n", left, right);} else {printf("0 0 0\n");}free(preorder);return 0;
}
代码详解
-
树节点结构体:
TreeNode
包含值val
和左右子节点指针,用于表示二叉树。
-
verifyBST函数:
- 终止条件:当区间无效时(
start > end
)返回true
。 - 查找右子树起点:遍历找到第一个大于根节点的元素位置
mid
。 - 检查右子树合法性:确保右子树所有元素均大于根。
- 递归验证:对左右子树递归调用
verifyBST
。
- 终止条件:当区间无效时(
-
buildTree函数:
- 递归构建二叉树,根据
mid
划分左右子树区间。 - 为每个节点分配内存,并初始化左右指针为
NULL
。
- 递归构建二叉树,根据
-
getLeftLeaf/getRightLeaf函数:
- 通过循环找到最左/右叶子节点。优先沿左/右路径向下,若无则转向另一侧。
-
parseInput函数:
- 将输入的字符串按空格分割为整数数组,动态扩展数组容量。
-
主函数:
- 读取输入并解析为数组。
- 验证是否为BST前序,构建树并查找伞坠。
- 输出结果并释放动态分配的内存。
示例测试
示例1:
输入:
8 3 1 6 4 7 10 14 13
输出:
1 1 13
解析:
- 数组是BST前序,左伞坠是左子树的最左叶子(1),右伞坠是右子树的最右叶子(13)。
示例2:
输入:
5
输出:
1 0 0
解析:
- 单节点树,无左右子树,伞坠均为0。
示例3:
输入:
5 3 6 2
输出:
0 0 0
解析:
- 右子树中存在
2
(小于根节点5
),验证失败。
综合分析
-
时间复杂度:
- 验证函数:最坏时间复杂度为 (O(n^2))(如链表结构),但实际场景下表现良好。
- 构建树和查找叶子:时间复杂度为 (O(n)),总体复杂度由验证主导。
-
空间复杂度:
- 递归调用栈深度为树的高度,最坏情况(链表结构)为 (O(n))。
- 动态数组和树节点的空间复杂度为 (O(n))。
-
优势:
- 直接利用BST前序特性递归验证,逻辑清晰。
- 动态数组处理输入,适应不同规模的输入数据。
-
适用场景:
- 处理数组长度≤1000时效率可接受。
- 递归实现直观,适合快速验证和树构建。
GO
问题分析
题目要求判断一个数组是否为二叉搜索树(BST)的前序遍历结果,并输出左右伞坠(最左和最右叶子节点值)。若数组非BST前序,输出0。核心挑战在于验证前序的有效性及高效构建树结构。
解题思路
-
验证前序合法性:
- 递归分割:根节点后,找到第一个大于根的元素作为右子树起点,确保右子树所有元素大于根,左子树元素均小于根。
- 递归验证:对左右子树递归执行相同操作。
-
构建二叉树:
- 递归构建,确定左右子树区间,生成树节点。
-
查找伞坠:
- 左伞坠:左子树的最左叶子(优先左子,若无则向右)。
- 右伞坠:右子树的最右叶子(优先右子,若无则向左)。
代码实现
package mainimport ("bufio""fmt""os""strconv""strings"
)type TreeNode struct {Val intLeft *TreeNodeRight *TreeNode
}// 验证前序遍历是否合法
func verifyBST(preorder []int, start, end int) bool {if start > end {return true}rootVal := preorder[start]mid := end + 1 // 右子树起点(默认无右子树)// 找到第一个大于根的元素for i := start + 1; i <= end; i++ {if preorder[i] > rootVal {mid = ibreak}}// 检查右子树是否全部大于根for i := mid; i <= end; i++ {if preorder[i] <= rootVal {return false}}// 递归验证左右子树return verifyBST(preorder, start+1, mid-1) && verifyBST(preorder, mid, end)
}// 构建二叉树
func buildTree(preorder []int, start, end int) *TreeNode {if start > end {return nil}root := &TreeNode{Val: preorder[start]}if start == end { // 叶子节点直接返回return root}mid := end + 1for i := start + 1; i <= end; i++ {if preorder[i] > root.Val {mid = ibreak}}root.Left = buildTree(preorder, start+1, mid-1)root.Right = buildTree(preorder, mid, end)return root
}// 获取左伞坠(最左叶子节点)
func getLeftLeaf(node *TreeNode) int {if node == nil {return 0}cur := nodefor cur.Left != nil || cur.Right != nil {if cur.Left != nil {cur = cur.Left} else {cur = cur.Right}}return cur.Val
}// 获取右伞坠(最右叶子节点)
func getRightLeaf(node *TreeNode) int {if node == nil {return 0}cur := nodefor cur.Right != nil || cur.Left != nil {if cur.Right != nil {cur = cur.Right} else {cur = cur.Left}}return cur.Val
}// 主函数
func main() {scanner := bufio.NewScanner(os.Stdin)scanner.Scan()input := scanner.Text()strs := strings.Fields(input)preorder := make([]int, len(strs))for i, s := range strs {num, _ := strconv.Atoi(s)preorder[i] = num}if verifyBST(preorder, 0, len(preorder)-1) {root := buildTree(preorder, 0, len(preorder)-1)left := 0if root.Left != nil {left = getLeftLeaf(root.Left)}right := 0if root.Right != nil {right = getRightLeaf(root.Right)}fmt.Printf("1 %d %d\n", left, right)} else {fmt.Println("0 0 0")}
}
代码详解
-
树节点定义:
TreeNode
结构体包含值Val
和左右子节点指针,用于表示二叉树。
-
verifyBST函数:
- 终止条件:当区间无效时(
start > end
)返回true
。 - 查找右子树起点:遍历找到第一个大于根节点的元素位置
mid
。 - 检查右子树合法性:确保右子树所有元素均大于根。
- 递归验证:对左右子树递归调用
verifyBST
。
- 终止条件:当区间无效时(
-
buildTree函数:
- 递归构建二叉树,根据
mid
划分左右子树区间。 - 为每个节点分配内存,并初始化左右指针为
nil
。
- 递归构建二叉树,根据
-
getLeftLeaf/getRightLeaf函数:
- 通过循环找到最左/右叶子节点。优先沿左/右路径向下,若无则转向另一侧。
-
主函数:
- 读取输入并解析为整数数组。
- 验证是否为BST前序,构建树并查找伞坠。
- 输出结果。
示例测试
示例1:
输入:
8 3 1 6 4 7 10 14 13
输出:
1 1 13
解析:
- 数组是BST前序,左伞坠是左子树的最左叶子(1),右伞坠是右子树的最右叶子(13)。
示例2:
输入:
5
输出:
1 0 0
解析:
- 单节点树,无左右子树,伞坠均为0。
示例3:
输入:
5 3 6 2
输出:
0 0 0
解析:
- 右子树中存在
2
(小于根节点5
),验证失败。
综合分析
-
时间复杂度:
- 验证函数:最坏时间复杂度为 (O(n^2))(如链表结构),但实际场景下表现良好。
- 构建树和查找叶子:时间复杂度为 (O(n)),总体复杂度由验证主导。
-
空间复杂度:
- 递归调用栈深度为树的高度,最坏情况(链表结构)为 (O(n))。
-
优势:
- 利用Go的简洁语法和自动内存管理,代码更易维护。
- 输入处理直接使用标准库函数,避免手动解析的复杂性。
-
适用场景:
- 处理数组长度≤1000时效率可接受。
- 递归实现直观,适合快速验证和树构建。
更多内容:
https://www.kdocs.cn/l/cvk0eoGYucWA
本文发表于【纪元A梦】,关注我,获取更多实用教程/资源!