数据结构与算法---栈---最大二叉树、利用栈寻找左右第一个比它大的数

最大二叉树

654. 最大二叉树

给定一个不含重复元素的整数数组。一个以此数组构建的最大二叉树定义如下:

二叉树的根是数组中的最大元素。
左子树是通过数组中最大值左边部分构造出的最大二叉树。
右子树是通过数组中最大值右边部分构造出的最大二叉树。

通过给定的数组构建最大二叉树,并且输出这个树的根节点。

示例 :
输入:[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;
	}
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值