二叉树基本算法以及题目

152 阅读5分钟

1.二叉树基本算法

1.1判断是否是完全二叉树?(IsCBT)

通过队列的方式 层式遍历; 当节点的右节点存在左节点不存在那么不是完全二叉树; 当一个节点为叶结点的时候 那么后面所有的节点都必须要为叶节点

   if (head == null) {
      return true;
   }
   LinkedList<Node> queue = new LinkedList<>();
   // 是否遇到过左右两个孩子不双全的节点
   boolean leaf = false;
   Node l = null;
   Node r = null;
   queue.add(head);
   while (!queue.isEmpty()) {
      head = queue.poll();
      l = 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 == null) {
         leaf = true;
      }
   }
   return true;
}

	public static class Node {
		public int value;
		public Node left;
		public Node right;

		public Node(int data) {
			this.value = data;
		}
	}

2.二叉树递归套路深度实践

二叉树的递归套路

1)假设以X节点为头,假设可以向X左树和X右树要任何信息

2)在上一步的假设下,讨论以X为头节点的树,得到答案的可能性(最重要)

3)列出所有可能性后,确定到底需要向左树和右树要什么样的信息

4)把左树信息和右树信息求全集,就是任何一棵子树都需要返回的信息S

5)递归函数都返回S,每一棵子树都这么要求

6)写代码,在代码中考虑如何把左树的信息和右树信息整合出整棵树的信息

2.1判断二叉树是不是平衡二叉树(IsBalanced)

平衡二叉树是指每一颗子树的左树最高度和右数最高度相差绝对值不超过1 得到平衡需要满足的条件:1.左数和右数都是平衡树 2.左数和右数的高度差不超过1

   if(x == null) {
      return new Info(true, 0);
   }
   Info leftInfo = process(x.left);
   Info rightInfo = process(x.right);
   int height = Math.max(leftInfo.height, rightInfo.height)  + 1;
   boolean isBalanced = true;
   if(!leftInfo.isBalanced) {
      isBalanced = false;
   }
   if(!rightInfo.isBalanced) {
      isBalanced = false;
   }
   if(Math.abs(leftInfo.height - rightInfo.height) > 1) {
      isBalanced = false;
   }
   return new Info(isBalanced, height);
}

public static class Info{
		public boolean isBalanced;
		public int height;
		
		public Info(boolean i, int h) {
			isBalanced = i;
			height = h;
		}
	}

2.2判断二叉树是否是搜索二叉树(siBst)

搜索二叉树是指每一个子树的左数节点值都是小于该树节点值,右树节点值都大于该树节点值。 需要获取信息1.左数是搜索二叉树 2.右数是搜索二叉树 3.左树的最大值,右树的最小值。 所以要拿到每个节点的最大值和最小值

   if (x == null) {
      return null;
   }
   Info leftInfo = process(x.left);
   Info rightInfo = process(x.right);
   int max = x.value;
   if (leftInfo != null) {
      max = Math.max(max, leftInfo.max);
   }
   if (rightInfo != null) {
      max = Math.max(max, rightInfo.max);
   }
   int min = x.value;
   if (leftInfo != null) {
      min = Math.min(min, leftInfo.min);
   }
   if (rightInfo != null) {
      min = Math.min(min, rightInfo.min);
   }
   boolean isBST = true;
   if (leftInfo != null && !leftInfo.isBST) {
      isBST = false;
   }
   if (rightInfo != null && !rightInfo.isBST) {
      isBST = false;
   }
   if (leftInfo != null && leftInfo.max >= x.value) {
      isBST = false;
   }
   if (rightInfo != null && rightInfo.min <= x.value) {
      isBST = false;
   }
   return new Info(isBST, max, min);
}

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

假设树节点为x 若与x无关 也就是不经过x节点:1.获取左树最大距离 2右树最大距离 若与x有关 也就是经过x节点:3.左树的高度 +右树的高度 这三种情况最大距离的比较

   public int maxDistance;
   public int height;

   public Info(int dis, int h) {
      maxDistance = dis;
      height = h;
   }
}

public static Info process(Node X) {
   if (X == null) {
      return new Info(0, 0);
   }
   Info leftInfo = process(X.left);
   Info rightInfo = process(X.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);
}

2.4 是否是满二叉树?(sFull)

满二叉树节点数是2的n 次方-1;需要获取每个节点的两个信息:高度h 和 节点数 获取左右两树节点数是否满足这个条件就ok;

// 收集整棵树的高度h,和节点数n
// 只有满二叉树满足 : 2 ^ h - 1 == n
public static boolean isFull1(Node head) {
   if (head == null) {
      return true;
   }
   Info1 all = process1(head);
   return (1 << all.height) - 1 == all.nodes;
}

public static class Info1 {
   public int height;
   public int nodes;

   public Info1(int h, int n) {
      height = h;
      nodes = n;
   }
}

public static Info1 process1(Node head) {
   if (head == null) {
      return new Info1(0, 0);
   }
   Info1 leftInfo = process1(head.left);
   Info1 rightInfo = process1(head.right);
   int height = Math.max(leftInfo.height, rightInfo.height) + 1;
   int nodes = leftInfo.nodes + rightInfo.nodes + 1;
   return new Info1(height, nodes);
}
// 收集子树是否是满二叉树
// 收集子树的高度
// 左树满 && 右树满 && 左右树高度一样 -> 整棵树是满的
public static boolean isFull2(Node head) {
   if (head == null) {
      return true;
   }
   return process2(head).isFull;
}

public static class Info2 {
   public boolean isFull;
   public int height;

   public Info2(boolean f, int h) {
      isFull = f;
      height = h;
   }
}

public static Info2 process2(Node h) {
   if (h == null) {
      return new Info2(true, 0);
   }
   Info2 leftInfo = process2(h.left);
   Info2 rightInfo = process2(h.right);
   boolean isFull = leftInfo.isFull && rightInfo.isFull && leftInfo.height == rightInfo.height;
   int height = Math.max(leftInfo.height, rightInfo.height) + 1;
   return new Info2(isFull, height);
}

2.5给定一棵二叉树的头节点head,返回这颗二叉树中最大的二叉搜索子树的大小(MaxSubBSTSize)

假设头节点为x 不经过x节点: 则 获取左树的最大BST和右树最大BST 经过x节点 也就是该树是BST:1.左树和右树是搜索二叉树 2.左树最大值 3.右树最小值 4.左右树的size(节点个数)

若maxBSTsize和size相等说明该树是BST。

public static int maxSubBSTSize2(Node head) {
		if(head == null) {
			return 0;
		}
		return process(head).maxBSTSubtreeSize;
	}

	public static class Info {
		public int maxBSTSubtreeSize;
		public int allSize;
		public int max;
		public int min;

		public Info(int m, int a, int ma, int mi) {
			maxBSTSubtreeSize = m;
			allSize = a;
			max = ma;
			min = mi;
		}
	}

	public static Info process(Node x) {
		if (x == null) {
			return null;
		}
		Info leftInfo = process(x.left);
		Info rightInfo = process(x.right);
		int max = x.value;
		int min = x.value;
		int allSize = 1;
		if (leftInfo != null) {
			max = Math.max(leftInfo.max, max);
			min = Math.min(leftInfo.min, min);
			allSize += leftInfo.allSize;
		}
		if (rightInfo != null) {
			max = Math.max(rightInfo.max, max);
			min = Math.min(rightInfo.min, min);
			allSize += rightInfo.allSize;
		}
		int p1 = -1;
		if (leftInfo != null) {
			p1 = leftInfo.maxBSTSubtreeSize;
		}
		int p2 = -1;
		if (rightInfo != null) {
			p2 = rightInfo.maxBSTSubtreeSize;
		}
		int p3 = -1;
		boolean leftBST = leftInfo == null ? true : (leftInfo.maxBSTSubtreeSize == leftInfo.allSize);
		boolean rightBST = rightInfo == null ? true : (rightInfo.maxBSTSubtreeSize == rightInfo.allSize);
		if (leftBST && rightBST) {
			boolean leftMaxLessX = leftInfo == null ? true : (leftInfo.max < x.value);
			boolean rightMinMoreX = rightInfo == null ? true : (x.value < rightInfo.min);
			if (leftMaxLessX && rightMinMoreX) {
				int leftSize = leftInfo == null ? 0 : leftInfo.allSize;
				int rightSize = rightInfo == null ? 0 : rightInfo.allSize;
				p3 = leftSize + rightSize + 1;
			}
		}
		return new Info(Math.max(p1, Math.max(p2, p3)), allSize, max, min);
	}

2.6.判断二叉树是否是完全二叉树 如何用递归套路来解这个题

列出几种情况:

image.png

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) {
      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 (leftInfo.isFull && rightInfo.isFull && leftInfo.height == rightInfo.height) {
      isCBT = true;
   } else if (leftInfo.isCBT && rightInfo.isFull && leftInfo.height == rightInfo.height + 1) {
      isCBT = true;
   } else if (leftInfo.isFull && rightInfo.isFull && leftInfo.height == rightInfo.height + 1) {
      isCBT = true;
   } else if (leftInfo.isFull && rightInfo.isCBT && leftInfo.height == rightInfo.height) {
      isCBT = true;
   }
   return new Info(isFull, isCBT, height);
}

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

该题与上面2.5思路一样

// 每一棵子树
public 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 != null) {
      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);
}

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

image.png

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);
}
public static class Node {
   public int value;
   public Node left;
   public Node right;
​
   public Node(int data) {
      this.value = data;
   }
}
​
public static Node lowestAncestor1(Node head, Node o1, Node o2) {
   if (head == null) {
      return null;
   }
   // key的父节点是value
   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)) {
      cur = parentMap.get(cur);
   }
   return cur;
}
​
public static void fillParentMap(Node head, 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);
   }
}

2.9派对的最大快乐值

员工信息的定义如下:

class Employee {

public int happy; // 这名员工可以带来的快乐值

List subordinates; // 这名员工有哪些直接下级

}

派对的最大快乐值

公司的每个员工都符合 Employee 类的描述。整个公司的人员结构可以看作是一棵标准的、 没有环的多叉树。树的头节点是公司唯一的老板。除老板之外的每个员工都有唯一的直接上级。 叶节点是没有任何下属的基层员工(subordinates列表为空),除基层员工外,每个员工都有一个或多个直接下级。

这个公司现在要办party,你可以决定哪些员工来,哪些员工不来,规则:

1.如果某个员工来了,那么这个员工的所有直接下级都不能来

2.派对的整体快乐值是所有到场员工快乐值的累加

3.你的目标是让派对的整体快乐值尽量大

给定一棵多叉树的头节点boss,请返回派对的最大快乐值。

image-20220308201357736

所以每个节点需要拿到来的情况下的最大收益和不来情况下的最大收益。

public static class Info {
   public int no;
   public int yes;
​
   public Info(int n, int y) {
      no = n;
      yes = y;
   }
}
​
public static Info process(Employee x) {
   if (x == null) {
      return new Info(0, 0);
   }
   int no = 0;
   int yes = x.happy;
   for (Employee next : x.nexts) {
      Info nextInfo = process(next);
      no += Math.max(nextInfo.no, nextInfo.yes);
      yes += nextInfo.no;
​
   }
   return new Info(no, yes);
}

2.9派对的最大快乐值

员工信息的定义如下:

class Employee {

public int happy; // 这名员工可以带来的快乐值

List subordinates; // 这名员工有哪些直接下级

}

派对的最大快乐值

公司的每个员工都符合 Employee 类的描述。整个公司的人员结构可以看作是一棵标准的、 没有环的多叉树。树的头节点是公司唯一的老板。除老板之外的每个员工都有唯一的直接上级。 叶节点是没有任何下属的基层员工(subordinates列表为空),除基层员工外,每个员工都有一个或多个直接下级。

这个公司现在要办party,你可以决定哪些员工来,哪些员工不来,规则:

1.如果某个员工来了,那么这个员工的所有直接下级都不能来

2.派对的整体快乐值是所有到场员工快乐值的累加

3.你的目标是让派对的整体快乐值尽量大

给定一棵多叉树的头节点boss,请返回派对的最大快乐值。

image.png

所以每个节点需要拿到来的情况下的最大收益和不来情况下的最大收益。

public static class Info {
   public int no;
   public int yes;

   public Info(int n, int y) {
      no = n;
      yes = y;
   }
}

public static Info process(Employee x) {
   if (x == null) {
      return new Info(0, 0);
   }
   int no = 0;
   int yes = x.happy;
   for (Employee next : x.nexts) {
      Info nextInfo = process(next);
      no += Math.max(nextInfo.no, nextInfo.yes);
      yes += nextInfo.no;

   }
   return new Info(no, yes);
}