二叉树的结构、构建及递归遍历算法 - 树结构 | 第1篇【java实现】

导语:要知道 ‘树’ 在整个数据结构体系中十分重要,它是某些算法乃至框架的架构基石。数据的逻辑结构大致可分为两类,一个是线性结构,另一个是非线性结构,前者有线性表、栈和队列等,后者主要是树和图结构。线性结构即一对一的,每一层都只有一个结点,可以理解为“单传”;至于非线性结构那就是一对多(树)和多对多(图),以树为例,可以想象成在一对一线性结构基础上将每层的元素个数横向拓展(根节点除外),即理解为“非单传”。

目录

一、树及二叉树的定义和术语 

1. 定义:

2. 树的表示方式

3. 基本术语

二、二叉树存储结构、性质和特点  

1. 存储结构 

2. 性质

3. 二叉树特点

三、二叉树的构建 

 源码: 

 控制台输出:

四、二叉树的递归遍历

源码: 

 控制台输出: 


一、树及二叉树的定义和术语 

        在大多数算法比赛、面试以及应用中,基本以二叉树为多,可谓是灵魂,但在此之前应该简单罗列一下树的相关术语;

1. 定义:

     树:是n个结点的有限集(n≥0)。若n = 0,为空树;若n > 0,则它满足以下条件:                               (1)有且仅有一个特定的称为根(root)的结点。                                                                               (2)其余结点可分为m (m≥0)个互不相交的有限集T1,T2,T3,...,Tm,其中每一个集合本身又是一棵树,并称为根的子树(SubTree).

    二叉树:即n(n≥0)个结点的有限集,它或者是空集(n=0), 或者由一个根结点及两棵互不相交的分别称作这个根的左子树和右的二叉树组成。

        无论对于树还是二叉树的定义,都是基于递归的思想,先假设有一棵k层二叉树,首先只看前两层,第1层就一个根节点root,第2层有两个结点,它们是root的左右两个子结点,那么第3层怎么办?按照递归的意思, 把第2层的两个结点看作第3层结点的根root1和root2,就是将root1(或root2)开始以及它下面的所有子结点也看作一棵二叉树,第4层、5层也是如此,直到第k层。也就是一棵二叉树的所有结点都可以认为是root根,而且它的左右子结点又可以分别作为一棵树(可以为空树)。

2. 树的表示方式

  1. 倒置树结构(树形表示法)
  2. 嵌套集合表示法(文氏图表示法)
  3. 广义表形式(嵌套括号表示法)
  4. 凹入表示法(目录结构) 

3. 基本术语

  • 结点(即数据元素及指向其他结点的分支)、根节点(仅有一个)
  • 度:结点的子树个数。  
  • 树的度:树内各结点度的最大值。
  • 叶子(终端结点 | 度为0的结点);
  • 分支结点(非终端结点 | 度不等于0的结点);
  • 内部节点(除根结点以外的分支节点);
  • 孩子:一结点A的子树的根节点;
  • 双亲:结点A为其子树的根节点的双亲结点;
  • 兄弟:双亲相同的结点;
  • 堂兄弟:父亲是兄弟关系或堂兄弟关系的结点;
  • 结点的祖先:该结点由下到上的所有结点;
  • 结点的子孙:该结点由上到下的所有结点;
  • 树的深度:树中结点的最大层次。
  • 有序树:树中结点的各子树从左到右有次序。
  • 无序树:树中结点的各子树没有次序。
  • 森林:是m ( m≥O)棵互不相交的树的集合。
  • 树-->深林:把根结点删除树就变成了森林。
  • 有序树:树中结点的各子树从左到右有次序。
  • 满二叉树:一棵深度为k且有(2^k)-1个结点的二叉树称为满二叉树。
  • 完全二叉树:深度为k的具有n个结点的二叉树,当且仅当其每一个结点都与深度为k的满二叉树中编号为1~n的结点一一对应时,称之为完全二叉树。

二、二叉树存储结构、性质和特点  

1. 存储结构 

    顺序存储结构:
        即使用数组存储结点元素。                                                                                                                特点:结点间关系蕴含在其存储位置中浪费空间,适于存满二叉树和完全二叉树。
    链式存储结构:
       二叉链表:Lchild,Data,Rchild
       三叉链表:Lchild,Data,Rchild,Parent 

2. 性质

        性质1:在二叉树的第i层上至多有2^(i-1)个结点(i≥1)。
        性质2:深度为k的二叉树至多有(2^k)- 1个结点(k≥1)。
        性质3:对任何一棵二叉树T,如果其叶子数为N0,度为2的结点数为N2,则N0 = N2+1。性质3推导如下:

          
        性质4:具有n个结点的完全二叉树的深度为⌊log2n⌋+1。(性质4表明了完全二叉树的结点数n与树深度k之间的关系)
        性质5:如果对一棵有n个结点的完全二叉树(深为⌊log2n⌋+1)的结点按层序编号,则对任一结点i(1≤isn),有:
            (1)如果i=1,则结点i是二叉树的根,无双亲;如果i>1,则其双亲是结点⌊i/2⌋。
            (2)如果2i> n,则结点i为叶子结点,无左孩子;否则,其左孩子是结点2i。
            (3)如果2i+1>n,则结点i无右孩子;否则,其右孩子是结点2i+1。  
     (性质5表明了完全二叉树的孩子结点编号与双亲结点编号之间的关系) 

3. 二叉树特点

        1、每个结点最多有俩孩子(二叉树中不存在度大于2的结点)。
        2、子树有左右之分,其次序不能颠倒。
        3、二叉树可以是空集合,根可以有空的左子树或空的右子树。 

三、二叉树的构建 

        本专栏算法使用Java代码实现,对于树结点只需要一个类表示(二叉链表形式):以字符串类型创建二叉树,以字符串"."记为空结点,创建完成后返回TreeNode结点类型的树根root。

 源码: 

public class TreeCreate {
    // 结点类
    public class TreeNode {
	    public String data;
	    public TreeNode Lchild;
    	public TreeNode Rchild;

	    public TreeNode(String data) {
	    	this.data = data;
    	}
    }

	private static String[] tree ;
	private static int i ;
	// 先序递归创建二叉树
	public static TreeNode PreOrderCreate() {
	    TreeNode bt = null;
	    String node = tree[i++];
	    if(node == ".") {
	        return bt;
	    }else {
	        bt = new TreeNode(node);
	        bt.Lchild = PreOrderCreate();
	        bt.Rchild = PreOrderCreate();
	        return bt;
	    }
	}
	public TreeCreate(String[] str) {
		this.tree = str;
	}
	
	
	public static void main(String[] args) {
		String[] strings = {"A","B","C",".",".","D","E",".","G",".",".","F",".",".","."}; 
		TreeNode root = new TreeCreate(strings).PreOrderCreate();
		// 树的打印,可私我获取源码
		PrintBinaryTree.printTree(head);
	}
	
}

  控制台输出:

四、二叉树的递归遍历

        递归思想与树的定义完全相同,把一棵二叉树的所有结点都可以认为是root根,且把它和它的的子孙结点又可以作为一棵树,直到叶子为止,然后对每一个结点都调用自身遍历算法,即为递归遍历。 

        1. 先序遍历:对每一棵树(包括子树)的访问次序都是 根 --> 左 --> 右;        

        2. 中序遍历:对每一棵树(包括子树)的访问次序都是 左 --> 根 --> 右;        

        3. 后序遍历:对每一棵树(包括子树)的访问次序都是 左 --> 右 --> 根

源码: 

public class TraverseRecursive {
	
	public static void main(String[] args) {
		// TODO Auto-generated method stub
		
        String[] strings = {"A","B","C",".",".","D","E",".","G",".",".","F",".",".","."}; 
		TreeNode head = new TreeCreate(strings).PreOrderCreate();
	    
	    System.out.println("先序:");
		PreOrderTraversal(head);
		System.out.println("\n后序:");
		PostOrderTraversal(head);
		System.out.println("\n中序:");
		InOrderTraversal(head);

	}
	
	public static void PreOrderTraversal(TreeNode root){
		// 递归先序遍历
    	if (root != null) {
    		Vist(root);
            PreOrderTraversal(root.Lchild);
            PreOrderTraversal(root.Rchild);
        }else {
			System.out.print("^");
		}
    }
    
    public static void InOrderTraversal(TreeNode root){
    	// 递归中序遍历
    	if (root != null) {
            InOrderTraversal(root.Lchild);
            Vist(root);
            InOrderTraversal(root.Rchild);
        }else {
			System.out.print("^");
		}
    }
 
    public static void PostOrderTraversal(TreeNode root){
    	// 递归后序遍历
    	if (root != null) {
            PostOrderTraversal(root.Lchild);
            PostOrderTraversal(root.Rchild);
            Vist(root);
        }else {
			System.out.print("^");
		}
    }
    
    public static void Vist(TreeNode tree) {
    	if(tree != null)
    		System.out.print(tree.data+"  ");
    }

}

以上面创建的二叉树为例,三种方式分别遍历。

 控制台输出: 

声明:相关源码及资源文件已上传云盘文件夹,若要获取,欢迎私信或在评论区沟通。

          承蒙各位的指正,一起成长!

猜你喜欢

转载自blog.csdn.net/kndjg/article/details/125881061