树和二叉树

树和二叉树
树代表一种非线性的数据结构,如果一组数据节点之间存在复杂的一对多关联时,程序就可以考虑使用树来保存这组数据了。

 

树的定义和基本术语
计算机世界的树,是从自然界实际的树抽象而来的,它指的是N个有父子关系的节点的有限集合。对于这个有限的节点集合而言,它满足如下条件:
当N = 0时,该节点集合为空,这棵树也被称为空树;
在任意的非空树中,有且只有一个根(root)节点;
当N>1时,处根节点意外的其余节点可分为M个护卫交互的有限集合T1,T2....,Tm,其中的每个集合本身又是一棵树,并称其为根的子树(subtree);

树的递归特性:
一棵树是由根和若干棵子树组成,而每个子树又由若干棵更小的子树组成。
树的任一节点可以有0或多个子节点,但只能有一个父节点,根节点例外,没有父节点。
按照节点是否包含子节点分,节点分为:
普通节点:包含子节点的节点
叶子节点:没有子节点的节点,因此叶子节点不可作为父节点
按照节点是否具有唯一的父节点区分:
根节点:没有父节点
普通节点:具有唯一的父节点

一棵树只能有一个根节点,如果一棵树有了多个根节点,那它已经不再是一棵树,而是多棵树的集合,有时也被称为森林。

与树有关的术语:
节点:树的最基本组成单位,常包括一个数据元素及若干指针用于指向其他节点
节点的度:节点又有的子树的个数被称为节点的度(degree)
树的度:树种所有节点的度的最大值就是该树的度
叶子节点:度为0的节点被称为叶子节点或终端节点
分支节点:度不为0的节点被称为分支节点或者非终端节点
子节点、父节点、兄弟节点:节点的子树的根被称为该节点的子节点,而该节点乘坐子节点的父节点(parent)。具有相同父节点的子节点之间互为兄弟节点(sibling)
节点的层次:节点的层次从根节点开始算起,根的层次值为1,其余节点的层次值为父节点层次值加1
树的深度(depth):树中节点的最大层次值称为树的深度或高度
有序树和无序树:如果将树种节点的各个子树看成从左到右是有序的(即不能互换),则称该树为有序树,否则称为无序树
祖先节点(ancestor):从根到该节点所经分支上的所有节点
后代节点(descendant):以某节点为根的子树种任一节点都称为该节点的后代节点
森林(forest):森林是两棵或两棵以上互不交互的树的集合

实现一棵树,程序不仅要以合适的方式保存该树的所有节点,还要记录节点与节点之间的父子关系。接下来,还应该为树实现如下基本操作:
初始化:用于创建空树,或者以指定节点为根创建树
为指定节点添加子节点
判断树是否为空
返回根节点
返回指定节点(非根节点)的父节点
返回指定节点(非叶子节点)的所有子节点
返回指定节点(非叶子节点)的第i个子节点
返回该树的深度
返回指定节点的位置
为了实现数这种数据结构,程序必须能记录节点与节点之间的父子关系,为此有以下两种选择:
父子节点表示法:每个子节点都记录它的父节点

孩子链表示法:每个非叶子节点通过一个链表来记录它所有的子节点

 


父子节点表示法:

package com.test_one;

import java.util.ArrayList;
import java.util.List;

/**
 * Created by Administrator on 2017/3/10.
 */
public class TreeParent<E> {
    public static class Node<T>{
        T data;
        int parent;
        public Node(){

        }
        public Node(T data){
            this.data = data;
        }
        public Node(T data , int parent){
            this.data = data;
            this.parent = parent;
        }
        public String toString(){
            return "TreeParent$Node[data=]" + data + ",parent=" + parent + "]";
        }
    }

    private final int DEFAULT_TREE_SIZE = 100;
    private int treeSize = 0;
    private Node<E>[] nodes;

    private int nodeNums;
    public TreeParent(E data){
        treeSize = DEFAULT_TREE_SIZE;
        nodes = new Node[treeSize];
        nodes[0] = new Node<E>(data , -1);
        nodeNums++;
    }

    public TreeParent(E data , int treeSize){
        this.treeSize = treeSize;
        nodes = new Node[treeSize];
        nodes[0] = new Node<E>(data , -1);
        nodeNums++;
    }

    public void addNode(E data , Node parent){
        for(int i = 0; i < treeSize; i++){
            if(nodes[i] == null){
                nodes[i] = new Node(data , pos(parent));
                nodeNums++;
                return;
            }
        }
        throw new RuntimeException("该树已满,无法添加新节点");
    }

    public boolean empty(){
        return nodes[0] == null;
    }

    public Node<E> root(){
        return nodes[0];
    }

    public Node<E> parent(Node node){
        return nodes[node.parent];
    }

    public List<Node<E>> children(Node parent){
        List<Node<E>> list = new ArrayList<Node<E>>();
        for(int i = 0; i < treeSize; i++){
            if(nodes[i] != null && nodes[i].parent == pos(parent)){
                list.add(nodes[i]);
            }
        }
        return list;
    }

    public int deep(){
        int max = 0;
        for(int i = 0; i < treeSize && nodes[i] != null; i++){
            int def = 1;
            int m = nodes[i].parent;
            while(m != -1 && nodes[m] != null){
                m = nodes[m].parent;
                def++;
            }
            if(max < def){
                max = def;
            }
        }
        return max;
    }

    public int pos(Node node){
        for(int i = 0; i < treeSize; i++){
            if(nodes[i] == node){
                return i;
            }
        }
        return -1;
    }
}

package com.test_one;

import java.util.List;

/**
 * Created by Administrator on 2017/3/10.
 */
public class TreeParentTest {
    public static void main(String[] args){
        TreeParent<String> tp = new TreeParent<String>("root");
        TreeParent.Node root = tp.root();
        System.out.println(root);
        tp.addNode("节点1" , root);
        System.out.println("此树的深度:" + tp.deep());
        tp.addNode("节点2" , root);
        List<TreeParent.Node<String>> nodes = tp.children(root);
        System.out.println("根节点的第一个子节点:" + nodes.get(0));
        tp.addNode("节点3", nodes.get(0));
        System.out.println("此树的深度:" + tp.deep());
    }
}
View Code

 


子节点链表示法:

package com.test_one;

import java.util.ArrayList;
import java.util.List;

/**
 * Created by Administrator on 2017/3/10.
 */
public class TreeChild<E> {
    private static class SonNode{
        private int pos;
        private SonNode next;
        public SonNode(int pos , SonNode next){
            this.pos = pos;
            this.next = next;
        }
    }
    public static class Node<T>{
        T data;
        SonNode first;
        public Node(T data){
            this.data = data;
            this.first = null;
        }
        public String toString(){
            if(first != null){
                return "TreeChild$Node[data=" + data + ", first=" +first.pos + "]";
            }else{
                return "TreeChildNode[data=" + data + ", first= -1]";
            }
        }
    }

    private final int DEFAULT_TREE_SIZE = 100;
    private int treeSize = 0;
    private Node<E>[] nodes;
    private int nodeNums;

    public TreeChild(E data){
        treeSize = DEFAULT_TREE_SIZE;
        nodes = new Node[treeSize];
        nodes[0] = new Node<E>(data);
        nodeNums++;
    }
    public TreeChild(E data , int treeSize){
        this.treeSize = treeSize;
        nodes = new Node[treeSize];
        nodes[0] = new Node<E>(data);
        nodeNums++;
    }
    public void addNode(E data , Node parent){
        for(int i = 0; i < treeSize; i++ ){
            if(nodes[i] == null){
                nodes[i] = new Node(data);
                if(parent.first == null){
                    parent.first = new SonNode(i , null);
                }else{
                    SonNode next = parent.first;
                    while(next.next != null){
                        next = next.next;
                    }
                    next.next = new SonNode(i , null);
                }
                nodeNums++;
                return;
            }
        }
        throw new RuntimeException("该树已满,无法添加新节点");
    }
    public boolean empty(){
        return nodes[0] == null;
    }
    public Node<E> root(){
        return nodes[0];
    }
    public List<Node<E>> children(Node parent){
        List<Node<E>> list = new ArrayList<Node<E>>();
        SonNode next = parent.first;
        while(next != null){
            list.add(nodes[next.pos]);
            next = next.next;
        }
        return list;
    }
    public Node<E> child(Node parent , int index){
        SonNode next = parent.first;
        for(int i = 0; next != null; i++){
            if(index == i){
                return nodes[next.pos];
            }
            next = next.next;
        }
        return null;
    }
    public int deep(){
        return deep(root());
    }
    private int deep(Node node){
        if(node.first == null){
            return 1;
        }else{
            int max = 0;
            SonNode next = node.first;
            while(next != null){
                int tmp = deep(nodes[next.pos]);
                if(tmp > max){
                    max = tmp;
                }
                next = next.next;
            }
            return max + 1;
        }
    }
    public int pos(Node node){
        for(int i = 0; i < treeSize; i++){
            if(nodes[i] == node){
                return i;
            }
        }
        return -1;
    }

}
import java.util.List;

/**
 * Created by Administrator on 2017/3/10.
 */
public class TreeChildTest {
    public static void main(String[] args){
        TreeChild<String> tp = new TreeChild<String>("root");
        TreeChild.Node root = tp.root();
        System.out.println("根节点:" + root);
        tp.addNode("节点1" , root);
        tp.addNode("节点2" , root);
        tp.addNode("节点3" , root);
        System.out.println("添加子节点后的根节点:" + root);
        System.out.println("此树的深度:" + tp.deep());
        List<TreeChild.Node<String>> nodes = tp.children(root);
        System.out.println("根节点的第一个子节点:" + nodes.get(0));
        tp.addNode("节点4" , nodes.get(0));
        System.out.println("根节点的第一个子节点:" + nodes.get(0));
        System.out.println("此树的深度:" + tp.deep());
    }
}
View Code

 

父节点表示法的思想是让每个节点“记住”它的父节点的索引,这种方式时从子节点着手;反过来,还有另一种方式,让父节点记住它所有子节点,由于每个父节点需要记住多个子节点,因此必须采用“子节点链”表示法。

二叉树
对于普通树来说,由于它需要遵循的规律太少,程序控制起来反而更加复杂,因此限制它在实际应用中的使用。如果对普通树增加一些限制,让一棵树中每个节点最多只能包含2个子节点,而且严格区分左子节点、右子节点(左、右子节点的位置不能交换),这棵树就变成了二叉树。

二叉树的定义和基本概念
二叉树指的是每个节点做多只能有两个子树的有序树。通常左边的子树被称为“左子树”,右边的子树被称作“右子树”。由此可见,二叉树依然是树,它是一种特殊的树。
二叉树的每个节点最多只能有2棵子树(不存在度大于2的节点),二叉树的子树有左、右之分,次序不能颠倒。
树和二叉树两个重要区别:
树种节点的最大度数没有限制:而二叉树的节点的最大度数为2,也就是说二叉树是节点的最大度数为2的树
无序树的节点无左、右之分,而二叉树的节点有左右之分,也就是说二叉树是有序树。

满二叉树:

 

一棵深度为k的二叉树,如果它包含了2^k-1个节点,就把这棵二叉树称为满二叉树。满二叉树的特点是每一层的节点数都是最大节点数,即各层节点数分别为1、2、4、8、16...2^k-1

完全二叉树:

一棵有n个节点的二叉树,按满二叉树的编号方式对它进行编号,若树中所有节点和满二叉树1~n编号完全一致,则称该树为完全二叉树。也就是说,如果一棵二叉树除最后一层外,就其余的所有节点都是满的,并且最后一层或者是满的,或者仅在右边缺少若干连续的节点,则此二叉树就是完全二叉树。

满二叉树是一种特殊的完全二叉树。当完全二叉树最后一层的所有节点都是满的时,这棵完全二叉树就变成了满二叉树

综上所述,二叉树大致有如下几个性质:
二叉树第i层的节点数目至多为2^(i-1)
深度为k的二叉树至多有2^k-1个节点。满二叉树的每层节点的数量依次为1、2、4、8...2^k-1
在任何一棵二叉树中,如果其叶子节点的数量为n0,度为2的子节点数量为n2,则n0 = n2 +1(不是很理解,如果时满二叉树的话就还理解)
具有n个节点的完全二叉树的深度为log2^n+1
对一棵有n个节点的完全二叉树的节点按层自左向右编号,则对任一编号为i(n>=i>=1)的节点具有以下性质:
若i==1时,节点i时二叉树的根;i》1,则节点的父节点是i/2
若2i<=n,则节点i有左孩子,左孩子的编号是2i,否则,节点无左孩子,并且是叶子节点
若2i+1<=n,则节点i有右孩子,右孩子的编号是2i+1;否则,节点无右孩子
(把i想象成二叉树某层最左边的节点时比较容易理解)
对一棵有n个节点的完全二叉树的节点按层自左向右编号,1~n/2的范围的节点都是有孩子的子节点的非叶子节点,其余的节点都是叶子节点。编号为n/2的节点可能只有左节点,也可能两个节点都有。

二叉树的基本操作
二叉树记录其父子关系更加简单,因为二叉树中的每个节点最多只能保存2个子节点。
初始化:创建空树或者以指定节点为根创建二叉树
为指定节点添加子节点
判断二叉树是否为空
返回根节点
返回指定节点(非根节点的父节点)
返回指定节点(非叶子节点的)左子节点
返回指定节点(非叶子节点)的右子节点
返回该二叉树的深度
返回指定节点的位置

要实现二叉树这种数据结构,有以下三种选择:
顺序存储:数组记录二叉树所有节点
二叉链表存储:每个节点保留一个left/right
三叉链表存储:每个节点保留一个left/right/parent)

二叉树的顺序存储:

顺序存储是充分利用满二叉树的特征--每层的节点数分别为1、2、4、8...,一棵深度为i的二叉树最多只能包含2^i-1个节点,因此定义一个同样长度的数组即可存储这棵二叉树。
对于普通二叉树,那些空出来的节点对应的数组元素留空就可以了。由此可见,二叉树采用顺序存储会造成一定的空间浪费

package com.test_one;

/**
 * Created by Administrator on 2017/3/11.
 */
public class ArrayBinTree<T> {
    private Object[] datas;
    private int DEFAULT_DEEP = 8;
    private int deep;
    private int arraySize;

    public ArrayBinTree(){
        this.deep = DEFAULT_DEEP;
        this.arraySize = (int)Math.pow(2 , deep) - 1;
        datas = new Object[arraySize];
    }

    public ArrayBinTree(int deep){
        this.deep = deep;
        this.arraySize = (int)Math.pow(2 , deep) - 1;
        datas = new Object[arraySize];
    }

    public ArrayBinTree(int deep , T data){
        this.deep = deep;
        this.arraySize = (int)Math.pow(2 , deep) - 1;
        datas = new Object[arraySize];
        datas[0] = data;
    }

    public void add(int index , T data , boolean left){
        if(datas[index] == null){
            throw new RuntimeException(index + "处节点为空,无法添加子节点");
        }if(2 * index +1 >= arraySize){
            throw new RuntimeException("树底层的数组已满,树越界异常");
        }
        if(left){
            datas[2 * index + 1] = data;
        }else{
            datas[2 * index + 2] = data;
        }
    }

    public boolean empty(){
        return datas[0] == null;
    }

    public T root(){
        return (T)datas[0];
    }

    public T parent(int index){
        return (T)datas[(index - 1) / 2];
    }

    public T left(int index){
        if(2 * index + 1 >= arraySize){
            throw new RuntimeException("该节点为叶子节点,无子节点");
        }
        return (T)datas[index * 2 + 1];
    }

    public T right(int index){
        if(2 * index + 1 >= arraySize){
            throw new RuntimeException("该节点为叶子节点,无子节点");
        }
        return (T)datas[index * 2 + 2];
    }

    public int deep(int index){
        return deep;
    }

    public int pos(T data){
        for(int i = 0; i < arraySize; i++){
            if(datas[i].equals(data)){
                return i;
            }
        }
        return -1;
    }

    public String toString(){
        return java.util.Arrays.toString(datas);
    }
}
package com.test_one;

/**
 * Created by Administrator on 2017/3/11.
 */
public class ArrayBinTreeTest {
    public static void main(String[] args){
        ArrayBinTree<String> binTree = new ArrayBinTree<String>(4 , "根");
        binTree.add(0 , "第二层右子节点" , false);
        binTree.add(2 , "第三层右子节点" , false);
        binTree.add(6 , "第三层右子节点" , false);
        System.out.println(binTree);
    }
}
View Code

这种二叉树,遍历查找都挺高效,唯一的缺点是空间浪费大。

二叉链表存储:

package com.test_one;

/**
 * Created by Administrator on 2017/3/11.
 */
public class TwoLinkBinTree<E> {
    public static class TreeNode{
        Object data;
        TreeNode left;
        TreeNode right;
        public TreeNode(){

        }
        public TreeNode(Object data){
            this.data = data;
        }
        public TreeNode(Object data , TreeNode left , TreeNode right){
            this.data = data;
            this.left = left;
            this.right = right;
        }
    }

    private TreeNode root;
    public TwoLinkBinTree(){
        this.root = new TreeNode();
    }
    public TwoLinkBinTree(E data){
        this.root = new TreeNode(data);
    }

    public TreeNode addNode(TreeNode parent , E data ,boolean isLeft){
        if(parent == null){
            throw new RuntimeException(parent + "节点为null,无法添加子节点");
        }
        if(isLeft && parent.left != null){
            throw new RuntimeException("节点已有左节点,无法添加左子节点");
        }
        if(!isLeft && parent.right != null){
            throw new RuntimeException("节点已有右子节点,无法添加右子节点");
        }
        TreeNode newNode = new TreeNode(data);
        if(isLeft){
            parent.left = newNode;
        }else{
            parent.right = newNode;
        }
        return newNode;
    }

    public boolean empty(){
        return root.data == null;
    }

    public TreeNode root(){
        if(empty()){
            throw new RuntimeException("树为空,无法访问根节点");
        }
        return root;
    }

    public E parent(TreeNode node){
        TreeNode parent = (TreeNode)findParent(root , node);
        if(parent == null){
            return null;
        }else{
            return (E)parent.data;
        }
    }

    public TreeNode findParent(TreeNode parent , TreeNode mayChildNode){
        if(parent == null){
            return null;
        }
        TreeNode left = parent.left;
        TreeNode right = parent.right;
        if(left == mayChildNode || right == mayChildNode){
            return parent;
        }else{
            TreeNode leftGet = findParent(left , mayChildNode);
            TreeNode rightGet = findParent(right , mayChildNode);
            if(leftGet != null) {
                return leftGet;
            } else if(right != null){
                 return right;
            }
            return null;
        }

    }

    public E leftChild(TreeNode parent){
        if(parent == null){
            throw new RuntimeException(parent + "节点为null,不存在子节点");
        }
        return parent.left == null ? null :(E)parent.left.data;
    }

    public E rightChild(TreeNode parent){
        if(parent == null){
            throw new RuntimeException("节点为null,不存在右节点");
        }
        return parent.right == null ? null :(E)parent.right.data;
    }

    public int deep(){
        return deep(root);
    }

    private int deep(TreeNode node){
        if(node == null){
            return 0;
        }
        if(node.left == null && node.right == null){
            return 1;
        }else{
            int leftDeep = deep(node.left);
            int rightDeep = deep(node.right);
            int max = leftDeep > rightDeep ? leftDeep : rightDeep;
            return max + 1;
        }

    }
}
package com.test_one;

/**
 * Created by Administrator on 2017/3/11.
 */
public class TwoLinkBinTreeTest {
    public static void main(String[] args){
        TwoLinkBinTree<String> binTree = new TwoLinkBinTree("根节点");
        TwoLinkBinTree.TreeNode n1 = binTree.addNode(binTree.root() , "第二层左节点" , true);
        TwoLinkBinTree.TreeNode n2 = binTree.addNode(binTree.root() , "第二层右节点" , false);
        TwoLinkBinTree.TreeNode n3 = binTree.addNode(n2 , "第三层左节点" , true);
        TwoLinkBinTree.TreeNode n4 = binTree.addNode(n2 , "第三层右节点" , false);
        TwoLinkBinTree.TreeNode n5 = binTree.addNode(n3 , "第四层左节点" , true);
        System.out.println(binTree.deep());
        System.out.println("n2的左子节点:" + binTree.leftChild(n2));
        System.out.println("n2的右子节点:" + binTree.rightChild(n2));
        System.out.println("n2的父节点内容:" + binTree.parent(n2));
    }
}
View Code

添加节点没有限制,不会像顺序存储那样产生大量的空间浪费。在遍历树节点时效率不高,指定节点访问其父节点时也比较困难,程序必须采用遍历二叉树的方式搜索其父节点

 

三叉链表存储:

package com.test_one;

import java.util.ArrayDeque;
import java.util.ArrayList;
import java.util.List;
import java.util.Queue;
import java.util.concurrent.ArrayBlockingQueue;

/**
 * Created by Administrator on 2017/3/11.
 */
public class ThreeLinkBinTree<E> {
    public static class TreeNode{
        Object data;
        TreeNode right;
        TreeNode left;
        TreeNode parent;
        public TreeNode(){}
        public TreeNode(Object data){
            this.data = data;
        }
        public TreeNode(Object data , TreeNode left , TreeNode right , TreeNode parent){
            this.data = data;
            this.left = left;
            this.right = right;
            this.parent = parent;
        }
    }

    private TreeNode root;

    public ThreeLinkBinTree(){
        this.root = new TreeNode();
    }
    public ThreeLinkBinTree(E data){
        this.root = new TreeNode(data);
    }
    public TreeNode addNode(TreeNode parent , E data , boolean isLeft){
        if(parent == null){
            throw new RuntimeException("节点为null,无法添加子节点");
        }
        if(isLeft && parent.left != null){
            throw new RuntimeException("父节点的左子节点已存在,无法添加左子节点");
        }
        if(!isLeft && parent.right == null){
            throw new RuntimeException("父节点的右子节点已经存在,无法添加右子节点");
        }
        TreeNode newNode = new TreeNode(data);
        if(isLeft){
            parent.left = newNode;
        }else{
            parent.right = newNode;
        }
        newNode.parent = parent;
        return newNode;
    }

    public boolean empty(){
        return root.data == null;
    }

    public TreeNode root(){
        if(empty()){
            throw new RuntimeException("树为空,无法访问根节点");
        }
        return root;
    }

    public E parent(TreeNode node){
        if(node == null){
            throw new RuntimeException(node + "节点为null,无法访问其父节点");
        }
        return (E)node.parent.data;
    }

    public E leftChild(TreeNode parent){
        if(parent == null){
            throw new RuntimeException("节点为null,其没有左节点");
        }
        return parent.left == null ? null :(E)parent.left.data;
    }

    public E rightChild(TreeNode parent){
        if(parent == null){
            throw new RuntimeException("节点为null,其没有右节点");
        }
        return parent.right == null ? null :(E)parent.right.data;
    }

    public int deep(){
        return deep(root);
    }

    private int deep(TreeNode node){
        if(node == null){
            return 0;
        }
        if(node.left == null && node.right == null){
            return 1;
        }else{
            int leftDeep = deep(node.left);
            int rightDeep = deep(node.right);
            int max = leftDeep > rightDeep ? leftDeep : rightDeep;
            return max + 1;
        }
    }

    public List<TreeNode> preIterator(){
        return preIterator(root);
    }

    private List<TreeNode> preIterator(TreeNode node){
        List<TreeNode> list = new ArrayList<TreeNode>();
        list.add(node);
        if(node.left != null){
            list.addAll(preIterator(node.left));
        }
        if(node.right != null){
            list.addAll(preIterator(node.right));
        }
        return list;
    }

    public List<TreeNode> inIterator(){
        return inIterator(root);
    }

    private List<TreeNode> inIterator(TreeNode node){
        List<TreeNode> list = new ArrayList<TreeNode>();
        if(node.left != null){
            list.addAll(inIterator(node.left));
        }
        list.add(node);
        if(node.right != null) {
            list.addAll(inIterator(node.left));
        }
        return list;
    }

    public List<TreeNode> postIterator(){
        return postIterator(root);
    }

    private List<TreeNode> postIterator(TreeNode node){
        List<TreeNode> list = new ArrayList<TreeNode>();
        if(node.left != null){
            list.addAll(postIterator(node.left));
        }
        if(node.right != null){
            list.addAll(postIterator(node.right));
        }
        list.add(node);
        return list;
    }

    public List<TreeNode> breadthFirst(){
        Queue<TreeNode> queue = new ArrayDeque<TreeNode>();
        List<TreeNode> list = new ArrayList<TreeNode>();
        if(root != null){
            queue.offer(root);
        }
        while(!queue.isEmpty()){
            list.add(queue.peek());
            TreeNode p = queue.poll();
            if(p.left != null){
                queue.offer(p.left);
            }
            if(p.right != null){
                queue.offer(p.right);
            }
        }
        return list;
    }

}
View Code

 


遍历二叉树
遍历二叉树指的是按照某种规律依次访问二叉树上的每个节点,对二叉树的遍历过程就是将非线性结构的二叉树的节点排列在一个线性序列上的过程。
如果采用顺序结构来保存二叉树,程序遍历二叉树将非常容易,无需进行任何思考,直接遍历底层数组即可。

如果采用链表保存二叉树的节点,则有以下两类遍历方式:
深度优先遍历:这种遍历算法将先访问到树中最深层次的节点
广度优先遍历:这种遍历算法将逐层访问每层的节点,广度优先遍历又被称为按层遍历。

对于深度遍历的算法而言,又可以分为以下三种:
先序遍历(先父节点,再左子节点、右子节点)

   public List<TreeNode> preIterator(){
        return preIterator(root);
    }

    private List<TreeNode> preIterator(TreeNode node){
        List<TreeNode> list = new ArrayList<TreeNode>();
        list.add(node);
        if(node.left != null){
            list.addAll(preIterator(node.left));
        }
        if(node.right != null){
            list.addAll(preIterator(node.right));
        }
        return list;
    }

 


中序遍历(左父右)

 public List<TreeNode> inIterator(){
        return inIterator(root);
    }

    private List<TreeNode> inIterator(TreeNode node){
        List<TreeNode> list = new ArrayList<TreeNode>();
        if(node.left != null){
            list.addAll(inIterator(node.left));
        }
        list.add(node);
        if(node.right != null) {
            list.addAll(inIterator(node.left));
        }
        return list;
    }

 


后序遍历(左右父)

 public List<TreeNode> postIterator(){
        return postIterator(root);
    }

    private List<TreeNode> postIterator(TreeNode node){
        List<TreeNode> list = new ArrayList<TreeNode>();
        if(node.left != null){
            list.addAll(postIterator(node.left));
        }
        if(node.right != null){
            list.addAll(postIterator(node.right));
        }
        list.add(node);
        return list;
    }

 


因为二叉树的定义本身就具有“递归性”,所以深度优先遍历时能非常方便的利用地柜来遍历每一个节点,依次遍历左根右三部分即可遍历整个二叉树。

广度优先遍历:
借助FIFO(先进先出)特征的队列来实现:
1、建立一个队列,把树的根节点压入队列
2、从队列中弹出一个节点(第一次弹出的就是根节点),然后把该节点的左、右节点压入队列,如果没有子节点,说明已经到达叶子节点了。
3、用循环重复执行第二步,知道队列为空。当队列为空时,说明所有的叶子节点(深度最深的层)都已经经过了队列,也就完成了遍历

 public List<TreeNode> breadthFirst(){
        Queue<TreeNode> queue = new ArrayDeque<TreeNode>();
        List<TreeNode> list = new ArrayList<TreeNode>();
        if(root != null){
            queue.offer(root);
        }
        while(!queue.isEmpty()){
            list.add(queue.peek());
            TreeNode p = queue.poll();
            if(p.left != null){
                queue.offer(p.left);
            }
            if(p.right != null){
                queue.offer(p.right);
            }
        }
        return list;
    }

 


森林、树和二叉树的转换
由于二叉树是一种更“确定”的数据结构,因此不管是存储、增加、删除还是遍历节点,程序都能更简单、方便的实现。繁殖,由于树的每个节点具有数不确定的子节点,因此程序实现起来更加复杂。
为了充分利用二叉树的简单易用性,可以将普通树转换成二叉树,以二叉树形式来保存普通书,当成宿需要树的时候,再将二叉树转换成为普通树。
森林其实很简单,如果将一棵普通树的根节点去掉,这棵树就变成了森林。

森林、树、二叉树的转换:
多叉树转二叉树:


原则:兄弟变右,左变子,旋转
实现操作:
1、加虚线:同一个父节点的响铃兄弟节点之间加虚线
2、抹实现:每个节点只保留它与最左子节点的连线,与其他子节点的连线都被抹掉
3、旋改实:虚线改为实线
森林只要想象成根节点被删除的多叉树,即可完成与二叉树的互换。
二叉树恢复对应的多叉树,森林:

1、加虚线:若某节点I时父节点的左子节点,则将该节点I的右子孩子链的所有节点分别与节点I的父节点添加连线
2、抹线:把有虚线的节点与原父节点的连线抹去
3.整理:虚线改成实线并按层排列

二叉树可以和多叉树之间进行自由转换,因此可以得到普通树的另外一种保存方式:以二叉树的形式保存多叉树,实际需要的时候再将二叉树转换成为普通树。一般选择三叉链表存储二叉树,这样得到的二叉树操作更方便而且进行二叉树与多叉树的转换更加方便

关于二叉树与多叉树的转换,参考:http://www.cnblogs.com/Lanly/p/6305766.html

posted @ 2017-03-13 11:10  guodaxia  阅读(207)  评论(0)    收藏  举报