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,分别标志左右指针域的类型。 其数据结构如下:

image_thumb

其中:

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

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

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

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

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

 

哈夫曼树

定义:

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

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

概念:

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

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

哈夫曼树的应用:

哈夫曼编码

 

树与森林的转化

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

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

3. 转化实例

若有不足,请多多指教。

 

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值