最大二叉树
给定一个不含重复元素的整数数组。一个以此数组构建的最大二叉树定义如下:
二叉树的根是数组中的最大元素。
左子树是通过数组中最大值左边部分构造出的最大二叉树。
右子树是通过数组中最大值右边部分构造出的最大二叉树。
通过给定的数组构建最大二叉树,并且输出这个树的根节点。
示例 :
输入:[3,2,1,6,0,5]
输出:返回下面这棵树的根节点:
6
/ \
3 5
\ /
2 0
\
1
提示:
给定的数组的大小在 [1, 1000] 之间。
思路
最大二叉树,可以理解为根是最大的,每个子树的根也是最大的
首先,肯定要找到数组的最大值,然后将数组以最大值为界一分为二
对左边的数组进行同样操作,
对右边的数组进行同样操作,
这就是递归了
/**
* Definition for a binary tree node.
* public class TreeNode {
* int val;
* TreeNode left;
* TreeNode right;
* TreeNode(int x) { val = x; }
* }
*/
class Solution {
public TreeNode constructMaximumBinaryTree(int[] nums) {
if(nums == null || nums.length == 0) return null;
return findRoot(nums, 0, nums.length);
}
private TreeNode findRoot(int[] nums, int left, int right){//左闭右开[left right)
//递归需要边界条件
if(left == right - 1) return new TreeNode(nums[left]);
if(left >= right) return null;
//上面两个边界条件,可以转化为下面一个边界条件
//if(left == right) return null;
int maxIndex = left;
for(int i = left + 1; i < right; i++)
{
if(nums[i] > nums[maxIndex])
{
maxIndex = i;
}
}
TreeNode node = new TreeNode(nums[maxIndex]);
node.left = findRoot(nums, left, maxIndex);
node.right = findRoot(nums, maxIndex + 1, right);
return node;
}
}
需要注意的地方:
假如只要一个数组[1]
如果只用if(left == right) return null;
做为边界条件的话
maxIndex = 0
node.val = 1;
node.left = findRoot(nums, 0, 0); -> node.left = null;
node.right = findRoot(nums, 1, 1); -> node.right = null;
完全满足要求
题目变种
上面的题目是返回根节点
现在,试着返回一个数组:数组里面存放的是每个节点的父节点的索引
比如上面例子原数组是:[3, 2, 1, 6, 0, 5]
每个节点的父节点值是:[6, 3, 2, -1, 5, 6]
每个节点的父节点的索引是:[3, 0, 1, -1, 5, 3]
思路1:暴力法
其实,这个跟树已经没有关系了
仔细观察上图原数组,
元素3,距离3最近的比3大的元素是6,6的下标为3,因此,元素3下面对应的返回是3
元素2,距离2最近的比2大的元素是3,3的下标为0,因此,元素2下面对应的返回是0
元素1,距离1最近的比1大的元素是2和6,取小的那个2,2的下标为1,因此,元素1下面对应的返回是1
元素6,距离6最近的比6大的元素没有,因此,元素6下面对应的返回是-1
元素0,距离0最近的比0大的元素是6和5,取小的那个5,5的下标为5,因此,元素0下面对应的返回是5
元素5,距离5最近的比5大的元素是6,6的下标为3,因此,元素5下面对应的返回是3
也就是,取出某一元素a,左右遍历,找到最近比元素a大的值,如果左右有两个,则取小的,然后将小的值的下标填入返回数组即可。
那,我们容易想到的是,每次拿到一个元素,都要左右寻找比元素大的值,最坏情况下每次都需要遍历整个数组,这样,最坏时间复杂度为O(n^2)
思路2:利用栈
建立一个单调递减的栈
第一个元素3入栈
准备第二个元素2入栈,2比3小,所以2可以入栈,且3就是2的左边第一个比它大的值
准备第三个元素1入栈,1比2小,所以1可以入栈,且2就是1的左边第一个比它大的值
准备第四个元素6入栈,6比1大,基于栈是单调递减的,则将元素1出栈,此时可以确定1的右边第一个比它大的值是6;
6再次准备入栈,6比2大,基于栈是单调递减的,则将元素2出栈,此时可以确定2的右边第一个比它大的值是6;
6再次准备入栈,6比3大,基于栈是单调递减的,则将元素3出栈,此时可以确定3的右边第一个比它大的值是6;
此时,栈为空,则6入栈
准备第五个元素0入栈,0比6小,所以0可以入栈,且6就是0的左边第一个比它大的值
准备第六个元素5入栈,5比0大,基于栈是单调递减的,则将元素0出栈,此时可以确定0的右边第一个比它大的值是5;
6再次入栈,6比5小,所以5可以入栈,且6就是5的左边第一个比它大的值
也就是,当某个元素a可以入栈的时候,则栈顶元素b就是可以入栈元素a的左边第一个比它大的值
当某个元素a比栈顶元素b大时,元素b要出栈,比较大的元素a就是栈顶元素b的右边第一个比它大的值
这样看来,我们需要两个数组leftArray和rightArray,leftArray存放元素左边第一个比它大的值,rightArray存放右边第一个比它大的值。两个数组计算完之后,找出两个数组中值比较小的放进resultArray中去。
数组里面放索引,好处是,索引与值都有了
package 排序_数组;
import java.util.Stack;
public class 左右第一个最大值 {
public static void main(String[] args)
{
}
public int[] parentIndex(int[] nums)
{
//先写边界值
if(nums == null || nums.length == 0) return null;
//数组里面放索引
int[] leftArray = new int[nums.length];
int[] rightArray = new int[nums.length];
int[] resultArray = new int[nums.length];
//保持栈,从栈底到栈顶是单调递减的
Stack<Integer> stack = new Stack<Integer>();
//扫描一遍数组
for(int i = 0; i < nums.length; i++)
{
//初始化为-1
leftArray[i] = -1;
rightArray[i] = -1;
while (!stack.empty() && nums[i] > nums[stack.peek()])
{
rightArray[stack.pop()] = i;
}
leftArray[i] = stack.empty() ? -1 : stack.peek();
stack.push(i);
}
for (int j = 0; j < resultArray.length; j++) {
if(leftArray[j] == -1 && rightArray[j] == -1)
{
resultArray[j] = -1;
continue;
}
if (leftArray[j] == -1) {
resultArray[j] = rightArray[j];
}else if (rightArray[j] == -1) {
resultArray[j] = leftArray[j];
}else if (nums[leftArray[j]] > nums[rightArray[j]]) {
resultArray[j] = rightArray[j];
}else {
resultArray[j] = leftArray[j];
}
}
return resultArray;
}
}