建立数组的MaxTree + 栈 + HashMap + 二叉树的建立

题目:

一个数组的MaxTree定义如下:

数组必须没有重复元素;

MaxTree是一个二叉树,数组的每一个值对应一个二叉树节点;

包括MaxTree树在内且在其中的每一颗子树上,值最大的节点都是数的头。

给定一个没有重复元素的数组arr,写出生成这个数组的MaxTree的函数,要求如果数组长度为N,则时间复杂度为O(N)、额外空间复杂度为O(N) 。

分析: 利用下列原则可以建立题目要求的树:

1每一个数的父节点是它左边第一个比它大的数和它右边第一个比它大的数中,较小的那个。
2如果一个数左边没有比它大的数,右边也没有,也就是说,这个数是整个数组的最大值,那个这个是树根节点。

证明省略。


如何快速地找到每一个数左右两边第一个比它大的树呢?

用栈,找每个数左边第一个比它大的数时,从左到右遍历每个数,栈中保持从上到下递增序列,新来的数不停的利用pop弹出栈顶,直到栈顶比新数大或没有数。 该方法,每个数进栈一次,出栈一次,时间复杂度为O(N)。


代码如下:

import java.util.*;
public class Main {
	public static void main(String[]args){
		Main test = new Main() ; 
		int []arr = {3 , 4 , 5 , 1 , 2} ;
		Node head = test.getMaxTree(arr) ;	 
		test.preOrder(head); //按照前序遍历二叉树
    }
	public Node getMaxTree(int[] arr){
		Node [] nArr = new Node[arr.length] ; //声明
		for(int i = 0 ; i< arr.length ; i++){
			nArr[i] = new Node(arr[i]) ;  //创建节点
		}
		//利用栈,找到每个数左边的第一个比它大的数,从左到右遍历,栈中保持递减序列,新来的数不停地利用pop出栈顶
		//直到栈顶比新数大或者栈空
		Stack<Node> stack = new Stack<Node>() ;
		HashMap<Node , Node> lBigMap = new HashMap<Node , Node>() ;
		for(int i = 0 ; i< arr.length ; i++){
			Node cur = nArr[i] ;
			while(!stack.empty() && stack.peek().value < cur.value){
				popStackSetMap(stack , lBigMap) ; //弹出栈顶,并保存其对应的左边第一个比它大的节点
			}
			stack.push(cur); // 压入新节点		
		}
		while(!stack.empty()){ //处理剩下的在栈中按从上到下升序的元素
			popStackSetMap(stack , lBigMap) ;
		}
		//用同样的方法,从右往左遍历,求得每个数往右第一个比它大的数
		HashMap<Node , Node > rBigMap = new HashMap<Node , Node>() ;
		for(int i = nArr.length -1 ; i >= 0 ; i--){
			Node cur = nArr[i] ;
			while(!stack.empty() && stack.peek().value < cur.value){
				popStackSetMap(stack , rBigMap) ;				
			}
			stack.push(cur) ;	
		}
		while(!stack.empty()){
			popStackSetMap(stack, rBigMap) ;
		}
		//构建树
		//原则是:1每一个数的父节点是它左边第一个比它大的数和它右边第一个比它大的数中,较小的那个。
		//2如果一个数左边没有比它大的数,右边也没有,也就是说,这个数是整个数组的最大值,那个这个是树根节点。
		Node head = null ;
		for(int i = 0 ; i< arr.length  ; i++){
			Node cur = nArr[i] ;
			Node left = lBigMap.get(cur); //找到的当前节点左边第一个比它大的节点
			Node right = rBigMap.get(cur) ;//找到的当前节点右边第一个比它大的节点
			if(left == null && right == null){  
				head = cur ; //根节点
			}else if(left == null){ 
				if(right.left == null){  //建立父子关系
					right.left = cur ;
				}else{
					right.right = cur ;
				}				
			}else if(right == null){
				if(left.left == null){
					left.left = cur ;
				}else{
					left.right = cur ;
				}
			}else{
				Node parent = left.value < right.value ? left : right ;
				if(parent.left == null){
					parent.left = cur ;
				}else{
					parent.right = cur ;
				}
			}
		}
		return  head;
	}
	public void popStackSetMap(Stack<Node> stack, HashMap<Node, Node> map) {
		// TODO Auto-generated method stub
		Node popNode = stack.pop() ; //出栈的时候,确定好了映射关系
		if(stack.isEmpty()){
			map.put(popNode , null) ;
		}
		else{
			map.put(popNode , stack.peek()) ;
		}
	}
	
	//前序遍历二叉树
	public void preOrder(Node head){
		if(head != null){
			System.out.println(head.value);
			preOrder(head.left) ;
			preOrder(head.right) ;
		}
	}		
}
class Node{
	int value ;
	Node left ;
	Node right ;
	public Node(int value){
		this.value = value ;
		left = right = null ; //不能省略,创建树时有效
	}
}


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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值