二叉树 :
二叉树结构:
class Node {
V value;
Node left;
Node right;
}
什么是子树?
【子树】说明:从某个头节点开始,拿到它下面的所有节点 才 称为 子树。
如:下图中的C-D ,就不是一个子树,因为它缺少了下面的E节点。但B节点,也是一个子树。
二叉树的 先序、中序、后序。
先序:任何子树的处理顺序都是,先头结点、再左子树、然后右子树。 即:头、左、右
中序:任何子树的处理顺序都是,先左子树、再头节点、然后右子树。即:左、头、右
后序:任何子树的处理顺序都是,先左子树、再右子树、然后头节点。即:左、右、头
二叉树 遍历说明:
上图中的子树总共有7个:
1-2-3-4-5-6-7
2-4-5
3-6-7
4
5
6
7
上图二叉树遍历:对每个子树来说,都要符合指定的遍历规则。
先序遍历(头左右):1-2-4-5-3-6-7 ,
中序遍历(左头右):4-2-5-1-6-3-7
后序遍历(左右头):4-5-2-6-7-3-1
二叉树代码实现
package com.example.demo.javatest.tree.binarytree;
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;
import java.util.Queue;
import java.util.Stack;
import com.alibaba.fastjson.JSONObject;
/**
* 二叉树
* @author likui
* @since 2021年4月23日 上午9:14:38
* @version 1.0
*
*/
public class BinaryTreeTest {
/**
*
* @Description: 二叉树节点实体类
* @ClassName: Node
* @version V1.0
* @author: likui
* @date: 2021年4月23日 上午9:16:52
*
*/
public static class Node {
private int value;// 头节点
private Node left;// 左树
private Node right;// 右树
public Node(int v) {
value = v;
}
public int getValue() {
return value;
}
public void setValue(int value) {
this.value = value;
}
public Node getLeft() {
return left;
}
public void setLeft(Node left) {
this.left = left;
}
public Node getRight() {
return right;
}
public void setRight(Node right) {
this.right = right;
}
}
/**
* @Title: f
* @Description: 二叉树遍历,递归实现
* @param: @param head
* @return: void
* @throws
*/
public static void f(Node head) {
if (head == null) {
return ;
}
f(head.getLeft());
f(head.getRight());
}
/**
* @Title: before
* @Description: 先序遍历(头左右) ,递归实现
* @param: @param head
* @return: void
* @throws
*/
public static void before(Node head) {
if (head == null) {
return ;
}
System.out.print(head.getValue() + "->");
before(head.getLeft());
before(head.getRight());
}
/**
* @Title: preOrderTraverse
* @Description: 先序遍历(头左右) ,非递归实现
* @param: head
* @return: void
* @throws
*
* 弹出则打印;
* 如有右,压入右。
* 如有左,压入左。
*/
public static void preOrderTraverse(Node head) {
// Stack是栈;特点是:先进后出。
if (head != null) {
Stack<Node> stack = new Stack<>();
// 把对象 压入堆栈顶部。
stack.push(head);
while (!stack.empty()) {
// pop 移除堆栈顶部的对象,并作为此函数的值返回该对象。
head = stack.pop();
System.out.print(head.getValue() + "->");
// 把右树对象压入堆栈顶部。
if (head.getRight() != null) {
stack.push(head.getRight());
}
// 把左树对象压入堆栈顶部。
if (head.getLeft() != null) {
stack.push(head.getLeft());
}
}
}
}
/**
* @Title: middle
* @Description: 中序遍历 (左头右),递归实现
* @param: @param head
* @return: void
* @throws
*/
public static void middle(Node head) {
if (head == null) {
return ;
}
middle(head.getLeft());
System.out.print(head.getValue() + "->");
middle(head.getRight());
}
/**
* @Title: inOrderTraverse
* @Description: 中序遍历 (左头右),非递归实现
* @param: root
* @return: void
* @throws
* 1、整条左边界依次入栈;
* 2、步骤1无法再继续,弹出节点,然后在弹出节点的右树上,继续执行步骤1
*
*/
public static void inOrderTraverse(Node head) {
if (head != null) {
Stack<Node> stack = new Stack<>();
while (head != null || !stack.isEmpty()) {
if (head != null) {
stack.push(head);
head = head.getLeft();
} else {
head = stack.pop();
System.out.print(head.getValue() + "->");
head = head.getRight();
}
}
}
}
/**
* @Title: after
* @Description: 后序遍历 (左右头),递归实现
* @param: head
* @return: void
* @throws
*/
public static void after(Node head) {
if (head == null) {
return ;
}
after(head.getLeft());
after(head.getRight());
System.out.print(head.getValue() + "->");
}
/**
* @Title: postOrderTraverse
* @Description: 后序遍历 (左右头),非递归实现
* @param: root
* @return: void
* @throws
*/
public static void postOrderTraverse(Node h) {
if (h != null) {
Stack<Node> stack = new Stack<>();
stack.push(h);
Node c = null;
while (!stack.empty()) {
// 查看堆栈顶部的对象,但不从堆栈中移除它。
c = stack.peek();
if (c.getLeft() != null && h != c.getLeft() && h != c.getRight()) {
// c的左节点不为空,并且上次打印的节点不是c 的左节点,也不是c的右节点,即:为新节点
stack.push(c.getLeft());
} else if (c.getRight() != null && h != c.getRight()) {
// c的右节点不为空,并且 上次打印的节点不是 c的右节点,即 c的右节点还没处理
stack.push(c.getRight());
} else {
// c 的左树、右树均已处理,打印头节点
System.out.print(stack.pop().getValue() + "->");
// h 跟踪的是上次打印的节点
h = c;
}
}
}
}
/**
* @Title: laywerTraverse
* @Description: 层序遍历,这个是最符合常规思维的遍历方式,
* 从上往下,一层一层的从左往右遍历,此处结果为1-2-3-4-5-6-7-8-9
* @param: @param node
* @return: void
* @throws
*/
public static void laywerTraverse(Node node){
if(node == null) {
return;
}
Queue<Node> queue = new LinkedList<>();
queue.add(node);
Node currentNode;
while (!queue.isEmpty()){
currentNode = queue.poll();
System.out.print(currentNode.getValue()+"->");
if(currentNode.getLeft() != null){
queue.add(currentNode.getLeft());
}
if(currentNode.getRight() != null){
queue.add(currentNode.getRight());
}
}
System.out.println();
}
// 先序方式序列化
public static Queue<String> preSerial(Node head) {
Queue<String> queue = new LinkedList<>();
beforeSerial(head,queue);
return queue;
}
// 先序方式序列化
private static void beforeSerial(Node head, Queue<String> queue) {
if (head == null) {
queue.add(null);
} else {
queue.add(String.valueOf(head.getValue()));
beforeSerial(head.getLeft(), queue);
beforeSerial(head.getRight(), queue);
}
}
// 通过先序队列构建二叉树
public static Node buildByBeforeQueue(Queue<String> beforeList) {
if (beforeList == null || beforeList.size() == 0) {
return null;
}
return beforeBuild(beforeList);
}
// 通过先序队列构建二叉树
private static Node beforeBuild(Queue<String> beforeList) {
String value = beforeList.poll();
if (value == null) {
return null;
}
Node head = new Node(Integer.valueOf(value));
head.setLeft(beforeBuild(beforeList));
head.setRight(beforeBuild(beforeList));
return head;
}
// 中序方式序列化
private static void middleSerial(Node head, Queue<String> queue) {
if (head == null) {
queue.add(null);
} else {
middleSerial(head.getLeft(), queue);
queue.add(String.valueOf(head.getValue()));
middleSerial(head.getRight(), queue);
}
}
// 后序方式序列化
private static void afterSerial(Node head, Queue<String> queue) {
if (head == null) {
queue.add(null);
} else {
afterSerial(head.getLeft(), queue);
afterSerial(head.getRight(), queue);
queue.add(String.valueOf(head.getValue()));
}
}
// 层级序列化
public Queue<String> levelSerialize(Node head) {
Queue<String> result = new LinkedList<>();
if (head == null) {
result.add(null);
return result;
}
result.add(String.valueOf(head.getValue()));
Queue<Node> queue = new LinkedList<>();
queue.add(head);
while (!queue.isEmpty()) {
head = queue.poll();
if (head.getLeft() != null) {
result.add(String.valueOf(head.getLeft().getValue()));
queue.add(head.getLeft());
} else {
result.add(null);
}
if (head.getRight() != null) {
result.add(String.valueOf(head.getRight().getValue()));
queue.add(head.getRight());
} else {
result.add(null);
}
}
return result;
}
// 层级反序列化
public Node levelBuild(Queue<String> levelList) {
if (levelList == null || levelList.isEmpty()) {
return null;
}
Node head = generateNode(levelList.poll());
Queue<Node> queue = new LinkedList<>();
if (head != null) {
queue.add(head);
}
Node node = null;
while (!queue.isEmpty()) {
node = queue.poll();
node.setLeft(generateNode(levelList.poll()));
if (node.getLeft() != null) {
queue.add(node.getLeft());
}
node.setRight(generateNode(levelList.poll()));
if (node.getRight() != null) {
queue.add(node.getRight());
}
}
return head;
}
//层级反序列化
private Node generateNode(String value) {
if (value == null) {
return null;
}
return new Node(Integer.parseInt(value));
}
/**
* @Title: createBinaryTree
* @Description: 创建二叉树
* @param: @return
* @return: List<Node>
* @throws
*/
public static List<Node> createBinaryTree(){
int[] array = {1,2,3,4,5,6,7,8,9};
List<Node> nodeList = new ArrayList<>();
for (int nodeIndex = 0; nodeIndex < array.length;nodeIndex++){
nodeList.add(new Node(array[nodeIndex]));
}
//对LastParentIndex-1个父节点按照父节点和子节点的关系建立二叉树
for (int parentIndex = 0; parentIndex < array.length/2-1;parentIndex++){
//左孩子
nodeList.get(parentIndex).left = nodeList.get(parentIndex * 2 +1);
//右孩子
nodeList.get(parentIndex).right = nodeList.get(parentIndex * 2 +2);
}
//最后一个父节点,可能存在没有右孩子的情况,所以拿出来单独处理
int lastParentIndex = array.length/2-1;
//左孩子
nodeList.get(lastParentIndex).left = nodeList.get(lastParentIndex * 2 + 1);
//右孩子,如果长度为奇数则建立右孩子
if(array.length % 2 == 1){
nodeList.get(lastParentIndex).right = nodeList.get(lastParentIndex * 2 + 2);
}
// 输出结果
// [
// {"left":{"left":{"left":{"value":8},"right":{"value":9},"value":4},"right":{"value":5},"value":2},"right":{"left":{"value":6},"right":{"value":7},"value":3},"value":1},
// {"left":{"left":{"value":8},"right":{"value":9},"value":4},"right":{"value":5},"value":2},
// {"left":{"value":6},"right":{"value":7},"value":3},
// {"left":{"value":8},"right":{"value":9},"value":4},
// {"value":5},
// {"value":6},
// {"value":7},
// {"value":8},
// {"value":9}
// ]
return nodeList;
}
public static void main(String[] args) {
List<Node> createBinaryTree = createBinaryTree();
System.out.println(JSONObject.toJSON(createBinaryTree));
Node node = createBinaryTree.get(0);
System.out.println(JSONObject.toJSON(JSONObject.toJSON(node)));
// 先序遍历
System.out.println("先序:------------------------------------------------");
before(node);
System.out.println();
preOrderTraverse(node);
System.out.println();
// 中序遍历
System.out.println("中序:-----------------------------------------------");
middle(node);
System.out.println();
inOrderTraverse(node);
System.out.println();
// 后序遍历
System.out.println("后序:-----------------------------------------------");
after(node);
System.out.println();
postOrderTraverse(node);
System.out.println();
// 层序遍历
System.out.println("层序:-----------------------------------------------");
laywerTraverse(node);
}
}
输出结果:
{"left":{"left":{"left":{"value":8},"right":{"value":9},"value":4},"right":{"value":5},"value":2},"right":{"left":{"value":6},"right":{"value":7},"value":3},"value":1}
先序:------------------------------------------------
1->2->4->8->9->5->3->6->7->
1->2->4->8->9->5->3->6->7->
中序:-----------------------------------------------
8->4->9->2->5->1->6->3->7->
8->4->9->2->5->1->6->3->7->
后序:-----------------------------------------------
8->9->4->5->2->6->7->3->1->
8->9->4->5->2->6->7->3->1->
层序:-----------------------------------------------
1->2->3->4->5->6->7->8->9->
二叉树分类:
-
1、 满二叉树(Full Binary Tree)
一棵满二叉树就是高度为k,且拥有(2^k)-1个节点的二叉树,一棵满二叉树每个节点,要么都有两棵子树,要么都没有子树;而且每一层所有的节点之间必须要么都有两棵子树,要么都没子树。
-
2、完全二叉树(Complete Binary Tree)
完全二叉树是一颗特殊的二叉树,它遵循以下规则:
假设完全二叉树高度为k,则完全二叉树需要符合以下两点:
1)所有叶子节点都出现在k层或k-1层,并且从1~k-1层必须达到最大节点数。
2)第k层可以是不满的,但是第k层的所有节点必须集中在最左边。