欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

java树数据结构(二叉树)

程序员文章站 2022-05-06 23:46:11
...

树的基本概念

概念定义:

java树数据结构(二叉树)

java树数据结构(二叉树)

 注:高度与深度的概念辨析

1. 对于节点来说:深度是从根节点往下,高度是从叶子节点向上。同一个节点的深度与高度有可能不同(根节点与叶子节点初始值为0为1都有定义,不同书籍的定义不同)

2. 对于整棵树来说:最深的叶结点的深度就是树的深度;树根的高度就是树的高度。这样树的高度和深度是相等的。

节点之间的关系:

对于任何非空二叉树, t, 如果 n0 是叶节点的数量, n2 是度数2的节点数, n0=n2+1即:n0+n1+n2=2*n2+n1+1(根节点)

同样的队医三叉树来说,n0=2*n3+n2+1 即:n0+n1+n2+n2=3*m3+2*n2+n1+1(根节点)

以此类推:对于N叉树,n0=(n-1)*Xn+(n-2)*X(n-1)+...+X2+1

二叉树的特殊种类

满二叉树:除叶子结点外的所有结点均有两个子结点。节点数达到最大值。所有叶子结点必须在同一层上.

给定高度k的满二叉树,具有2^(k+1)-1个节点

完全二叉树:若设二叉树的深度为h,除第 h 层外,其它各层 (1~h-1) 的结点数都达到最大个数,第 h 层所有的结点都连续集中在最左边,这就是完全二叉树。

 

二叉树的实现

import java.util.LinkedList;
import java.util.Queue;
import java.util.Stack;


public class Tree<Type> {
	private TreeNode<Type> root;
	public Tree() {
		this.root=null;
	}
	public Tree(TreeNode<Type> node) {
		this.root=node;
	}
	public boolean isEmpty() {
		return this.root==null;
	}
	public Type getRoot() {
		if(root==null) {
			throw new NullPointerException("树为空");
		}else {
			return this.root.data;
		}
	}
	public TreeNode<Type> leftChild(TreeNode<Type> node){
		if(node==null) {
			throw new NullPointerException("此节点不存在");
		}else {
			return node.lchild;
		}
	}
	public TreeNode<Type> rightChild(TreeNode<Type> node){
		if(node==null) {
			throw new NullPointerException("此节点不存在");
		}else {
			return node.rchild;
		}
	}
	public TreeNode<Type> getParnts(TreeNode<Type> node){
		if(node==null) {
			throw new NullPointerException("此节点不存在");
		}else {
			return parent(root,node);
		}
	}
	public TreeNode<Type> parent(TreeNode<Type> node,TreeNode<Type> current){
		if(node==null) {
			return null;
		}else {  
			if(node.lchild==current || node.rchild==current) {
				return node;
			}else {
				TreeNode<Type> n=parent(node.lchild,current);
				if(n!=null) {
					return n;
				}else {
					return parent(node.rchild,current);
				}
				
			}
		}
	}
	public TreeNode<Type> find(Type data){
		return findNotNull(root,data);	
	}
	public TreeNode<Type> findNotNull(TreeNode<Type> node,Type data){
		if(node==null) {
			return null;
		}else {
			if(data!=null) {
				if(data.equals(node.data)) {
					return node;
				}else {
					TreeNode<Type> n=findNotNull(node.lchild,data);
					if(n!=null) {
						return n;
					}else {
						return findNotNull(node.rchild,data);
					}
				}
			}else {
				if(node.data==null) {
					return node;
				}else {
					TreeNode<Type> n=findNotNull(node.lchild,data);
					if(n!=null) {
						return n;
					}else {
						return findNotNull(node.rchild,data);
					}
				}
			}
		}
	}
	public int size() {//试一下第二种方法
		return get_s(root);
	}
	public int get_s(TreeNode<Type> node) {
		if(node==null) {
			return 0;
		}else {
			return get_s(node.lchild)+get_s(node.rchild)+1;
		}
	}
	public int height() {
		return get_h(root);
	}
	public int get_h(TreeNode<Type> node) {
		if(node==null) {
			return -1;
		}else {
			return Math.max(get_h(node.lchild)+1,get_h(node.rchild)+1);
		}
	}
	//前序递归遍历
	public  void pre_print() {
		pre_print(root);
		System.out.println();
	}
	public void pre_print(TreeNode<Type> node) {
		if(node!=null) {
			System.out.print(node.data+"-->");
			pre_print(node.lchild);
			pre_print(node.rchild);
		}
		
	}
	//前序非递归遍历
	public void preN_print() {  
		Stack<TreeNode<Type>> s=new Stack<TreeNode<Type>>();
		TreeNode<Type> node=root;
		while(node!=null || !s.isEmpty()) {
			while(node!=null) {
				System.out.print(node.data+"-->");
				s.push(node);
				node=node.lchild;
			}
			if(!s.isEmpty()) {
				node=s.peek();
				s.pop();
				node=node.rchild;
			}
		}
		System.out.println();
	}
	
	//中序遍历
	//终须递归遍历
	public void ind_print() {
		ind_print(root);
		System.out.println();
	}
	public void ind_print(TreeNode<Type> node) {
		if(node!=null) {
			ind_print(node.lchild);
			System.out.print(node.data+"-->");
			ind_print(node.rchild);
		}
		
	}
	//中序非递归遍历
	public void indN_print() {
		Stack<TreeNode<Type>> s=new Stack<>();
		TreeNode<Type> node=this.root;
		while(node!=null || !s.isEmpty()) {
			while(node!=null) {
				s.push(node);
				node=node.lchild;
			}
			if(!s.isEmpty()) {
				node=s.peek();
				s.pop();
				System.out.print(node.data+"-->");
				node=node.rchild;
			}
		}
		System.out.println();
 	}
	
	//后序遍历
	//递归后序遍历
	public void post_print() {
		post_print(root);
		System.out.println();
	}
	public void post_print(TreeNode<Type> node) {
		if(node!=null) {
			post_print(node.lchild);
			post_print(node.rchild);
			System.out.print(node.data+"-->");
		}
		
	}
	//非递归后序遍历
	public void postN_print() {  //https://www.cnblogs.com/gaopeng527/p/5451176.html
		Stack<TreeNode<Type>> s1=new Stack<>();
		Stack<Integer> s2=new Stack<>();
		TreeNode<Type> node=this.root;
		while(node!=null || !s1.isEmpty()) {
			while(node!=null) {
				s1.push(node);
				s2.push(0);
				node=node.lchild;
			}
			while(!s2.isEmpty()&&s2.peek()==1) {
				s2.pop();
				System.out.print(s1.pop().data+"-->");
			}
			if(!s1.isEmpty()) {
				s2.pop();
				s2.push(1);
				node=s1.peek();
				node=node.rchild;
				
			}
		}
		System.out.println();
	}
	
	//广度优先遍历
	public void lev_print() {
		Queue<TreeNode<Type>> q = new LinkedList<TreeNode<Type>>();
		q.add(root);
		while(!q.isEmpty()) {
			TreeNode<Type> node=q.peek();
			System.out.print(node.data+"-->");
			q.remove();
			if(node.lchild!=null) {
				q.add(node.lchild);
			}
			if(node.rchild!=null) {
				q.add(node.rchild);
			}
		}
		System.out.println();
	}
	
	
	
	
	
}
class TreeNode<Type>{
	public Type data;
	public TreeNode<Type> lchild;
	public TreeNode<Type> rchild;
	public TreeNode() {
		this.data=null;
		this.lchild=this.rchild=null;
	}
	public TreeNode(Type data,TreeNode<Type> lchild,TreeNode<Type> rchild){
		this.data=data;
		this.lchild=lchild;
		this.rchild=rchild;
	}
	public TreeNode(Type data) {
		this.data=data;
	}
	
}
class Test{
	public static void main(String args[]) {
		TreeNode<Character> b1 = new TreeNode<>('a');  
		TreeNode<Character> b2 = new TreeNode<>('b');  
		TreeNode<Character> b3 = new TreeNode<>('c');  
		TreeNode<Character> b4 = new TreeNode<>('d');  
		TreeNode<Character> b5 = new TreeNode<>('e'); 
		TreeNode<Character> b6 = new TreeNode<>('f'); 
		TreeNode<Character> b7 = new TreeNode<>('g'); 
		TreeNode<Character> b8 = new TreeNode<>('h'); 
		b1.lchild = b2;  
        b1.rchild = b3;  
        b2.lchild = b4;  
        b2.rchild = b5; 
        b5.rchild = b6;
        b3.rchild = b7;
        b7.lchild = b8;
//        		a
//        	  /	  \
//        	 b     c
//        	/  \    \
//         d    e    g
//               \   /
//               f  h
        Tree<Character> tree=new Tree<>(b1);
        System.out.println("树为空?"+tree.isEmpty());
        System.out.println("树共有"+tree.size()+"个节点");
        System.out.println("树的高度为"+tree.height());
        System.out.println("输的根节点元素为:"+tree.getRoot());
        System.out.println("元素"+b5.data+"的父节点为:"+tree.getParnts(b5).data);
        System.out.println("元素"+b5+"的内容为:"+tree.find(b5.data).data);
        System.out.println("前序遍历:");
        tree.pre_print();
        tree.preN_print();
        System.out.println("中序遍历:");
        tree.ind_print();
        tree.indN_print();
        System.out.println("后序遍历:");
        tree.post_print();
        tree.postN_print();
        System.out.println("广度优先遍历:");
        tree.lev_print();
		
	}
}

 

线索树(Threaded Tree)

在一棵只有n个结点的二叉树中,假设有n个结点,那么就有2n个指针域,因为二叉树只用到了其中的n-1个结点,所以只要利用剩下的n+1个节点,我们就能把中需遍历时所得到的中序二叉树保存下来,以便下次访问。

中序二叉树的指针域有两种类型:一是用于链接二叉树本身;二是用于链接中序遍历序列。这类型的指针,左指指向中序遍历时节点顺序的前驱,右指针指向中序遍历时的后继。为了区别这两种指针域类型,我们在树的结点中要加上两个标志lchild和rchild,分别标志左右指针域的类型。 其数据结构如下:

java树数据结构(二叉树)

其中:

LTag = 0 lchild域指示节点的左孩子;LTag=1 lchild域指示节点的前驱。

RTag = 0 rchild域指示节点的左孩子;RTag=1 rchild域指示节点的前驱。

1. 前序线索树(依靠先序遍历来寻找前驱节点和后继节点):

java树数据结构(二叉树)

2.中序线索树(依靠中序遍历来寻找前驱节点和后继节点)

java树数据结构(二叉树)

3.后序线索树(依靠后序遍历确定前驱节点和后继结点)

java树数据结构(二叉树)

 

哈夫曼树

定义:

当用 n 个结点(都做叶子结点且都有各自的权值)试图构建一棵树时,如果构建的这棵树的带权路径长度最小,称这棵树为“最优二叉树”,有时也叫“赫夫曼树”或者“哈夫曼树”。

在构建哈弗曼树时,要使树的带权路径长度最小,只需要遵循一个原则,那就是:权重越大的结点离树根越近,哈夫曼树一定为二叉树

概念:

1. 路径:在一棵树中,一个结点到另一个结点之间的通路,称为路径

2. 结点的路径之和:到根节点的路径之和。为各个节点的路孔之和相加,并不是树的路径之和。
3. 路径长度:在一条路径中,每经过一个结点,路径长度都要加 1 。例如在一棵树中,规定根结点所在层数为1层,那么从根结点到第 i 层结点的路径长度为 i - 1 。
4. 结点的权:给每一个结点赋予一个新的数值,被称为这个结点的权。
5. 结点的带权路径长度:指的是从根结点到该结点之间的路径长度与该结点的权的乘积,WPL= java树数据结构(二叉树) wi  ´ Li

哈夫曼树的应用:

哈夫曼编码

 

树与森林的转化

1. 先序遍历树林正好等同于按先序遍历对应的二叉树;后序遍历森林正好等同于中序对应的二叉树

2. 在进行树与二叉树的转化的时候。二叉树的左节点为原树的第一个叶子节点。二叉树的右子节点为原树的第一个兄弟节点。在进行森林的准话的时候,多个度一次加到二叉树的右子节点中。

3. 转化实例

若有不足,请多多指教。

 

 

 

相关标签: 数据结构