二叉树
class Tree {
Node left;
Node right;
int value;
//递归
public static void f(Tree head) {
if(head == null) {
reutrn;
}
f(head.left);
f(head.right);
}
//遍历结果
//1,2,4,4,4,2,5,5,5,2,1,3,6,6,6,3,7,7,7,3,1
}
遍历
先序遍历
先序遍历指的是所有子树中先打印头节点、在左节点、最后右节点
头左右::1,2,4,5,3,6,7(利用递归顺第一次出现的打印)
递归方式:
public static void f(Tree head) {
if(head == null) {
reutrn;
}
System.out.println(head.value);
f(head.left);
f(head.right);
}
非递归压栈形式可实现:
每次
- 从栈中弹出一个节点cur
- 打印
- 先右后左压入(如果有)
- 重复1
public static void f(Tree head) {
if(head == null) {
return;
}
Stack<Node> stack = new Stack();
stack.add(head);
while(!stack.isEmpty()){
Node head = stack.pop();
System.out.print(head.value);
if(head.right != null) {
stack.push(head.right);
}
if(head.left!= null) {
stack.push(head.left);
}
}
}
中序遍历
左头右:4,2,5,1,6,3,7(利用递归顺第二次出现的打印)
public static void f(Tree head) {
if(head == null) {
reutrn;
}
f(head.left);
System.out.println(head.value);
f(head.right);
}
非递归:
- 先将所有子树左节点放入栈中
- 弹出打印
- 弹出时判断是否存在右子树
- 存在压入右节点
public void inOrderUnRecur(Node head) {
if (head == null) {
return;
}
Stack<Node> stack = new Stack<Node>();
while (!stack.isEmpty() || head != null) {
if (head != null) {
stack.push(head);
head = head.left;
} else {
head = stack.pop();
System.out.println(head.value);
head = head.right;
}
}
}
后序遍历
左右头:4,5,2,6,7,3,1(利用递归顺第三次出现的打印)
public static void f(Tree head) {
if(head == null) {
reutrn;
}
f(head.left);
f(head.right);
System.out.println(head.value);
}
非递归:
- 申请1个栈、1个收集栈
- 放入头cur 弹出放入收集栈
- 先左在右放入(如果右)
- 弹出
- 遍历收集栈
public static void f(Tree head) {
if(head == null) {
return;
}
Stack<Node> s1= new Stack();
Stack<Node> s2 = new Stack();
stack.push(head);
while(!s1.isEmpty()){
head = s1.pop();
s2.push(head);
if(head.left!= null) {
stack.push(head.right);
}
if(head.right != null) {
stack.push(head.right);
}
}
while(!s2.isEmpty()){
System.out.println(s2.pop().value);
}
}
深度遍历
二叉树的深度遍历即为先序遍历。
广度遍历(宽度遍历)
用队列先左在右放入取出即可
public static void f(Node head) {
if(head == null) {
return;
}
Queue<Node> queue = new LinkedList<Node>();
queue.add(head);
while(!queue.isEmpty()){
head= queue.poll();
System.out.print(head.value);
if(head.left!= null) {
queue.add(head.left);
}
if(head.right!= null) {
queue.add(head.right);
}
}
}
计算当前树最大宽度是多少
public static int w(Node head) {
if(head == null) {
return 0;
}
Queue<TreeNode> queue = new LinkedList<TreeNode>();
queue.add(head);
//记录节点个数
Map<TreeNode, Integer> levelMap = new HashMap<>();
levelMap.put(head,1);
//当前最大个数
int max = Integer.MIN_VALUE;
//当前层数
int curLevel = 0;
//当前节点个数统计
int curNodeNum = 0;
while(!queue.isEmpty()){
head = queue.poll();
if (levelMap.get(head) == curLevel) {
curNodeNum ++;
} else {
max = Math.max(max, curNodeNum);
curLevel ++;
curNodeNum = 1;
}
if(head.left!= null) {
levelMap.put(head.left, curLevel + 1);
queue.add(head.left);
}
if(head.right!= null) {
levelMap.put(head.right, curLevel + 1);
queue.add(head.right);
}
}
return Math.max(max, curNodeNum);
}
搜索二叉树
所有子树左节点都比右小
判断是否为搜索二叉树
中序遍历 判断是否为依次升序、升序即为搜索二叉树
public static boolean isBST(Tree head) {
int preValue = 0;
if(head == null) {
reutrn true;
}
boolean result= isBST(head.left);
if(!result){
return false;
}
if(head.value <= preValue) {
return false;
}else{
preValue = head.value;
}
return isBST(head.right);
}
判断完全二叉树
- 进行广度遍历并记录
- 如果出现有右孩子无左孩子直接false
- 满足2条件如果出现第一个左右不全必须为叶子节点
public Boolean isCBT(Node head) {
Queue<Node> queue = new LinkedList<Node>();
queue.add(head);
Node l = null;
Node r = null;
//是否遇到左右孩子不双全
boolean leaf = false;
while (!queue.isEmpty()) {
Node node = queue.poll();
l = node.left;
r = node.right;
if ((l == null && r != null) ||(leaf && (l != null || r != null))) {
return false;
}
if (node.left != null) {
queue.add(node.left);
}
if (node.right != null) {
queue.add(node.right);
}
if (l== null || r == null) {
leaf = true;
}
}
return true;
}
满二叉树
概念:
- 最大深度l
- 节点个数n
- 当满足n = 2l-1时该树必为满二叉树
@Data
@AllArgsConstructor
@NoArgsConstructor
public class ReturnData {
private int nodeNum;
private int height;
}
public boolean isF(Node head) {
if (head == null) {
return true;
}
ReturnData data = isFull(head);
return data.nodeNum == (1 << data.height -1);
}
public ReturnData isFull(Node x) {
if (x == null) {
return new ReturnData(0,0);
}
ReturnData left = isFull(x.left);
ReturnData right = isFull(x.right);
int height = Math.max(left.height, right.height) +1;
int nodes = left.nodeNum + right.nodeNum + 1;
return new ReturnData(nodes, height);
}
平衡二叉树
- 概念:
-
对于任何二叉树而言他的左树和右树高度差不超过1的树
算法判断:
- 左子树要是平衡二叉树
- 右子树要是平衡二叉树
- |左高-右高|<=1
以上成立即为平衡二叉树
@Data
@AllArgsConstructor
public class ReturnType {
private int height;
private boolean isBalanced;
}
public boolean isBalanced(Node head) {
return process(head).isBalanced;
}
public ReturnType process(Node x) {
if (x == null) {
return new ReturnType(0, true);
}
ReturnType leftProcess = process(x.left);
ReturnType rightProcess = process(x.right);
//计算当前高度 子树最大高度加上自身1 即为当前高度
int height = Math.max(leftProcess.height, rightProcess.height) +1;
//判断是否为平衡树并高度小于等于1
boolean isBalanced = leftProcess.isBalanced && rightProcess.isBalanced && Math.abs( leftProcess.height - rightProcess.height) < 2;
return new ReturnType(height, isBalanced);
}
//搜索二叉树
@Data
@AllArgsConstructor
public class ReturnData {
private boolean isSearch;
private int max;
private int min;
}
public boolean isSearch(Node head) {
return isIBS(head).isSearch;
}
public ReturnData isIBS(Node x) {
if (x == null) {
return null;
}
ReturnData left = isIBS(x.left);
ReturnData right = isIBS(x.right);
int max = x.value;
int min = x.value;
boolean isBST = true;
if (left != null) {
max = Math.max(left.max, max);
min = Math.max(left.min, min);
}
if (right != null) {
max = Math.max(right.max, max);
min = Math.max(right.min, min);
}
if (left != null && (!left.isSearch || left.max >= x.value)) {
isBST = false;
}
if (right != null && (!right.isSearch || right.min <= x.value)) {
isBST = false;
}
return new ReturnData(isBST, max, min);
}
二叉树算法中套路总结
- 递归形式
- 思考所有可能性
- 默认可以拿到左右树所有信息
例子:平衡二叉树
可能性:假设x为头的二叉树
- 左树要为平衡二叉树
- 右树要为平衡二叉树
- 左右高度差<=1
- 都满足则为平衡