数据结构——树与二叉树

来源:互联网 发布:康佳网络电视售后电话 编辑:程序博客网 时间:2024/05/16 00:38
1.树的基本概念
1)树的定义
是若干节点的集合,由唯一的根和若干互不相交的子树组成的
树是一种非线性的数据结构
树的节点书目可以为0,此时为空树
2)树的基本术语
节点的度:节点拥有的子树个数或者分支个数
树的度:树中各节点度的最大值
叶子节点:又叫终端节点,指度为0的节点
层次:从根开始,根为第一层,根的孩子为第二层,根的孩子的孩子为第三次,以此类推
树的高度(深度):树中节点的最大层次,如共有4层,则高度为4
节点的深度和高度:深度从根节点算起,根节点的深度为1;高度从最底层叶子节点算起,最底层的叶子节点的高度为1
有序树:树中节点的子树从左到右是有顺序的,不能交换
丰满树:即理想平衡树,要求除底层外,其它层都是满的
森林:若干棵互不相交的树的集合
3)树的存储结构△
①顺序存储结构
双亲存储结构
②链式存储结构
a.孩子存储结构
b.孩子兄弟存储结构
2.二叉树
1)二叉树的定义
①每个节点最多2个子树,即二叉树中节点的度只能为0,1,2
②子树有左右顺序之分,不能颠倒
2)满二叉树
所有的分支节点都有左孩子和右孩子,叶子节点集中在最底层
3)完全二叉树
满二叉树去掉一些节点后,剩下节点与原来满二叉树编号相同
3.二叉树的主要性质
1)非空二叉树上叶子节点数等于双分支节点树+1
证:设二叉树上叶子节点数为n0,单分支节点数为n1,双分支节点数为n2
显然,总结点数=n0+n1+n2
总分支数=n1+2n2,总结点数=总分支数+根节点=总分支数+1=n1+2n2+1
所以n0+n1+n2=n1+2n2+1,即n0=n2+1
2)二叉树第i层上最多有2^(i-1)(i≥1)个节点
3)高度(或深度)为k的二叉树最多有2^k-1(k≥1)个节点
4)有n个节点的完全二叉树,对各节点从上到下,从左到右依次编号(1~n),则节点之间有如下关系
若i为某节点A的编号,则:
如果i≠1,则A的双亲节点编号为[i/2]
如果2i≤n,则A的左孩子节点编号为2i,如果2i>n,则A无左孩子
如果2i+1≤n,则A的左孩子节点编号为2i+1,如果2i+1>n,则A右左孩子
注:有时候从0开始先全体加1按照上面求节点编号,之后再减1得到真实的节点编号
5)函数Catalan():给定n个节点能构成h(n)种不同的二叉树,h(n)=C(n,2n)/(n+1)
6)具有n个节点的完全二叉树高度(或深度)为[log2(n)]+1或者[log2(n+1)]
4.二叉树的存储结构
1)顺序存储结构
最适合于完全二叉树,用于存储一般二叉树会浪费大量的存储空间
用BTree[]数组来存储节点元素,下标从1开始存储,满足二叉树的性质
2)链式存储结构
public class BinaryTree<T>{
private class BTNode{
BTNode lchild;//左孩子
BTNode rchild;//右孩子
T data;//数据域
}
private BTNode root;//根节点
}
5.二叉树的遍历算法
1)先序遍历
①访问根节点
②先序遍历左子树
③先序遍历右子树
2)中序遍历
①中序遍历左子树
②访问根节点
③中序遍历右子树
3)后序遍历
①后序遍历左子树
②后序遍历右子树
③访问根节点
4)层次遍历
从根节点开始,一层访问完再访问下一层

5)先序遍历和中序遍历、中序遍历和后序遍历可以唯一确定二叉树,而先序遍历和后序遍历则不能

package tree.遍历;@SuppressWarnings("unused")public class BinaryTree {//节点private class BTNode{public char data;//数据域public BTNode lchild;//左指针域public BTNode rchild;//右指针域}private BTNode root;private int maxSize;//访问节点的函数public void visit(BTNode p){//根据需要写操作}//先序遍历public void preorder(BTNode p){if(p != null){visit(p);preorder(p.lchild);preorder(p.rchild);}}//中序遍历public void inorder(BTNode p){if(p != null){preorder(p.lchild);visit(p);preorder(p.rchild);}}//后序遍历public void postorder(BTNode p){if(p != null){preorder(p.lchild);preorder(p.rchild);visit(p);}}//层次遍历public void level(BTNode p){int front = 0,rear = 0;//首尾指针BTNode[] queue = new BTNode[maxSize];//创建循环队列BTNode q = new BTNode();if(p != null){//根节点入队rear = (rear + 1) % maxSize;queue[rear] = p;//队列不为空时循环while(front != rear){front = (front + 1) % maxSize;//首指针后移,指向队首元素q = queue[front];//出队visit(q);//访问队首节点//如果左子树不空,则左子树入队if(q.lchild != null){rear = (rear + 1) & maxSize;queue[rear] = q.lchild;}//如果右子树不空,则右子树入队if(q.rchild != null){rear = (rear + 1) & maxSize;queue[rear] = q.rchild;}}}}}

6.遍历算法的改进

深度优先算法非递归形式,运用自定义栈+循环

package tree.遍历改进;@SuppressWarnings("unused")public class BinaryTree {// 节点private class BTNode {public char data;// 数据域public BTNode lchild;// 左指针域public BTNode rchild;// 右指针域}private BTNode root;private int maxSize;// 访问节点的函数public void visit(BTNode p) {// 根据需要写操作}//先序遍历非递归算法public void preorderNonrecursion(BTNode p){if(p != null){BTNode[] stack = new BTNode[maxSize];int top = -1;stack[++top] = p;BTNode q = new BTNode();while(top != -1){q = stack[top--];visit(q);//栈是先进后出,所以右孩子先入栈if(q.rchild != null){stack[++top] = q.rchild;}if(q.lchild != null){stack[++top] = q.lchild;}}}}//中序遍历非递归算法public void inorderNonrecursion(BTNode p){if(p != null){BTNode[] stack = new BTNode[maxSize];int top = -1;BTNode q = p;while(top != -1 || q != null){//只要节点左孩子还在,就一直入栈while(q != null){stack[++top] = q;q = q.lchild;}if(top != -1){q = stack[top--];visit(q);q = q.rchild;}}}}//后序遍历非递归算法public void postorderNonrecursion(BTNode p){if(p != null){BTNode[] stack1 = new BTNode[maxSize];//存储逆后序遍历BTNode[] stack2 = new BTNode[maxSize];//存储后序遍历int top1 = -1;int top2 = -1;stack1[++top1] = p;BTNode q = new BTNode();while(top1 != -1){q = stack1[top1--];stack2[++top2] = q;visit(q);//后序遍历的逆序恰巧是先序遍历左右优先顺序颠倒的结果(节点-右孩子-左孩子)if(q.lchild != null){stack1[++top1] = q.lchild;}if(q.rchild != null){stack1[++top1] = q.rchild;}}while(top2 != -1){q = stack2[top2--];visit(q);}}}}

线索二叉树还是用递归形式

package tree.线索二叉树;/** * @author 芜情 * */@SuppressWarnings("unused")public class BinaryTree {private class TBTNode{public char data;//数据域int ltag;//左线索标记int rtag;//右线索标记TBTNode lchild;//左指针域TBTNode rchild;//右指针域}private TBTNode root;private void visit(TBTNode p) {// 访问函数}//中序线索二叉树递归算法public void inThread(TBTNode p,TBTNode pre){if(p != null){inThread(p.lchild, pre);//递归,左子树线索化//建立当前节点前驱线索if(p.lchild == null){p.lchild = pre;p.ltag = 1;}//建立前驱节点的后继线索if(pre != null && pre.rchild == null){pre.rchild = p;pre.rtag = 1;}pre = p;inThread(p.rchild, pre);//递归,右子树线索化}}//中序线索二叉树主程序@SuppressWarnings("null")public void createInThread(TBTNode root){TBTNode pre = null;if(root != null){inThread(root, pre);pre.rchild = null;//处理最后一个节点的后继pre.rtag = 1;}}/* * 遍历中序线索二叉树 *///求以p为根的中序线索二叉树中第一个节点private TBTNode first(TBTNode p){while(p.ltag == 0){p = p.lchild;}return p;}//求以p为根的中序线索二叉树中最后一个节点private TBTNode last(TBTNode p){while(p.rtag == 0){p = p.rchild;}return p;}//求p在中序线索二叉树中的后继节点private TBTNode next(TBTNode p){if(p.rtag == 0){return first(p.rchild);}return p.rchild;}//求p在中序线索二叉树中的前驱节点private TBTNode prior(TBTNode p){if(p.ltag == 0){return last(p.lchild);}return p.lchild;}//遍历public void inorder(TBTNode root){for(TBTNode p = first(root);p != null; p=next(p)){visit(p);}}/* * 先序线索二叉树的遍历 *///先序线索二叉树public void preThread(TBTNode p,TBTNode pre){if(p != null){if(p.lchild == null){p.lchild = pre;p.ltag = 1;}if(pre != null && pre.rchild == null){pre.rchild = p;pre.rtag = 1;}pre = p;if(p.ltag == 0){preThread(p.lchild, pre);}if(p.rtag == 0){preThread(p.rchild, pre);}}}//遍历先序线索二叉树public void preorder(TBTNode root){if(root != null){TBTNode p = root;while(p != null){while(p.ltag == 0){visit(p);p = p.lchild;}visit(p);p = p.rchild;}}}}

7.树和森林与二叉树的相互转换
1)树转换为二叉树
①将同一节点的各孩子用线串起来
②将每个节点的分支从左往右除了第一个之外,其余的都剪掉
③调整节点使之符合二叉树的层次结构
注:左子节点是第一个孩子,右子节点是相邻的下一个兄弟
2)二叉树转为树
与上面相反
3)森林转换为二叉树
本质上还是树转为二叉树,只是原本根节点是没有兄弟的,所以右子节点为null
现在是多棵树组成的森林,所以根节点右子节点用来放相邻的下一个根节点
4)二叉树转换为森林
首先将森林断开成各个树,然后就是二叉树转树
5)树和森林的遍历
1>树的遍历
①先序遍历
转换成二叉树后先序遍历
②后序遍历
转换成二叉树后中序遍历
2>森林的遍历
①先序遍历
转换成二叉树后先序遍历
②后序遍历
转换成二叉树后中序遍历
8.赫夫曼树额赫夫曼编码(默认为二叉树)
1)与赫夫曼树相关的一些概念
赫夫曼树又叫作最优二叉树,它的特点是带权路径最短
路径:从树的一个节点到另一个节点的分支所构成的路线
路径长度:路径上分支的书目
树的路径长度:根到每个节点的路径长度之和
带权路径长度:节点具有权值,从该节点到根路径长度乘以节点权值
树的带权路径长度(WPL):所有叶子节点的带权路径长度之和
2)赫夫曼树的构造方法
3)赫夫曼树的特点
①权值越大的节点,距离根节点越近
②树中没有度为1的加点。这类树叫做正则(严格)二叉树
③树的带权路径长度最短
4)赫夫曼编码
理解~~~没法表达...