1.什么是二叉树
- 概念
二叉树是节点最多可以包含两个子节点的树,每一个节点都可以区分为左子节点和右子节点,二叉树的一个重要性质就是叶节点的数量<=2,如图所示就是一棵简单的二叉树
2.二叉树的相关概念
- 路径
指的是从根节点到叶节点的路径。 - 节点的度
一个节点含有的子树的个数称为该节点的度 - 节点的权
节点中有意义的值(通常是数值) - 层
从根开始定义起,根为第1层,根的子节点为第2层,以此类推; - 高度
是由根结点出发,到子结点的最长路径长度。 - 结点深度
是指对应结点到根结点路径长度。 - 森林
多棵树构成森林
3.二叉树的几种形态
- 空树
- 左斜树和右斜树
- 不知道怎么描述
几种特殊的二叉树
-
1.满二叉树
一个二叉树,如果每一个层的结点数都达到最大值,则这个二叉树就是满二叉树。也就是说,如果一个二叉树的层数为K,且结点总数是(2^k) -1 ,则它就是满二叉树。
性质:- 1.一个层数为k 的满二叉树总结点数为:(2^k) -1 。因此满二叉树的节点数一定是奇数
- 2.第 i 层上的节点个数是:2^ (i -1) 个
-
2.完全二叉树
完全二叉树是由满二叉树而引出来的。对于一个树高为h的二叉树,如果其第0层至第h-1层的节点都满。如果最下面一层节点不满,又如果其所有的节点在左边的连续排列,空位都在右边。这样的二叉树就是一棵完全二叉树。
性质:- 所有的叶结点都出现在第k层或k-l层(层次最大的两层)所以,对任一结点,如果其右子树的最大层次为L,则其左子树的最大层次为L或L+l。
- 一棵具有n个结点的深度为k的二叉树,它的每一个结点都与深度为k的满二叉树中编号为1~n的结点一一对应
二叉树的构建
-
顺序存储结构实现 — 基于数组
按照完全二叉树的节点按层依次自左向右编号,若节点为空,则在数组中留空。
public class ArrayTree<T> {
private T[] data;
private int deep;
private int MaxSize;
private int size = 0;
public ArrayTree(int deep){
this.deep = deep;
//根据二叉树的深度计算元素的最大个数
MaxSize = (int)Math.pow(2, deep)-1;
data = (T[]) new Object[MaxSize];
}
/**
*
* @param value 根节点的值
*/
public void creatRoot(T value){
data[0] = value;
size++;
}
/**
* @param value 添加的节点的值
* @param index 父节点的索引
* @param left 是否添加到左节点,否则为右节点
* */
public void add(T value,int index,boolean left){
if(data[index] == null){
throw new RuntimeException(index + ": null");
}
if(2*index+1 >= MaxSize){
throw new RuntimeException("array full");
}
if(left){
data[2*index+1] = value;
size++;
}
else{
data[2*index+2] = value;
size++;
}
}
//根据根节点是否为空判断二叉树是否为空
public boolean isEmpty(){
return data[0]==null;
}
//二叉树中元素的个数
public int size(){
return this.size;
}
//打印二叉树
public String toString(){
String str = "";
for(int i=0; i<MaxSize; i++){
str = str + data[i] + " ";
}
return str;
}
public static void main(String args[]){
ArrayTree<String> at = new ArrayTree<String>(3);
/**
* 根节点
* 第二层左节点 第二层右节点
* 第三层右节点 第三层左节点
*/
at.creatRoot("根节点");
at.add("第二层左节点", 0, true);
at.add("第二层右节点", 0, false);
at.add("第三层右节点", 1, false);
System.out.println(at.toString());
at.add("第三层左节点", 2, true);
System.out.println(at.toString());
System.out.println("二叉树元素个数: " + at.size());
}
}
- 链式存储结构实现 – 基于链表
一个节点主要有三部分组成:
-
节点携带的数据
-
节点指向左子树的指针
-
节点指向右子树的指针
public class BinaryTreeNode {
//值
private int value;
//左子节点
private BinaryTreeNode left;
//右子节点
private BinaryTreeNode right;
public BinaryTreeNode(int value, BinaryTreeNode left, BinaryTreeNode right) {
super();
this.value = value;
this.left = left;
this.right = right;
}
public BinaryTreeNode(int value) {
super();
this.value = value;
}
//省略get / set
public class BinaryTree {
//根节点
BinaryTreeNode root;
public BinaryTreeNode getRoot() {
return root;
}
public void setRoot(BinaryTreeNode root) {
this.root = root;
}
public static void main(String[] args) {
BinaryTreeNode root = new BinaryTreeNode(21);
BinaryTree binaryTree = new BinaryTree();
binaryTree.setRoot(root);
BinaryTreeNode leftNode = new BinaryTreeNode(12);
root.setLeft(leftNode);
BinaryTreeNode rightNode = new BinaryTreeNode(32);
root.setRight(rightNode);
leftNode.setLeft(new BinaryTreeNode(15));
leftNode.setRight(new BinaryTreeNode(1));
rightNode.setLeft(new BinaryTreeNode(4));
rightNode.setRight(new BinaryTreeNode(5));
}
}
二叉树的遍历
树的遍历操作有三种,前序遍历,中序遍历和后序遍历。三者的不同之处在于处理子节点的时间不同。前序遍历是先处理根节点,然后处理左孩子最后处理右孩子。将处理根节点的操作挪到处理左右节点的操作之间,我们就得到了中序遍历。如果挪到最后,那就是后序遍历。每一种遍历都有递归和非递归两种实现方式
- 前序遍历
/**
* 前序遍历非递归实现
* @param binaryTree
*/
private void preOrder(BinaryTree binaryTree) {
BinaryTreeNode root = binaryTree.getRoot();
ArrayList<Integer> list = new ArrayList<>();
Stack<BinaryTreeNode> stack = new Stack<>();
if(root == null) {
return ;
}else {
stack.push(root);
while(!stack.isEmpty()) {
BinaryTreeNode node = stack.pop();
list.add(node.getValue());
if(node.getRight()!=null) {
stack.push(node.getRight());
}
if(node.getLeft()!=null) {
stack.push(node.getLeft());
}
}
}
for(Integer i : list) {
System.out.println(i);
}
}
/**
* 前序遍历递归实现
* @param root
*/
private void preOrder2(BinaryTreeNode root) {
if(root!=null) {
System.out.println(root.getValue());
preOrder2(root.getLeft());
preOrder2(root.getRight());
}
}
- 中序遍历
/**
* 中序遍历非递归实现
* @param binaryTree
*/
private void order(BinaryTree binaryTree) {
BinaryTreeNode root = binaryTree.getRoot();
ArrayList<Integer> list = new ArrayList<>();
Stack<BinaryTreeNode> stack = new Stack<>();
while(root!=null || !stack.isEmpty()) {
if(root!=null) {
//为了能让结果list先存放的左子节点
stack.push(root);
root = root.getLeft();
}else {
root= stack.pop();
list.add(root.getValue());
root = root.getRight();
}
}
for(Integer i : list) {
System.out.println(i);
}
}
/**
* 中序遍历非递归实现
* @param root
*/
private void order2(BinaryTreeNode root) {
if(root != null) {
order2(root.getLeft());
System.out.println(root.getValue());
order2(root.getRight());
}
}
- 后序遍历
/**
* 后序遍历非递归实现
* @param binaryTree
*/
private void proOrder(BinaryTree binaryTree) {
BinaryTreeNode root = binaryTree.getRoot();
ArrayList<Integer> list = new ArrayList<>();
//操作栈
Stack<BinaryTreeNode> stack = new Stack<>();
//结果栈,用于存放后序遍历的结果
Stack<BinaryTreeNode> resultStack = new Stack<>();
while(root!=null || !stack.isEmpty()) {
if(root!=null) {
//执行完这里后,结果栈中已经从栈顶到栈底分别是:右子节点 --- 根节点
stack.push(root);
resultStack.push(root);
root = root.getRight();
}else {
//拿到左子节点放入操作栈,重复上边操作,此时,结果栈变成了我们想要的顺序:左子节点 -- 右子节点 -- 根节点
root = stack.pop();
root = root.getLeft();
}
}
//将遍历结果放入ArrayList中,其实本可以不使用这一步直接遍历栈,个人习惯
while(!resultStack.isEmpty()) {
BinaryTreeNode node = resultStack.pop();
list.add(node.getValue());
}
for(Integer i : list) {
System.out.println(i);
}
}
/**
* 后序遍历递归实现
* @param root
*/
private void proOrder2(BinaryTreeNode root) {
if(root!=null) {
proOrder2(root.getLeft());
proOrder2(root.getRight());
System.out.println(root.getValue());
}
}
测试
public static void main(String[] args) {
BinaryTreeNode root = new BinaryTreeNode(21);
BinaryTree binaryTree = new BinaryTree();
binaryTree.setRoot(root);
BinaryTreeNode leftNode = new BinaryTreeNode(12);
root.setLeft(leftNode);
BinaryTreeNode rightNode = new BinaryTreeNode(32);
root.setRight(rightNode);
leftNode.setLeft(new BinaryTreeNode(15));
leftNode.setRight(new BinaryTreeNode(1));
rightNode.setLeft(new BinaryTreeNode(4));
rightNode.setRight(new BinaryTreeNode(5));
System.out.println("********前序遍历非递归**********");
binaryTree.preOrder2(binaryTree.root);
System.out.println("********前序遍历递归**********");
binaryTree.preOrder(binaryTree);
System.out.println("********中序遍历非递归**********");
binaryTree.order(binaryTree);
System.out.println("********中序遍历递归**********");
binaryTree.order2(binaryTree.root);
System.out.println("********后序遍历非递归**********");
binaryTree.proOrder(binaryTree);
System.out.println("********后序遍历递归**********");
binaryTree.proOrder2(binaryTree.root);
}
该二叉树是
前序遍历:
21 --> 12 --> 15 --> 1 --> 32 --> 4 --> 5
中序遍历:
15 --> 12 --> 1 --> 21 --> 4 --> 32 --> 5
后序遍历:
15 --> 1 -->12 --> 4 -->5 -->32 -->21