二叉树
1.简述
二叉树是n(n>=0)个结点的有限集合,该集合或者为空集(称为空二叉树),或者由一个根结点和两棵互不相交的、分别称为根结点的左子树和右子树组成。
二叉树的定义:
- 每个节点最多有两个子节点的树称为二叉树。
二叉树的特点:
- 每个节点最多有两个子树(注意:是最多有,而不是只有)。
- 左子树和右子树是有次序的,不能任意颠倒。
- 即使某个节点只有一个子树,也要区分是左子树还是右子树。
二叉树的五种基本形态:
- 空二叉树。
- 只有一个根节点。
- 根节点只有左子树。
- 根节点只有右子树。
- 根节点既有左子树又有右子树。
特殊的二叉树:
- 斜树:所有的节点都在一个方向(节点全在左边或右边)。
- 满二叉树:所有叶子在同一层上。
- 完全二叉树:若设二叉树的高度为h,除第 h 层外,其它各层 (1~h-1) 的结点数都达到最大个数,第h层有叶子结点,并且叶子结点都是从左到右依次排布,这就是完全二叉树。
二叉树的性质:
- 在二叉树的第i层上至多有 2^{i - 1} 2i−1 个结点。
- 深度为k的二叉树至多有 2^k - 1 2k−1 个节点。
2.二叉树的存储结构
二叉树同样有链表存储和数组存储二种方式。
使用数组存储时,数组arr[0]不存储任何结点。对于二叉树任意结点arr[i],其左子树为arr[2] ,右子树为arr[2 + 1]。如果是完全二叉树,那么其所有的结点都刚好可以存储在数组中,并且没有浪费任何存储空间。但如果不是完全二叉树,如上图所示,arr[5] 就浪费了。
3.二叉树的实现
(1)二叉树数组实现
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
public class Test { public static void main(String[] args) { ArrayBinaryTree<String> binTree = new ArrayBinaryTree<String>(5, "1"); binTree.addLeft(0, "2"); binTree.addRight(0, "3"); binTree.addLeft(1, "4"); binTree.addRight(1, "5"); binTree.addLeft(2, "6"); binTree.addRight(2, "7"); System.out.println(binTree); System.out.println("====前序遍历===="); binTree.preOrder(0); System.out.println(); System.out.println("====中序遍历===="); binTree.inOrder(0); System.out.println(); System.out.println("====后序遍历===="); binTree.postOrder(0); System.out.println(); } } /**数组实现二叉树 */ class ArrayBinaryTree<T>{ private T[] data;//存储树的所有节点 private int deep;//树的深度 private int size;//数组大小 private static final int DEFAULT_DEEP = 5; // 树的默认深度 /**默认的深度来创建二叉树 */ @SuppressWarnings("unchecked") public ArrayBinaryTree() { this.deep = DEFAULT_DEEP; this.size = (int) Math.pow(2, deep) - 1;//返回第一个参数的第二个参数次幂的值 this.data = (T[])new Object[size]; } /**指定深度来创建二叉树 */ @SuppressWarnings("unchecked") public ArrayBinaryTree(int deep) { this.deep = deep; this.size = (int) Math.pow(2, deep) - 1; data = (T[])new Object[size]; } /**指定深度,按照根节点创建二叉树 */ @SuppressWarnings("unchecked") public ArrayBinaryTree(int deep, T data) { this.deep = deep; this.size = (int) Math.pow(2, deep) - 1; this.data = (T[])new Object[size]; this.data[0] = data; } /**指定节点添加左子节点。 */ public void addLeft(int index, T data){ if (this.data[index] == null) throw new RuntimeException("根节点为空,无法添加子节点"); expand(2 * index + 1); this.data[2 * index + 1] = data; //添加左子节点 } /**指定节点添加右子节点。 */ public void addRight(int index, T data){ if (this.data[index] == null) throw new RuntimeException("根节点为空,无法添加子节点"); expand(2 * index + 2); this.data[2 * index + 2] = data; //添加右子节点 } /**检测是否超出数组长度,是的话根据当前数组大小扩容2倍,但是当前位置不能超出扩容后的数组 */ private void expand(int index){ if(index > this.size){ int size = this.data.length * 2; if(size < index) throw new RuntimeException("数组越界,无法添加子节点"); this.size = size; this.data = Arrays.copyOf(this.data, size); } } /** 二叉树的前序递归遍历 */ public void preOrder(int index) { if (this.data == null || this.data.length == 0 || this.data[index] == null) return; System.out.print(this.data[index]+"\t"); // 向左递归遍历 if (2 * index + 1 < this.data.length) this.preOrder(2 * index + 1); // 向右递归遍历 if (2 * index + 2 < this.data.length) this.preOrder(2 * index + 2); } /** 二叉树的中序递归遍历 */ public void inOrder(int index) { if (this.data == null || this.data.length == 0 || this.data[index] == null) return; // 遍历左子树 if (2 * index + 1 < this.data.length) this.inOrder(2 * index + 1); System.out.print(this.data[index]+"\t"); // 遍历右子树 if (2 * index + 2 < this.data.length) this.inOrder(2 * index + 2); } /** 二叉树的后序递归遍历 */ public void postOrder(int index) { if (this.data == null || this.data.length == 0 || this.data[index] == null) return; // 遍历左子树 if (2 * index + 1 < this.data.length) this.inOrder(2 * index + 1); // 遍历右子树 if (2 * index + 2 < this.data.length) this.inOrder(2 * index + 2); System.out.print(this.data[index]+"\t"); } /**打印数组 */ public String toString() { return Arrays.toString(this.data); } }
(2)二叉树链表实现
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
public class Test { public static void main(String[] args) { Integer[] array = {1,2,3,4,5,6,7,8}; BinaryTree<Integer> bt = new BinaryTree<Integer>(array); System.out.println("树的高度:"+ bt.height(bt.getRoot())); System.out.println("节点的个数:" + bt.size(bt.getRoot())); System.out.println("======递归先序遍历====="); bt.preOrder(bt.getRoot()); System.out.println(); System.out.println("======非递归先序遍历====="); bt.nonRecPreOrder(bt.getRoot()); System.out.println(); System.out.println("======递归中序遍历====="); bt.inOrder(bt.getRoot()); System.out.println(); System.out.println("======非递归中序遍历====="); bt.nonRecInOrder(bt.getRoot()); System.out.println(); System.out.println("======递归后序遍历====="); bt.postOrder(bt.getRoot()); System.out.println(); System.out.println("======非递归后序遍历====="); bt.nonRecPostOrder(bt.getRoot()); System.out.println(); System.out.println("======层次遍历====="); bt.levelOrder(bt.getRoot()); } } class BinaryTree<E>{ /**二叉树节点类 */ class Node<T>{ /**构造一个新节点 */ public Node(){} public Node(T data) { this.data = data; } T data; //节点数据 Node<T> left; //左节点 Node<T> right; //右节点 public T getData() { return data; } public void setData(T data) { this.data = data; } public Node<T> getLeft() { return left; } public void setLeft(Node<T> left) { this.left = left; } public Node<T> getRight() { return right; } public void setRight(Node<T> right) { this.right = right; } } //二叉树的根节点 private Node<E> root; //二叉树节点的链式结构 private List<Node<E>> nodeList = null; /**默认构造函数 */ public BinaryTree(){} /**创建根节点构造函数 */ public BinaryTree(Node<E> root){ this.root = root; } /**数组转化为二叉树构造函数 */ public BinaryTree(E[] array){ nodeList = new LinkedList<Node<E>>(); //将数组中的元素依次转换为Node节点,存放于链表中 for(int i=0; i< array.length; i++) nodeList.add(new Node<E>(array[i])); //对前(array.length / 2 - 1)个父节点,按照父节点与孩子节点的数字关系建立完全二叉树 //对完全二叉树,按从上到下,从左到右的顺序依次编号0,1,2,3....N,则i>0的节点,其左孩子为(2*i+1), //其右孩子为(2*i+2) for(int j = 0; j < (array.length / 2 - 1); j++){ //左孩子 nodeList.get(j).setLeft(nodeList.get(2 * j + 1)); //右孩子 nodeList.get(j).setRight(nodeList.get(2 * j + 2)); } //最后一个父节点:因为最后一个父节点可能没有右孩子,所以单独处理 int index = array.length/2 -1; //左孩子 nodeList.get(index).setLeft(nodeList.get(2 * index + 1)); //右孩子:如果数组的长度为奇数才有右孩子 if(array.length % 2 == 1) nodeList.get(index).setRight(nodeList.get(2 * index + 2)); root = nodeList.get(0); //设置根节点 } /**获取根节点 */ public Node<E> getRoot(){ return root; } /**获取树的高度 */ public int height(Node<E> node){ if(node == null){ return 0; }else{ int i = height(node.getLeft()); int j = height(node.getRight()); return (i < j) ? (j+1) : (i+1); } } /**获取节点的个数 */ public int size(Node<E> node){ if(node == null){ return 0; }else{ return 1 + size(node.getLeft()) + size(node.getRight()); } } /**递归实现先序遍历 */ public void preOrder(Node<E> node){ if(node != null){ System.out.print(node.getData() + " "); preOrder(node.getLeft()); preOrder(node.getRight()); } } /**非递归实现先序遍历 */ public void nonRecPreOrder(Node<E> node){ Stack<Node<E>> nodeStack = new Stack<Node<E>>(); Node<E> nodeTemp = node;//nodeTemp作为遍历指针 while(nodeTemp != null || !nodeStack.isEmpty()){//当nodeTemp非空或栈非空时循环 if(nodeTemp != null){//根指针非空,遍历左子树 nodeStack.push(nodeTemp);//根指针进栈 System.out.print(nodeStack.peek().getData() + " ");//根指针退栈,访问根节点 nodeTemp = nodeTemp.getLeft();//每遇到非空二叉树先向左走 }else{//再向右子树走 nodeTemp = nodeStack.pop(); nodeTemp = nodeTemp.getRight(); } } } /**递归实现中序遍历 */ public void inOrder(Node<E> node){ if(node != null){ inOrder(node.getLeft()); System.out.print(node.getData() + " "); inOrder(node.getRight()); } } /**非递归实现中序遍历 */ public void nonRecInOrder(Node<E> node){ Stack<Node<E>> nodeStack = new Stack<Node<E>>(); Node<E> nodeTemp = node;//nodeTemp作为遍历指针 while(nodeTemp != null || !nodeStack.isEmpty()){//当nodeTemp非空或栈非空时循环 if(nodeTemp != null){//根指针非空,遍历左子树 nodeStack.push(nodeTemp);//根指针进栈 nodeTemp = nodeTemp.getLeft();//每遇到非空二叉树先向左走 }else{ nodeTemp = nodeStack.pop();//根指针退栈,访问根节点 System.out.print(nodeTemp.getData() +" "); nodeTemp = nodeTemp.getRight();//再向右子树走 } } } /**递归实现后序遍历 */ public void postOrder(Node<E> node){ if(node != null){ postOrder(node.getLeft()); postOrder(node.getRight()); System.out.print(node.getData() + " "); } } /**非递归实现后序遍历 */ public void nonRecPostOrder(Node<E> node){ Stack<Node<E>> nodeStack = new Stack<Node<E>>(); Node<E> nodeTemp = node;//nodeTemp作为遍历指针 Node<E> preNode = null;//表示最近一次访问的节点 while(nodeTemp != null || !nodeStack.isEmpty()){//当nodeTemp非空或栈非空时循环 while(nodeTemp != null){//一直向左走,遍历左子树 nodeStack.push(nodeTemp); nodeTemp = nodeTemp.getLeft(); } nodeTemp = nodeStack.peek(); if(nodeTemp.getRight()==null || nodeTemp.getRight() == preNode){//右子树为空或右子树已被访问时,该节点出栈 nodeTemp = nodeStack.pop(); System.out.print(nodeTemp.getData()+" "); preNode = nodeTemp;//将该节点赋值给最近一个访问节点 nodeTemp = null;//此处很重要,将刚出栈节点设置为空,对应于while循环的条件之一,否则陷入死循环 }else{ nodeTemp = nodeTemp.getRight();//遍历右子树 } } } /**层次遍历 */ public void levelOrder(Node<E> root){ Queue<Node<E>> nodeQueue = new LinkedList<Node<E>>(); Node<E> node = null; nodeQueue.add(root);//将根节点入队 while(!nodeQueue.isEmpty()){ //队列不空循环 node = nodeQueue.peek(); System.out.print(node.getData()+" "); nodeQueue.poll();//队头元素出队 if(node.getLeft() != null){//左子树不空,则左子树入队列 nodeQueue.add(node.getLeft()); } if(node.getRight() != null){//右子树不空,则右子树入队列 nodeQueue.add(node.getRight()); } } } }
(3)二叉搜索树实现
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
public class Test { public static void main(String[] args) { Integer[] array = {1,2,3,4,5,6,7,8}; BinaryTree<Integer> bt = new BinaryTree<Integer>(array); System.out.println("获取树的指定元素:"+ bt.find(bt.getRoot(), 7)); System.out.println("树的最大元素:"+ bt.findMax(bt.getRoot())); System.out.println("树的最小元素:"+ bt.findMin(bt.getRoot())); System.out.println("树的高度:"+ bt.height(bt.getRoot())); System.out.println("节点的个数:" + bt.size(bt.getRoot())); System.out.println("删除指定元素:" + bt.delete(bt.getRoot(), 7)); System.out.println("======递归先序遍历====="); bt.preOrder(bt.getRoot()); System.out.println(); System.out.println("======非递归先序遍历====="); bt.nonRecPreOrder(bt.getRoot()); System.out.println(); System.out.println("======递归中序遍历====="); bt.inOrder(bt.getRoot()); System.out.println(); System.out.println("======非递归中序遍历====="); bt.nonRecInOrder(bt.getRoot()); System.out.println(); System.out.println("======递归后序遍历====="); bt.postOrder(bt.getRoot()); System.out.println(); System.out.println("======非递归后序遍历====="); bt.nonRecPostOrder(bt.getRoot()); System.out.println(); System.out.println("======层次遍历====="); bt.levelOrder(bt.getRoot()); } } class BinaryTree<E extends Comparable<E>>{ /**二叉树节点类 */ class Node<T extends Comparable<T>>{ /**构造一个新节点 */ public Node(){} public Node(T data) { this.data = data; } T data; //节点数据 Node<T> left; //左节点 Node<T> right; //右节点 public T getData() { return data; } public void setData(T data) { this.data = data; } public Node<T> getLeft() { return left; } public void setLeft(Node<T> left) { this.left = left; } public Node<T> getRight() { return right; } public void setRight(Node<T> right) { this.right = right; } } //二叉树的根节点 private Node<E> root; /**默认构造函数 */ public BinaryTree(){} /**创建根节点构造函数 */ public BinaryTree(Node<E> root){ this.root = root; } /**数组转化为二叉树构造函数 */ public BinaryTree(E[] array){ //将数组中的元素依次转换为Node节点 for(int i=0; i< array.length; i++) insert(array[i]); } /**判断节点是否为空 */ private boolean isEmpty() { return root == null; } /**元素比较方法 */ private int compare(Node<E> node, E value) { return value.compareTo(node.getData()); } /**插入元素到输中 */ void insert(E value) { if (value == null) { return; } root = insert(root, value); } /**插入元素到指定节点(递归实现) */ private Node<E> insert(Node<E> node, E value) { if (node == null) {//节点为空,则创建节点 return new Node<>(value); } else { if (compare(node, value) < 0)//插入的元素值小于当前节点的值,则向左节点添加 node.setLeft(insert(node.getLeft(), value)); else if (compare(node, value) > 0)//插入的元素值大于当前节点的值,则向右节点添加 node.setRight(insert(node.getRight(), value)); } return node; } /**获取根节点 */ public Node<E> getRoot(){ return root; } /**获取指定元素(递归实现) */ public Node<E> find(Node<E> node, E value) { if(node == null)return null; if (compare(node, value) < 0) return find(node.getLeft(), value); else if (compare(node, value) > 0) return find(node.getRight(), value); else return node; } /**获取指定元素(非递归实现) */ public Node<E> findIter(E value) { Node<E> current = root; while (current != null) { if (compare(current, value) < 0) current = current.getLeft(); else if (compare(current, value) > 0) current = current.getRight(); else return current; } return null; } /**获取最大元素(递归实现) */ public E findMax(Node<E> node) { Node<E> temp = node; while (temp.getRight() != null) temp = temp.getRight(); return temp.getData(); } /**获取最小元素(递归实现) */ public E findMin(Node<E> node) { Node<E> temp = node; while (temp.getLeft() != null) temp = temp.getLeft(); return temp.getData(); } /**获取树的高度(递归实现) */ public int height(Node<E> node){ if(node == null){ return 0; }else{ int i = height(node.getLeft()); int j = height(node.getRight()); return (i < j) ? (j+1) : (i+1); } } /**获取节点的个数 */ public int size(Node<E> node){ if(node == null){ return 0; }else{ return 1 + size(node.getLeft()) + size(node.getRight()); } } /**从树中删除指定元素 */ public Node<E> delete(Node<E> node, E value) { if (node == null)return null; if (compare(node, value) < 0) node.setLeft(delete(node.getLeft(), value)); else if (compare(node, value) > 0) node.setRight(delete(node.getRight(), value)); else{ if (node.getLeft() != null && node.getRight() != null) { E temp = findMin(node.getRight()); node.setData(temp); node.setRight(delete(node, temp)); } else { if (node.getLeft() != null) node = node.getLeft(); else node = node.getRight(); } } return node; } /**递归实现先序遍历 */ public void preOrder(Node<E> node){ if(node != null){ System.out.print(node.getData() + " "); preOrder(node.getLeft()); preOrder(node.getRight()); } } /**非递归实现先序遍历 */ public void nonRecPreOrder(Node<E> node){ Stack<Node<E>> nodeStack = new Stack<Node<E>>(); Node<E> nodeTemp = node;//nodeTemp作为遍历指针 while(nodeTemp != null || !nodeStack.isEmpty()){//当nodeTemp非空或栈非空时循环 if(nodeTemp != null){//根指针非空,遍历左子树 nodeStack.push(nodeTemp);//根指针进栈 System.out.print(nodeStack.peek().getData() + " ");//根指针退栈,访问根节点 nodeTemp = nodeTemp.getLeft();//每遇到非空二叉树先向左走 }else{//再向右子树走 nodeTemp = nodeStack.pop(); nodeTemp = nodeTemp.getRight(); } } } /**递归实现中序遍历 */ public void inOrder(Node<E> node){ if(node != null){ inOrder(node.getLeft()); System.out.print(node.getData() + " "); inOrder(node.getRight()); } } /**非递归实现中序遍历 */ public void nonRecInOrder(Node<E> node){ Stack<Node<E>> nodeStack = new Stack<Node<E>>(); Node<E> nodeTemp = node;//nodeTemp作为遍历指针 while(nodeTemp != null || !nodeStack.isEmpty()){//当nodeTemp非空或栈非空时循环 if(nodeTemp != null){//根指针非空,遍历左子树 nodeStack.push(nodeTemp);//根指针进栈 nodeTemp = nodeTemp.getLeft();//每遇到非空二叉树先向左走 }else{ nodeTemp = nodeStack.pop();//根指针退栈,访问根节点 System.out.print(nodeTemp.getData() +" "); nodeTemp = nodeTemp.getRight();//再向右子树走 } } } /**递归实现后序遍历 */ public void postOrder(Node<E> node){ if(node != null){ postOrder(node.getLeft()); postOrder(node.getRight()); System.out.print(node.getData() + " "); } } /**非递归实现后序遍历 */ public void nonRecPostOrder(Node<E> node){ Stack<Node<E>> nodeStack = new Stack<Node<E>>(); Node<E> nodeTemp = node;//nodeTemp作为遍历指针 Node<E> preNode = null;//表示最近一次访问的节点 while(nodeTemp != null || !nodeStack.isEmpty()){//当nodeTemp非空或栈非空时循环 while(nodeTemp != null){//一直向左走,遍历左子树 nodeStack.push(nodeTemp); nodeTemp = nodeTemp.getLeft(); } nodeTemp = nodeStack.peek(); if(nodeTemp.getRight()==null || nodeTemp.getRight() == preNode){//右子树为空或右子树已被访问时,该节点出栈 nodeTemp = nodeStack.pop(); System.out.print(nodeTemp.getData()+" "); preNode = nodeTemp;//将该节点赋值给最近一个访问节点 nodeTemp = null;//此处很重要,将刚出栈节点设置为空,对应于while循环的条件之一,否则陷入死循环 }else{ nodeTemp = nodeTemp.getRight();//遍历右子树 } } } /**层次遍历 */ public void levelOrder(Node<E> root){ Queue<Node<E>> nodeQueue = new LinkedList<Node<E>>(); Node<E> node = null; nodeQueue.add(root);//将根节点入队 while(!nodeQueue.isEmpty()){ //队列不空循环 node = nodeQueue.peek(); System.out.print(node.getData()+" "); nodeQueue.poll();//队头元素出队 if(node.getLeft() != null){//左子树不空,则左子树入队列 nodeQueue.add(node.getLeft()); } if(node.getRight() != null){//右子树不空,则右子树入队列 nodeQueue.add(node.getRight()); } } } }