zoukankan      html  css  js  c++  java
  • 二叉树

    1.简述

      二叉树是n(n>=0)个结点的有限集合,该集合或者为空集(称为空二叉树),或者由一个根结点和两棵互不相交的、分别称为根结点的左子树和右子树组成

      二叉树的定义

    • 每个节点最多有两个子节点的树称为二叉树。

      二叉树的特点

    • 每个节点最多有两个子树(注意:是最多有,而不是只有)。
    • 左子树和右子树是有次序的,不能任意颠倒。
    • 即使某个节点只有一个子树,也要区分是左子树还是右子树。

      二叉树的五种基本形态

    • 空二叉树。
    • 只有一个根节点。
    • 根节点只有左子树。
    • 根节点只有右子树。
    • 根节点既有左子树又有右子树。

      特殊的二叉树

    • 斜树:所有的节点都在一个方向(节点全在左边或右边)。
    • 满二叉树:所有叶子在同一层上。
    • 完全二叉树:若设二叉树的高度为h,除第 h 层外,其它各层 (1~h-1) 的结点数都达到最大个数,第h层有叶子结点,并且叶子结点都是从左到右依次排布,这就是完全二叉树。

      二叉树的性质

    • 在二叉树的第i层上至多有 2^{i - 1} 2i1 个结点。
    • 深度为k的二叉树至多有 2^k - 1 2k1 个节点。

    2.二叉树的存储结构

      二叉树同样有链表存储和数组存储二种方式。

      使用数组存储时,数组arr[0]不存储任何结点。对于二叉树任意结点arr[i],其左子树为arr[2] ,右子树为arr[2 + 1]。如果是完全二叉树,那么其所有的结点都刚好可以存储在数组中,并且没有浪费任何存储空间。但如果不是完全二叉树,如上图所示,arr[5] 就浪费了。

    3.二叉树的实现

    (1)二叉树数组实现

    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]+"	");
            
            // 向左递归遍历
            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]+"	");
            
            // 遍历右子树
            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]+"	");
        }
        
        /**打印数组
         */
        public String toString() {
            return Arrays.toString(this.data);
        }
    }
    View Code

    (2)二叉树链表实现

    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());
                }
            }
        }
    }
    View Code

    (3)二叉搜索树实现

    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());
                }
            }
        }
    }
    View Code
  • 相关阅读:
    二分查找算法
    java多线程模拟停车位问题
    ECShop模板原理
    linux 常用命令 自己 积累
    ubuntu忘记root密码解决
    centos下一键安装lamp环境,快捷,方便
    腾讯php部分面试题答案
    PHP面试题集
    php笔试题
    流行的php面试题及答案
  • 原文地址:https://www.cnblogs.com/bl123/p/14191863.html
Copyright © 2011-2022 走看看