数据结构之树

时光怂恿深爱的人放手 提交于 2019-12-14 07:26:28

数据结构之树(含代码)

树的基本概念

  1. 子树的个数没有限制,但它们一定是互不相交的
  2. 树的结点包含一个数据元素及若干指向其子树的分支;结点拥有的子树数称为结点的度;度为0的结点称为叶结点或终端结点;度不为0的结点称为非终端结点或分支结点;除根结点之外,分支结点也称为内部结点;树的度是树内各结点的度的最大值。
  3. 结点的层次从根开始定义起;树中结点的最大层次称为树的深度或高度;
  4. 如果将树中结点的各子树看成从左至右是有次序的,不能互换的,则称该树为有序树,否则称为无序树
  5. 双亲表示法
    每个结点都有data和left,right,data是数据,存储结点的数据信息;而lef,rig是指针,存储该结点的双亲在数组中的下标;根结点没有双亲,所以指针域为-1;

二叉树

  1. 二叉树是n个结点的有限集合,该集合或者为空集,或者由一个根结点和两棵互不相交的、分别成为根结点的左子树和右子树的二叉树组成
  2. 二叉树的特点:
    每个结点最多有两棵子树,所以二叉树中不存在度大于2的结点
    左子树和右子树是有顺序的,次序不能任意颠倒
    即使树中某结点只有一棵子树,也要区分它是左子树还是右子树
  3. 满二叉树:在一棵二叉树中,如果所有分支结点都存在左子树和右子树,并且所有叶子都在同一层上
    特点:
    叶子只能出现在最下一层
    非叶子结点的度一定是2
    在同样深度的二叉树中,满二叉树的结点个数最多,子树最多
  4. 完全二叉树:如果编号为i的结点与同样深度的满二叉树中编号为i的结点在二叉树中位置完全相同,则这棵二叉树称为完全二叉树(可以少,但位置必须一样)按层序编号,没有也要编号
    特点:
    叶子结点只能出现在最下两层
    最下层的叶子一定集中在左部连续位置
    倒数二层,若有叶子结点,一定都在右部连续位置
    如果结点度为1,则该结点只有左孩子,即不存在只有右子树的情况
    同样结点数的二叉树,完全二叉树的深度最小
  5. 二叉树的性质
    (1)在二叉树的第i层上至多有2的i-1次方个结点
    (2)深度为k的二叉树至多有2的k-1次方个结点
    (3)对任何一棵二叉树T,如果其终端结点数为n0,度为2的节点数为n2,则n0=n2+1
    (4)具有n个结点的完全二叉树的深度为log2(底)n+1
  6. 顺序存储结构一般只用于完全二叉树
  7. 二叉树每个结点最多有两个孩子,所以为它设计一个数据域和两个指针域,称这样的链表叫做二叉链表
  8. 二叉树的遍历是指从根结点出发,按照某种次序依次访问二叉树中所有结点,使得每个结点被访问一次且仅被访问一次
  9. 前序遍历:若二叉树为空,则空操作返回,否则先访问根结点,然后前序遍历左子树,再前序遍历右子树
    中序遍历:若树为空,则空操作返回,否则从根结点开始(注意并不是先访问根结点),中序遍历根结点的左子树,然后是访问根结点,最后中序遍历右子树
    中序遍历非递归遍历算法
    遇到一个结点,就把它压栈,并去遍历它的左子树;
    当左子树遍历结束后,从栈顶弹出这个结点并访问它;
    然后按其右指针再去中序遍历该结点的右子树;

    后序遍历:若树为空,则空操作返回,否则先从左到右先叶子后结点的方式遍历访问左右子树,最后是访问根结点。

对于下面的图:

先序遍历:ABDECF
中序遍历:DBEAFC
后序遍历:DEBFCA

  1. 层序遍历:若树为空,则空操作返回,否则从树的第一层,也就是根结点开始访问,从上而下逐层遍历,在同一层中,按从左到右的顺序对结点逐个访问
  2. 已知前序和中序可以推后序,唯一确定一棵二叉树
    已知后序和中序可以推前序,唯一确定一棵二叉树,其他则不可以
    先序和中序遍历序列来确定一棵二叉树:
    根据先序遍历序列第一个结点确定根结点;
    根据根结点在中序遍历序列中分割出左右两个子序列;
    对左子树和右子树分别递归使用相同的方法继续分解;
  3. 将二叉树中每个结点的空指针引出一个虚结点,其值为一特定值,这种处理后的二叉树为原二叉树的扩展二叉树
  4. 指向前驱和后继的指针称为线索,加上线索的二叉链表称为线索链表,相应的二叉树就称为线索二叉树
  5. 对二叉树以某种次序遍历使其变为线索二叉树的过程称做是线索化,线索化的过程就是在遍历的过程中修改空指针的过程
  6. 如果所用的二叉树需经常遍历或查找结点时需要某种遍历序列中的前驱和后继,那么采用线索二叉链表的存储结构就是非常不错的选择
  7. 将树转换为二叉树的步骤:
    加线;在所有兄弟结点之间加一条连线
    去线;对树中每个结点,只保留它与第一个孩子结点的连线,删除它与其他孩子结点之间的连线
    层次调整;以树的根结点为轴心,将整棵树顺时针旋转一定角度
  8. 从树中一个结点到另一个结点之间的分支构成两个结点之间的路径,路径上的分支数目称做路径长度,树的路径长度就是从树根到每一结点的路径长度之和
  9. 如果考虑到带权的结点,结点的带权的路径长度为从该结点到树根之间的路径长度与结点上权的乘积,树的带权路径长度为树中所有叶子结点的带权路径长度之和。
  10. 构造二叉树代码:
    (1)节点类:
public class BTNode {
 private int data;
 private BTNode left;
 private BTNode right;
 public BTNode(){};
 public BTNode(int data,BTNode left,BTNode right) {
  this.data=data;
  this.left=left;
  this.right=right;
 }
 public void setdata(int data) {
  this.data=data;
 }
 public int getdata() {
  return this.data;
 }
 public void setleft(BTNode left) {
  this.left=left;
 }
 public BTNode getleft() {
  return this.left;
 }
 public void setright(BTNode right) {
  this.right=right;
 }
 public BTNode getright() {
  return this.right;
 }
}

(2)构造二叉树(我用的先序构造)与遍历:

public class preorderbinarytree {
 public static BTNode precreate(BTNode btnode) {
  Scanner in=new Scanner(System.in);
  System.out.println("请输入节点的值");
  int value=in.nextInt();
  if(value!=0) {
      btnode=new BTNode();
   btnode.setdata(value);
   btnode.setleft(precreate(btnode.getleft()));
   btnode.setright(precreate(btnode.getright()));
  }
  else
   btnode=null;
  return btnode; 
 }
 public static void visit(BTNode btnode) {
  if(btnode!=null) {
   System.out.print(btnode.getdata()+";");
  }
 }
 public static void preorder(BTNode btnode) {
  //如果是其他顺序则将visit的顺序改变即可,同样,如果想改变输入的顺序(即建立顺序)将create中方法改变即可
  if(btnode!=null) {
  visit(btnode);
  preorder(btnode.getleft());
  preorder(btnode.getright());}
 }
 public static void main(String args[]) {
  BTNode tree=new BTNode();
  tree=precreate(tree);
  preorder(tree);}}

赫夫曼树

  1. 带权路径长度WPL最小的二叉树称做赫夫曼树
    构造赫夫曼树:
    根据给定的n个权值构成n棵二叉树的集合F,其中每棵二叉树Ti中只有一个带权为wi根结点,其左右子树均为空
    在F中选取两棵根结点的权值最小的树作为左右子树构造一棵新的二叉树,且置新的二叉树的根结点的权值为其左右子树上根结点的权值之和
    在F中删除这两棵树,同时将新得到的二叉树加入F中
    重复2和3步骤,直到F只含一棵树为止,这棵树便是赫夫曼树
  2. 若要设计长短不等的编码,则必须是任一字符的编码都不是另一个字符的编码的前缀,这种编码称做前缀编码
  3. 规定赫夫曼树的左分支代表0,右分支代表1,则从根结点到叶子结点所经过的路径分支组成的0和1的序列便为该结点对应字符的编码,这就是赫夫曼编码。

AVL树

  1. 平衡二叉树:空树,或者任一结点左、右子树高度差的绝对值不超过1
  2. 代码(没有写delete操作,过几天补上):
public class AVLTree<T extends Comparable <T>> {
   public static final int Cheight=1;
   public static class AVLTnode<T extends Comparable <T>> {
  	T key;
  	AVLTnode<T> left;
  	AVLTnode<T> right;
  	int height;
 	 public AVLTnode(T key) {
 	  this(key,null,null);
 	 }
   public AVLTnode(T key,AVLTnode<T> left,AVLTnode<T> right) {
   	this.height=0;
   	this.key=key;
   	this.left=left;
   	this.right=right;
  	 }
 	}
   private AVLTnode<T> root;
   public int getheight(AVLTnode<T> root) {
  	return root==null?-1:root.height;
    }
   public AVLTnode<T> insert(T key,AVLTnode<T> root){
  	if(root==null) {
   		return new AVLTnode<T>(key);
  	}
  	int cmp=key.compareTo(root.key);
  	if(cmp<0) {
   		root.left=insert(key,root.left);
  	}
  	else if(cmp>0){
  		 root.right=insert(key,root.right);
 	 }
 	 else {
   		System.out.println("THE KEY HAS BEEN STORED");
 	 }
  		return balance(root);
 }
   public AVLTnode<T> balance(AVLTnode<T> root){
 	 if(root==null) {
  	 return null;
  	}
  	if(getheight(root.left)-getheight(root.right)>Cheight) {
   	if(getheight(root.left.left)>getheight(root.left.right)) {
          root=rotatewithright(root);
   	}
  	 else{
   	 root=rotatewithleftright(root);
   	 }
   	}
 	 else if(getheight(root.right)-getheight(root.left)>Cheight){
   		if(getheight(root.right.right)>getheight(root.right.left)) {
   			 root=rotatewithleft(root);
   		}
  		 else {
   			 root=rotatewithrightleft(root);
   		}
  	}
 	 root.height=Math.max(getheight(root.left), getheight(root.right))+1;
  	return root;
 }
   private AVLTnode<T> rotatewithleft(AVLTnode<T> node5){
 	 AVLTnode<T> node4=node5.right;
  	node5.right=node4.left;
 	 node4.left=node5;
 	 node5.height=Math.max(getheight(node5.left), getheight(node5.right))+1;
  	node4.height=Math.max(getheight(node4.left), getheight(node4.right))+1;
  	return node4;
 	}
   private AVLTnode<T> rotatewithright(AVLTnode<T> node5){
  	AVLTnode<T> node4=node5.left;//拔高
  	node5.left=node4.right;
  	node4.right=node5;
  	node5.height=Math.max(getheight(node5.left), getheight(node5.right))+1;
  	node4.height=Math.max(getheight(node4.left), getheight(node4.right))+1;
  	return node4;
   }
   private AVLTnode<T> rotatewithrightleft(AVLTnode<T> node5){
  	node5.right=rotatewithright(node5.right);
  	return rotatewithleft(node5);
   }
   private AVLTnode<T> rotatewithleftright(AVLTnode<T> node5){
 	 node5.left=rotatewithleft(node5.left);
 	 return rotatewithright(node5);
   }
    public static void main(String[] args) {
     AVLTree<Integer> avl=new AVLTree<Integer>();
     for(int i=0;i<5;i++) {
      avl.root=avl.insert(i, avl.root);
     }
     System.out.println(avl.getheight(avl.root));
    }
}
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!