树、二叉树及二叉查找树
2016-05-30 16:21
120 查看
树状图是一种数据结构,它是由n(n>=1)个有限节点组成一个具有层次关系的集合。把它叫做“树”是因为它看起来像一棵倒挂的树,也就是说它是根朝上,而叶朝下的。它具有以下的特点:
每个节点有零个或多个子节点;没有父节点的节点称为根节点;每一个非根节点有且只有一个父节点;除了根节点外,每个子节点可以分为多个不相交的子树。
(以下实现代码均在git)
1. 概念和基本术语
1.1树的定义
n (n >= 0) 个结点的有限集。如果 n = 0,称为空树;如果 n > 0,则1)
有且仅有一个特定的称为根(Root)的结点,根只有直接后继,没有直接前驱;
2)
当n > 1,除根外其它结点划分为 m (m >0) 个互不相交的有限集T1, T2 ,…, Tm,其中每个集合本身又是一棵树,称为根的子树(SubTree)。
1.2相关术语
树的结点:包含一个数据元素及若干指向子树的分支;孩子结点:结点的子树的根称为该结点的孩子;
双亲结点:B 结点是A 结点的孩子,则A结点是B 结点的双亲;
兄弟结点:同一双亲的孩子结点; 堂兄结点:同一层上结点;
祖先结点: 从根到该结点的所经分支上的所有结点子孙结点:以某结点为根的子树中任一结点都称为该结点的子孙
结点层:根结点的层定义为1;根的孩子为第二层结点,依此类推;
树的深度:树中最大的结点层
结点的度:结点子树的个数
树的度: 树中最大的结点度。
叶子结点:也叫终端结点,是度为 0 的结点;
分枝结点:度不为0的结点;
有序树:子树有序的树,如:家族树;
无序树:不考虑子树的顺序;
1.3树的其它三种表示法
2.二叉树
定义:一棵二叉树是结点的一个有限集合,该集合或者为空,或者是由一个根结点加上两棵分别称为左子树和右子树的、互不相交的二叉树组成。特点:每个结点至多只有两棵子树(二叉树中不存在度大于2的结点)
五种形态:
性质:(见二叉树的五大性质及证明)
3. 二叉查找树
二叉树的一个重要的应用是它们在查找中的使用。假设树中的每个结点存储一项数据。使二叉树成为二叉查找树的性质是,对于树中的每个节点X,它的左子树中所有项的值小于X中的值,而它的右子树中所有项的值大于X中的值。3.1 二叉查找树的数据结构
public class BinarySearchTree<AnyType extends Comparable<? super AnyType>> { //树的节点 private static class Node<AnyType>{ AnyType element; //节点数据 Node<AnyType> left; //左孩子 Node<AnyType> right; //左孩子 Node(AnyType element){ this(element, null, null); } Node(AnyType element, Node<AnyType> lt, Node<AnyType> rt){ this.element = element; left = lt; right = rt; } } private Node<AnyType> root; //根节点 public BinarySearchTree(){ root = null; } public void makeEmpty(){ root = null; } public boolean isEmpty(){ return root == null; } }
3.2二叉查找树的元素查找
通过二叉查找树有序的性质,能有效节省查找时的效率,在平均情况下查找的时间复杂度为O(logn),但在最坏的情况下(此时二叉查找树退化成链)查找的时间复杂度会达到O(n)。/** * 查找元素的内部方法,递归 * @param x 要查找的元素 * @param t 子树的根节点 * @return 是否查找成功 */ private boolean contains(AnyType x, Node<AnyType> t){ if(t == null) return false; int compareResult = x.compareTo(t.element); if(compareResult < 0) return contains(x, t.left); else if(compareResult > 0) return contains(x, t.right); else return true; //匹配 }
3.3 二叉查找树的最大/最小值查找
在二叉查找树的性质中可知,对于树中的每个节点X,它的左子树中所有项的值小于X中的值而它的右子树中所有项的值大于X中的值。这样使用递归的方法可很简单地实现最大/最小值的查找。(以下对于最大值和最小值分别使用递归和非递归的形式实现)/** * 查找树中最小元素,递归 * @param t * @return */ private Node<AnyType> findMin(Node<AnyType> t){ if(t == null) return null; else if(t.left == null){ return t; } return findMin(t.left); } /** * 查找树中最大元素,非递归实现 * @param t * @return */ private Node<AnyType> findMax(Node<AnyType> t){ if(t != null){ while(t.right != null){ t = t.right; } } return t; }
3.4 二叉查找树节点的插入
二叉查找树插入的实现与查找相似,即找到对应位置在执行插入操作。/** * 将元素插入到二叉查找树 * @param x 要查找的元素 * @param t 子树的根节点 * @return 插入后的树的根节点 */ private Node<AnyType> insert(AnyType x, Node<AnyType> t) { if(t == null) return new Node<>(x, null, null); //递归最后找到了属于自己的位置 int compareResult = x.compareTo(t.element); if(compareResult < 0) t.left = insert(x, t.left); else if(compareResult > 0) t.right = insert(x, t.right); else ; //什么都不做 return t; }
3.5 二叉查找树节点的删除
二叉查找树节点的删除有两种情况:1.被删除的节点只有一个儿子,则该结点可以在其父节点调整自己的链后删除;(若该节点是树叶的情况等同,此时赋值的值为null)
2.被删除的节点只有两个儿子,用其右子树的最小的数据代替该节点的数据,并递归第删除那个节点。
private Node<AnyType> remove(AnyType x, Node<AnyType> t) { if(t == null) return t; //未找到该元素 int compareResult = x.compareTo(t.element); if(compareResult < 0) t.left = remove(x, t.left); else if(compareResult > 0) t.right = remove(x, t.right); else if(t.left != null && t.right != null){ //要删除的节点有两个孩子 t.element = findMin(t.right).element; t.right = remove(t.element, t.right); } else t = (t.left != null)?t.left:t.right; //要删除的节点有一个孩子或无孩子 return t; }
4. 二叉查找树的遍历
4.1中序遍历
中序遍历首先遍历左子树,然后访问根结点,最后遍历右子树。在遍历左、右子树时,仍然先遍历左子树,再访问根结点,最后遍历右子树。private void inOrder(Node<AnyType> root){ if(root != null){ inOrder(root.left); System.out.print(root.element + " "); inOrder(root.right); } }
4.2 先序遍历
首先访问根结点然后遍历左子树,最后遍历右子树。在遍历左、右子树时,仍然先访问根结点,然后遍历左子树,最后遍历右子树,如果二叉树为空则返回。private void preOrder(Node<AnyType> root){ if(root != null){ System.out.print(root.element + " "); preOrder(root.left); preOrder(root.right); } }
4.3 后序遍历
后序遍历首先遍历左子树,然后遍历右子树,最后访问根结点,在遍历左、右子树时,仍然先遍历左子树,然后遍历右子树,最后遍历根结点。private void postOrder(Node<AnyType> root){ if(root != null){ postOrder(root.left); postOrder(root.right); System.out.print(root.element + " "); } }
4.4
层序遍历
设二叉树的根节点所在层数为1,层序遍历就是从所在二叉树的根节点出发,首先访问第一层的树根节点,然后从左到右访问第2层上的节点,接着是第三层的节点,以此类推,自上而下,自左至右逐层访问树的结点的过程就是层序遍历。/** * 层次遍历二叉树(使用队列) * 描述:先将根节点放入队列中,然后每次都从队列中取出一个节点打印, * 若这个节点有子节点,则将它的子节点放入队列尾,直到队列为空 */ public void layerTranverse(){ if(this.root == null){ return; } Queue<Node<AnyType>> q = new LinkedList<Node<AnyType>>(); q.add(this.root); while(!q.isEmpty()){ Node<AnyType> n = q.poll(); System.out.print(n.element + " "); if(n.left != null){ q.add(n.left); } if(n.right != null){ q.add(n.right); } } }
4.5
遍历结果对比
参考资料:
1. 《数据结构与算法分析》
2. 《Java程序员面试笔试宝典》
3. Boss的PPT
相关文章推荐
- js事件模型
- 百度下拉搜索
- java在线预览txt、word、ppt、execel,pdf代码
- 【转载】测试质量知识梳理
- MySQL: 忘记root用户密码
- Linked List Cycle
- 【转载】测试质量知识梳理
- RAR文件格式官方说明书的翻译[中英对照]
- C++中的 istringstream 的用法
- 会报编译器警告的Xcode 6.3新特性:Nullability Annotations
- ThinkPHP(2)——配置文件
- JS省市区三级联动
- split()方法解析
- Aop检查Session,全局过滤器和No全局过滤器
- jquery提交前事件
- Hibernate连接数据库
- mysql查询当前时间的前后几天时间
- 利用OpenCV鼠标事件设置函数setMouseCallback显示图像像素坐标值!
- Research URL
- 逻辑正确的程序为什么运行后不符合逻辑