算法模板 [1] 二叉树(结合课程总结的算法模板)

二叉树模版

据不完全总结,二叉树的题大致可以分为两种,一种是通过前序、中序、后序、层序遍历来解决问题。

另一种为递归问题,需要从每个节点来获取信息,然后提取出题目中要求的信息

遍历模版

二叉树非递归遍历

中序

  1. 先找到最左节点,并逐步压栈
  2. 当最左为空时,弹出栈顶(此时为最左节点),并输出
  3. 找最左节点有没有右孩子,有则压栈(循环1,2),没有进行下一步
  4. 没有右孩子时,当前节点为null,弹出栈顶(此时栈顶为最左节点的父亲节点)
  5. 输出 最左节点的父亲节点 的值
  6. 找 5中的节点有没有右孩子 重复(1,2)
  7. 当栈为空 且 当前遍历的节点为null 时 ,遍历结束
    public List<Integer> inorderTraversal(TreeNode root) {
    
    
        if(root==null){
    
    
            return new ArrayList<>();
        }
        List<Integer> list = new ArrayList<>();
        Stack<TreeNode> stack=new Stack<>();
        while(!stack.isEmpty() || root!=null){
    
    
            if(root!=null){
    
    
                stack.push(root);
                root=root.left;
            }else{
    
    
                root=stack.pop();
                list.add(root.val);
                root=root.right;
            }
        }
        return list;
    }

前序

public List<Integer> preorderTraversal(TreeNode root) {
    
    
        if(root==null){
    
    
            return new ArrayList<>();
        }
        List<Integer> list = new ArrayList<>();
        Stack<TreeNode> stack=new Stack<>();
        stack.push(root);
        while(!stack.isEmpty()){
    
    
            TreeNode node = stack.pop();
            
            if(node.right!=null){
    
    
                stack.push(node.right);
            }                  list.add(node.val);
                  if(node.left!=null){
    
    
                stack.push(node.left);
            }
            
        }
        return list;
    }

后序

    public List<Integer> postorderTraversal(TreeNode root) {
    
    
        if(root==null){
    
    
            return new ArrayList<>();
        }
        List<Integer> list = new ArrayList<>();
        Stack<TreeNode> stack=new Stack<>();
        Stack<Integer> temp =new Stack<>();
        stack.push(root);
        while(!stack.isEmpty()){
    
    
            TreeNode node = stack.pop();
                        if(node.left!=null){
    
    
                stack.push(node.left);
            }        
            temp.push(node.val);
    if(node.right!=null){
    
    
                stack.push(node.right);
            }    
            
        }
        while(!temp.isEmpty()){
    
    
            list.add(temp.pop());
        }
        return list;
    }

层次遍历非递归

很多求层相关的问题都可以使用此模版解题

 public List<List<Integer>> resList = new ArrayList<List<Integer>>();
    public List<List<Integer>> levelOrder(TreeNode root) {
    
    
        checkFun02(root);
        return resList;
    }
    public void checkFun02(TreeNode node) {
    
    
        if (node == null) return;
        Queue<TreeNode> que = new LinkedList<TreeNode>();
        que.offer(node);

        while(!que.isEmpty()){
    
    
            List<Integer> itemList = new ArrayList<Integer>();
            int len = que.size();

            while (len > 0) {
    
    
                TreeNode tmpNode = que.poll();
                itemList.add(tmpNode.val);

                if (tmpNode.left != null) que.offer(tmpNode.left);
                if (tmpNode.right != null) que.offer(tmpNode.right);
                len--;
            }
            resList.add(itemList);
        }
    }

注意事项

求二叉树对称的时候就是把两个树的节点同时加入队列进行比较

遇到在二叉搜索树上求什么最值,求差值之类的,都要思考一下二叉搜索树可是有序的,要利用好这一特点

迭代法中,一般一起操作两个树都是使用队列模拟类似层序遍历,同时处理两个树的节点,这种方式最好理解,如果用模拟递归的思路的话,要复杂一些。

大多数二叉搜索树的题目,其实都离不开中序遍历,因为这样就是有序的

代码随想录

递归模版

可以解决面试中绝大多数的二叉树问题尤其是树型dp问题
本质是利用递归遍历二叉树的便利性

递归套路步骤

  1. 假设以X节点为头,假设可以向X左树和X右树要任何信息
  2. 在上一步的假设下,讨论以X为头节点的树,得到答案的可能性(最重要)
  3. 列出所有可能性后,确定到底需要向左树和右树要什么样的信息
  4. 把左树信息和右树信息求全集,就是任何一 一棵子树都需要返回的信息S
  5. 递归函数都返回S,每一棵子树都这么要求
  6. 写代码,在代码中考虑如何把左树的信息和右树信息整合出整棵树的信息

判断是否为平衡二叉树

给定一棵二叉树的头节点head,返回这颗二叉树是不是平衡二叉树

平衡二叉树(Balanced Binary Tree)又被称为AVL树:它是一 棵空树或它的左右两个子树的高度差的绝对值不超过1,并且左右两个子树都是一棵平衡二叉树

列可能性:

1.只有左、右子树都为平衡二叉树,且俩个子树高度相差为的绝对值小于1,这个树才是平衡二叉树

要信息:

1.左树,高度? 平衡否

2.右树,高度? 平衡否

public static boolean isBalanced2(Node head) {
    
    
	return process2(head) .isBalaced;
}


//左、右要求一样,Info信息返回的结构体
public static class Info {
    
    
    public boolean isBalaced;
    public int height;
    public Info(boolean b, int h) {
    
    
    isBalaced = b;
    height = h;
    }
}

public static Info process2(Node X) {
    
    
    if (X == nu1l) {
    
    
        // 必须返回自己的信息
    	return new Info(true, 0);
    }
    Info leftInfo = process2(X.1eft);
    Info rightInfo = process2(X. right);
    
    int height = Math. max(1eftInfo . height, rightInfo.height) + 1;
    
    boolean isBalanced = true;
    
    if (!leftInfo.isBalaced|| 
        ! rightInfo. isBalaced|| 
        Math. abs(leftInfo. height - rightInfo.height)>1
       ){
    
    
    	isBalanced = false;
    }
    return new Info(isBalanced, height);
}


返回整棵二叉树的最大距离

给定一棵二叉树的头节点head,任何两个节点之间都存在距离,
返回整棵二叉树的最大距离

/pic:mw://088ba329253e8a81d284465b9d7d838b

1.情况分类

​ 如果最大距离与当前节点无关,

​ 最大距离就是,要么是左树最大距离,要么右树最大距离

​ 如果最大距离与当前节点有关

​ 左树最远节点经过当前节点,到达最右节点,即为最大距离

2.返回信息

​ 左树,返回最大距离

​ 右树,返回最大距离

​ 当前节点,返回 左树、右树、左树加根节点加右树,三者最大的

public static int maxDistance2(Node head) {
    
    
	return process(head) . maxDistance;
}
public static class Info {
    
    
    public int maxDistance;
    public int height;
    public Info(int dis, int h) {
    
    
    maxDistance = dis;
    height = h;
}
public static Info process(Node head) {
    
    
    if (head == nu1l) {
    
    
    return new Info(0, 0);
    Info leftInfo = process(head.left);
    Info rightInfo = process(head. right);
    int height = Math .max(leftInfo. height, rightInfo. height) + 1;
    
    int maxDistance = Math . max(
    	Math . max(leftInfo . maxDistance, rightInfo. maxDistance),
	    leftInfo. height + rightInfo.height + 1);	
	    
    return new Info( maxDistance, height);
}

二叉树的直径

返回这颗二叉树中最大的二叉搜索子树的节点数

给定一棵二叉树的头节点head,
返回这颗二叉树中最大的二叉搜索子树的节点数

搜索二叉树:整个树上没有重复值,左树的值比根小,右树比根大,每颗子树都如此

1.可能性

​ 与当前节点有关, 子树最大二叉搜索子树的节点+根节点

​ 要求左搜存在,右搜存在,且左最大<根,根<右最小

​ 与当前节点无关,左子树的最大二叉搜索子树,右子树的最大二叉搜索子树,取最大

2.信息

​ 左树,最大子搜的size,是否平衡,左树的最大节点值

​ 右树,最大子搜的size,是否平衡,右树的最小节点值

3.求全集

​ info 信息类定义为:最大子搜的size,是否平衡,子树最大节点值,子树最小节点值

pub1ic static int maxSubBSTSize2(Node head) {
    
    
    if (head == nu1l) {
    
    
    return 0;
    return process( head) . maxSubBSTSize;
}

public static class Info {
    
    
    public boolean isBST;
    public int maxSubBSTSize;
    public int min;
    public int max;
    public Info(boolean is, int size, int mi, int ma) {
    
    
    isBST = is;
    maxSubBSTSize = size;
    min = mi;
    max = ma;
}
public static Info process(Node X){
    
    
    //不好设置初始信息,所以认为判空
    if(X==null){
    
    
        return null;
    }
    Info leftInfo = process(X.left);
    Info rightInfo = process(X.right);
    
 
    int min=X.value;
    int max=X.value;
    
    
    if(leftInfo!=null){
    
    
        min=Math.min(min,leftInfo.min);
        max=Math.max(max,leftInfo.max);
    }
    if(rightInfo!=null){
    
    
        min=Math.min(min,rightInfo.min);
        max=Math.max(max,rightInfo.max);
    }
   
    int maxSubBSTSize=0;
    
    if(leftInfo!=null){
    
    
        maxSubBSTSize=leftInfo.maxSubBSTSize;
    }
    if(rightInfo!=null){
    
    
        maxSubBSTSize=Math.max(maxSubBSTSize,rightInfo.maxSubBSTSize);
    }
    
    boolean isBST=false;
    //如果可能性二成立,需要更新maxSubBSTSize,且设置isBST
 
    
    if(
   
        // 左树整体是搜索二叉树
        (leftInfo==null?true :leftInfo.isBST) 
        &&
         // 右树整体是搜索二叉树
        (rightInfo==null?true :rightInfo.isBST) 
        &&
        // 左树最大值 《 x
        (leftInfo==null?true : leftInfo.max <X.value)
        &&
        // 右树最小值 》x
        (rightInfo==null?true : rightInfo.min >X.value)
    
    ){
    
    
        // 以x为头的所有节点,只要用了信息就要判空
        maxSubBSTSize=
        (leftInfo == null ? 0 : leftInfo . maxSubBSTSize)
		+
		(rightInfo == nu1l ? 0 : rightInfo. maxSubBSTSize )
		+
		1;

        isBST=true;
    }

    
    return new Info(isBST,maxSubBSTSize,min,max);
    
}

若求,最大二叉搜索树的头节点?

验证二叉搜索树

派对的最大快乐值

员工信息的定义如下:
class Employee {
public int happy;//这名员工可以带来的快乐值
List< Employee> subordinates; //这名员工有哪些直接下级
}
公司的每个员工都符合Employee类的描述。整个公司的人员结构可以看作
是一棵标准的、没有环的多叉树。树的头节点是公司唯一的老板。除老板之
外的每个员工都有唯一的直接上级。叶节点是没有任何下属的基层员工
(subordinates列表为空),除基层员工外,每个员工都有一个或多个直接下级。

这个公司现在要办party,你可以决定哪些员工来,哪些员工不来,规则:
1.如果某个员工来了,那么这个员工的所有直接下级都不能来
2.派对的整体快乐值是所有到场员工快乐值的累加
3.你的目标是让派对的整体快乐值尽量大
给定一棵多叉树的头节点boss,请返回派对的最大快乐值。

可能性

1.给x 发了邀请,即 所有直接子树不来的最大快乐值

2.没有给x 发邀请,所有子树的最大快乐值

需要的信息

​ 该节点不参加的最大快乐值

​ 该节点参加的最大快乐值

public static class Employee {
    
    
    public int happy;
    public List< Employee> nexts;
    public Employee(int h) {
    
    
    happy = h;
    nexts = new
    ArrayList<>();
}
public static class Info {
    
    
    pub1ic int yes;
    public int no;
    public Info(int y, int n) {
    
    
    yes = y;
    no=n;
    }
}
public static Info process2(Employee x) {
    
    
    if (x.nexts. isEmpty() {
    
    
    	return new Info(x.happy, 0);
    }
    int yes = x.happy;
    intno=0;
    for (Employee next : x.nexts) {
    
    
    	Info nextInfo = process2(next);
    	yes += nextInfo. no;
    	no += Math .max(nextInfo.yes, nextInfo.no);
    }
    return new Info(yes, no);
}

给定一棵二叉树的头节点head,返回这颗二叉树是不是满二叉树

视频链接

满二叉树

高度是L,节点树是n

满足 (2^L ) -1 = n

public static boolean isFul12(Node head) {
    
    
    if (head == nu1l) {
    
    
    return true ; 
    }
    Info all = process(head);
    return (1 << all.height) - 1 == all.nodes;
}
public static class Info {
    
    
    public int height;
    public int nodes;
    public Info(int h, int n) {
    
    
    height = h;
    nodes = n;
    }
}
public static Info process(Node head) {
    
    
    if (head == nu1l) {
    
    
    return new Info(00);
    Info leftInfo = process (head.1eft);
    Info rightInfo = process(head. right);
    int height = Math .max(leftInfo. height, rightInfo.height) + 1;
    int nodes = leftInfo.nodes + rightInfo.nodes + 1;
    return new Info(height, nodes);
}

返回这颗二叉树中最大的二叉搜索子树的头节点

pub1ic static Node maxSubBSTHead2(Node head) {
    
    
    if (head == nu1l) {
    
    
    return nu11 ;
    }
    return process(head) . maxSubBSTHead;
}
pub1ic static class Info {
    
    
    public Node maxSubBSTHead;
    public int maxSubBSTSize;
    public int min;
    public int max;
    public Info(Node h, int size, int mi, int ma) {
    
    
    maxSubBSTHead = h ;
    maxSubBSTSize = size;
    min = mi;
    max = ma;
    }
}

public static Info process(Node X){
    
    
    if (X == null) {
    
    
    return null ;
		}
    Info leftInfo = process(X.left);
    Info rightInfo= process(X.right);
    int min = X. value;
    int max = X. value ;
    Node maxSubBSTHead = null;
    int maxSubBSTSize = 0;
    if (leftInfo != nu1l) {
    
    
    min = Math . min(min, leftInfo. min);
    max = Math . max(max, leftInfo. max);
    maxSubBSTHead = leftInfo . maxSubBSTHead;
    maxSubBSTSize = leftInfo. maxSubBSTSize ;
       }
    if (rightInfo != null) [
    min = Math. min(min, rightInfo .min);
    max = Math . max(max, rightInfo . max);
      if (rightInfo. maxSubBSTSize > maxSubBSTSize) {
    
    
      maxSubBSTHead = rightInfo. maxSubBSTHead;
      maxSubBSTSize = rightInfo . maxSubBSTSize;
      }
		}
if ((leftInfo == null ? true : (leftInfo.maxSubBSTHead == X.left && leftInfo.max < X.value))
				&& (rightInfo == null ? true : (rightInfo.maxSubBSTHead == X.right && rightInfo.min > X.value))) {
    
    
			maxSubBSTHead = X;
			maxSubBSTSize = (leftInfo == null ? 0 : leftInfo.maxSubBSTSize)
					+ (rightInfo == null ? 0 : rightInfo.maxSubBSTSize) + 1;
		}
		return new Info(maxSubBSTHead, maxSubBSTSize, min, max);
}

下面例子的视频链接

给定一棵二叉树的头节点head,返回这颗二叉树中是不是完全二叉树

完全二叉树:一棵深度为k的有n个结点的二叉树,对树中的结点按从上至下、从左到右的顺序进行编号,如果编号为i(1≤i≤n)的结点与满二叉树中编号为i的结点在二叉树中的位置相同,则这棵二叉树称为完全二叉树

层次遍历

1.任意节点,有右无左 false,否则继续

2.一旦遇到左右孩子不双全,接下来的所有节点都必须是叶节点

public static boolean isCBT1(Node head) [
    if (head == nu1l) (
    return true ;
    ]
    LinkedList<Node> queue = new LinkedList<>();
    
    boolean leaf = false;
    Node l = null;
    Node r = null;
    queue . add(head);
  	while (!queue . isEmpty()) {
    
     
    head = queue. pol1();
    1 = head.left;
    r = head.right;
    if (
    //如果遇到了不双全的节点之后,又发现当前节点不是叶节点
    (leaf && !(l == null && r == null)) || (l == null && r != null)) (
    return false;
    ]
    if (l != null) (
    queue .add(l);
    ]
    if (r != null) {
    
     
    queue.add(r);
    ]
    if (l == null r == nu1l) {
    
     
    leaf = true ;
    }
   }
   return true 
}

递归

根据叶节点到达的位置

1.满二叉树=完全二叉树,无缺口

2.缺口停在左树,有缺口

3.缺口在右树,但没接触到左树的最右子树

4.缺口在右树,且接触到了左树的最右子树

需求信息:

左树,是否满二叉树,及高度

左树,是否满二叉树,及高度

1俩树都满

2 左树完全二叉树,右树满二叉树,且左树高度-1等于右树高度

3左树满二叉树,右树满二叉树,且左树高度-1等于右树高度

4左树满的,右树完全,高度相等

四种情况属于一种即是完全二叉树

  public static boolean isCBT2(Node head) (
  if (head == null) [
  return true ;
  ]
  return process (head) .isCBT;
  ]
  //对每一棵子树,1是否是满二叉树、是否是完全二叉树、高度|
  public static class Info {
    
     
  public boolean isFull;
  public boolean isCBT ;
  public int height;
    public Info(boolean full, boolean cbt,
    int h) {
    
    
    isFull = full;
    isCBT = cbt;
    height = h;
    ]
	]
  public static Info process(Node X) [
  if (X == null) 1
  return new Info(true, true, 0);
      }
  Info leftInfo = process(X.left);
  Info rightInfo = process(X.right);
  int height = Math . max(leftInfo . height, rightInfo.height) + 1;
  boolean isFull = leftInfo. isFull && rightInfo. isFull && leftInfo.height == rightInfo.height;
      
  boolean isCBT = false;
  if (isFull) f
  isCBT = true;
  ] Ielse (
  if (leftInfo.isCBT && rightInfo.isCBT) {
    
     
    	
    if (leftInfo.isCBT && rightInfo. isFull && leftInfo.height == rightInfo.height+1
    	isCBT = true;
    ]
    if (leftInfo. isFull && rightInfo. isFull && leftInfo. height == rightInfo.height+1
    	isCBT = true;
    ]
    if (leftInfo.isFull && rightInfo.isCBT && leftInfo.height == rightInfo.height
    	isCBT = true;
     }
    }
  }
  return new Info(isFull, isCBT, height);
}

返回a和b的最低公共祖先

给定一-棵二叉树的头节点head,
和另外两个节点a和b。
返回a和b的最低公共祖先

map+set

建立 key 是当前节点, value 是父节点的map

然后将当前节点加入set,然后不断找父节点,直到根节点

将b节点的父节点加入set,每次判断set大小,第一次遇到重复的就是最近 公共祖先

public static Node lowestAncestor1(Node head, Node o1, Node o2) [
  if (head== null) (
  return null;
  }
  HashMap<Node, Node> parentMap = new HashMap<>();
  parentMap. put(head, null);
  filLParentMap(head, parentMap);
    
  HashSet<Node> o1Set = new HashSet<>( );
  Node cur = o1;
  o1Set.add(cur);
  While (parentMap.get(cur) != null) [
  cur = parentMap. get(cur);
  o1Set. add(cur);
  ]
  cur = o2;
  while (!o1Set. contains(cur)) f
  cur = parentMap. get(cur);
  return cur ;
 }
public static void fillParentMap(Node helad, HashMap<Node, Node> parentMap) {
    
      
  if (head.left != null) [
  parentMap . put(head.left, head);
  fillParentMap(head .left, parentMap);
   }
  if (head. right!= null){
    
    
  parentMap. put (head.right, head);
  fillParentMap(head . right, parentMap);
	}
}

递归

列可能

1.o1 和 o2 都不在当前节点上

2.o1 或 o2 只有一个在x 上

3 o1 和 o2 都在x上

​ 1》左右各一个

​ 2》都在左树

​ 3 都在右树

​ 4》x 是o1 ,左树找o2

​ x是o2 右树找o1

public static Node lowestAncestor2(Node head, Node a, Node b) {
    
    
		return process(head, a, b).ans;
	}
 
	public static class Info {
    
    
		public boolean findA;
		public boolean findB;
    // 子树最初交汇点
		public Node ans;
		public Info(boolean fA, boolean fB, Node an) {
    
    
			findA = fA;
			findB = fB;
			ans = an;
		}
	}
 
	public static Info process(Node x, Node a, Node b) {
    
    
		if (x == null) {
    
    
			return new Info(false, false, null);
		}
		Info leftInfo = process(x.left, a, b);
		Info rightInfo = process(x.right, a, b);
		boolean findA = (x == a) || leftInfo.findA || rightInfo.findA;
		boolean findB = (x == b) || leftInfo.findB || rightInfo.findB;
		Node ans = null;
		if (leftInfo.ans != null) {
    
    
			ans = leftInfo.ans;
		} else if (rightInfo.ans != null) {
    
    
			ans = rightInfo.ans;
		} else {
    
    
			if (findA && findB) {
    
    
				ans = x;
			}
		}
		return new Info(findA, findB, ans);
	}

猜你喜欢

转载自blog.csdn.net/qq_41852212/article/details/121031653