牛客 5 —— 二叉树
牛客 5 —— 二叉树
二叉树的遍历
二叉树的前序、中序、后序遍历 — 递归
//前序——递归
public static void previsit(Node node) {
System.out.println(node);
previsit(node.left);
previsit(node.right);
}
//中序——递归
public static void invisit(Node node) {
invisit(node.left);
System.out.println(node);
invisit(node.right);
}
//后序——递归
public static void postvisit(Node node) {
postvisit(node.left);
postvisit(node.right);
System.out.println(node);
}
二叉树的前序、中序、后序遍历 — 非递归
中序:当前节点为空,从栈拿一个,打印,向右移动。当前节点不为空,压栈,往左移动。
后序:两个栈,从前序改良。有一种很棒的技巧,就是后序遍历,可以从前序遍历多加一个栈实现。
//前序——非递归
public static void Fprevisit(Node node) {
if(node != null) {
Stack<Node> stack = new Stack<Node>();
stack.add(node);
while(!stack.empty()) {
node = stack.pop();
visit(node);
if(node.right != null) {
stack.push(node.right);
}
if(node.left != null) {
stack.push(node.left);
}
}
}
}
//中序——非递归
public static void Finvisit(Node node) {
if(node != null) {
Stack<Node> stack = new Stack<Node>();
while(!stack.empty() || node != null) {
//这里有两个继续循环的条件
//1、栈不为空——还有元素没有访问到
//2、当前节点node不为空——说明没有找到最左边。
if(node != null) {
stack.push(node);
node = node.left;
}else {
node = stack.pop();
visit(node);
node = node.right;
}
}
}
}
//后续第一种实现——非递归——双栈
public static void Fpostvist1(Node node) {
if (node != null) {
Stack<Node> s1 = new Stack<Node>();
Stack<Node> s2 = new Stack<Node>();
s1.push(node);
while(!s1.empty()) {
node = s1.pop();
s2.push(node);
if(node.left != null) {
s1.push(node.left);
}
if(node.right != null) {
s1.push(node.right);
}
}
while(!s2.empty()) {
visit(s2.pop());
}
}
}
//后续第二种实现——非递归
public static void Fpostvist2(Node node) {
if(node != null) {
Stack<Node> stack = new Stack<Node>();
stack.push(node);
Node c = null;
while(!stack.empty()) {
c = stack.peek();
if (c.left != null && node != c.left && node != c.right) {
stack.push(c.left);
} else if (c.right != null && node != c.right) {
stack.push(c.right);
} else {
System.out.print(stack.pop().value + " ");
node = c;
}
}
}
}
直观打印树
这个是左神提供给我们作为测试使用的,很直观的打印出树的形状。
public class Code_02_PrintBinaryTree {
public static class Node {
public int value;
public Node left;
public Node right;
public Node(int data) {
this.value = data;
}
}
public static void printTree(Node head) {
System.out.println("Binary Tree:");
printInOrder(head, 0, "H", 17);
System.out.println();
}
public static void printInOrder(Node head, int height, String to, int len) {
if (head == null) {
return;
}
printInOrder(head.right, height + 1, "v", len);
String val = to + head.value + to;
int lenM = val.length();
int lenL = (len - lenM) / 2;
int lenR = len - lenM - lenL;
val = getSpace(lenL) + val + getSpace(lenR);
System.out.println(getSpace(height * len) + val);
printInOrder(head.left, height + 1, "^", len);
}
public static String getSpace(int num) {
String space = " ";
StringBuffer buf = new StringBuffer("");
for (int i = 0; i < num; i++) {
buf.append(space);
}
return buf.toString();
}
public static void main(String[] args) {
Node head = new Node(1);
head.left = new Node(-222222222);
head.right = new Node(3);
head.left.left = new Node(Integer.MIN_VALUE);
head.right.left = new Node(55555555);
head.right.right = new Node(66);
head.left.left.right = new Node(777);
printTree(head);
head = new Node(1);
head.left = new Node(2);
head.right = new Node(3);
head.left.left = new Node(4);
head.right.left = new Node(5);
head.right.right = new Node(6);
head.left.left.right = new Node(7);
printTree(head);
head = new Node(1);
head.left = new Node(1);
head.right = new Node(1);
head.left.left = new Node(1);
head.right.left = new Node(1);
head.right.right = new Node(1);
head.left.left.right = new Node(1);
printTree(head);
}
}
后继节点
如果节点有右子树,那么:他的后继节点就是右子树上最左边的节点。
如果节点没有右子树,那么:通过该节点的父指针,找到该节点的父亲,一直往上,直到某一个节点是他父节点的左孩子停止。
//树的节点类
public static class Node {
public int value;
public Node left;
public Node right;
public Node parent;
public Node(int data) {
this.value = data;
}
}
public static Node getNext(Node node) {
if(node == null) {
return null;
}
//如果树的右子树不为空,则后继节点为右子树最左边的节点
if(node.right != null) {
Node c = node;
while(c.left != null) {
c = c.left;
}
return c;
}else {
//如果节点的右子树为空,则找父节点,直到该节点的父亲的左孩子是该节点。
Node parent = node.parent;
while(parent != null && parent.left != node) {
node = parent;
parent = node.parent;
}
return parent;
}
}
二叉树的序列化与反序列化
可以用递归实现。
平衡树
1、左树是不是平衡树,如果左树不是平衡树,接下来都不用判断了,肯定不是平衡树。
2、同理,判断右树是否平衡
3、在左树平衡的情况下,拿到左树的高度
4、在右树平衡的情况下拿到右树的高度
5、比较左右子树的高度差
package niuke5;
public class pingheng {
// 节点类
public static class Node {
public int value;
public Node left;
public Node right;
public Node(int data) {
this.value = data;
}
}
// 返回类
public static class ReturnData {
public boolean isB;
public int hight;
public ReturnData(boolean isB, int hight) {
this.hight = hight;
this.isB = isB;
}
}
public static ReturnData judge(Node head) {
// 如果头指针为空,说明平衡
if (head == null) {
return new ReturnData(true, 0);
}
// 左
ReturnData leftData = judge(head.left);
if (!leftData.isB) {
return new ReturnData(false, 0);
}
// 右
ReturnData rightData = judge(head.right);
if (!rightData.isB) {
return new ReturnData(false, 0);
}
// 高度差异
if (Math.abs(rightData.hight - leftData.hight) > 1) {
return new ReturnData(false, 0);
}
// 如果是平衡树
return new ReturnData(true, Math.max(rightData.hight, leftData.hight) + 1);
}
}
搜索二叉树、完全二叉树
搜索二叉树:中序遍历的情况下,是升序的!!!! 所以,我们只要会写中序遍历,在打印数的时改成比较数字大小即可。
完全二叉树:二叉树按层遍历。1、如果一个节点有右孩子没有左孩子,一定不是二叉树;2、如果一个节点,不是左右两个孩子都全,也即是有左没右,或者左右都没有,其后面的所有节点都必须是叶子节点。
public class judge {
// 判断是否是搜索二叉树、完全二叉树
// 搜索二叉树:中序遍历下,是升序的
//完全二叉树:二叉树按层遍历。
//1、如果一个节点有右孩子没有左孩子,一定不是二叉树;
//2、如果一个节点,不是左右两个孩子都全,
//也即是有左没右,或者左右都没有,其后面的所有节点都必须是叶子节点。
// 节点类
public static class Node {
public int value;
public Node left;
public Node right;
public Node(int data) {
this.value = data;
}
}
//判断是否是搜索二叉树
public static boolean judgeTree1(Node node) {
if (node != null) {
Stack<Node> stack = new Stack<Node>();
while (!stack.empty() || node != null) {
// 这里有两个继续循环的条件
// 1、栈不为空——还有元素没有访问到
// 2、当前节点node不为空——说明没有找到最左边。
if (node != null) {
stack.push(node);
node = node.left;
} else {
node = stack.pop();
// visit(node) —— 原来是visit的时候这里改成比较大小
if (node.value > stack.peek().value) {
return false;
}
node = node.right;
}
}
}
return true;
}
public static boolean judgeTree2(Node head) {
if(head == null) {
return true;
}
Queue<Node> queue = new LinkedList<Node>();
boolean leaf = false;
Node l = null;
Node r = null;
queue.offer(head);
while(!queue.isEmpty()) {
head = queue.poll();
l = head.left;
r = head.right;
//1、该节点是叶子节点,且左右子节点都不为空,
//或者左不为空,并且作为空
if((leaf && (l != null || r != null)) || (l == null && r != null)) {
return false;
}
//2、该节点有左节点
if(l != null) {
queue.offer(l);
}
//3、该节点有右节点
if(r != null) {
queue.offer(r);
}
//4、该节点有有节点,没有左节点
if(r != null && l == null) {
leaf = true;
}
}
return true;
}
}
完全二叉树节点个数
递归递归!!!
终点看图!!!
完全二叉树:最后的叶子节点必须先摆满左边的,再接右边的。
所以,这里要看节点k,也就是头节点的右子树最左边的节点,看看他的高度是否跟左子树的最左边的节点的高度相同。如果高度相同,就说明左子树一定是全部都满的,然后再递归判断右子树即可。
package niuke5;
public class number_of_node {
public static class Node {
public int value;
public Node left;
public Node right;
public Node(int data) {
this.value = data;
}
}
public static int nodeNum(Node head) {
if (head == null) {
return 0;
}
return bs(head, 1, mostLeftLevel(head, 1));
}
public static int bs(Node node, int l, int h) {
if (l == h) {
return 1;
}
if (mostLeftLevel(node.right, l + 1) == h) {
return (1 << (h - l)) + bs(node.right, l + 1, h);
} else {
return (1 << (h - l - 1)) + bs(node.left, l + 1, h);
}
}
public static int mostLeftLevel(Node node, int level) {
while (node != null) {
level++;
node = node.left;
}
return level - 1;
}
}