Java数据结构与算法之树(动力节点java学院整理)

为什么使用树:

树结合了两种数据结构的有点:一种是有序数组,树在查找数据项的速度和在有序数组中查找一样快;另一种是链表,树在插入数据和删除数据项的速度和链表一样。既然这样,就要好好去学了....
(最主要讨论的是二叉树中的二叉搜索树,即一个节点的左子节点关键值小于这个节点,右子节点的关键值大于这个节点)

设计前的思考:

树——>元素(节点)

class Node
{
 public int iData ;
 public float fData ;
 public Node left ;
 public Node right ;
 //方法
 public Node(int iData,float fData){}
 public void displayNode(){}
}
class Tree
{
 Node root ;//树根
 //方法
 public void insert(){}
 public void displayTree(){}
 public void find(){}
 public void delete(){}
}

插入数据:

 //插入子节点
 public void insert(int iData ,float fData)
 {
 Node newNode = new Node(iData,fData) ;
 if(root == null)
 root = newNode ;
 else
 {
 Node current = root ;
 Node parent ;
 while(true)//寻找插入的位置
 {
 parent = current ;
 if(iData<current.iData)
 {
 current = current.left ;
 if(current == null)
 {
 parent.left = newNode ;
 return ;
 }
 }
 else
 {
 current =current.right ;
 if(current == null)
 {
 parent.right = newNode ;
 return ;
 }
 }
 }
 }
 }

遍历树:

//中序遍历方法
 public void inOrder(Node localRoot)
 {
 if(localRoot != null)
 {
 inOrder(localRoot.left) ;//调用自身来遍历左子树
 localRoot.displayNode() ;//访问这个节点
 inOrder(localRoot.right) ;//调用自身来遍历右子树
 }
 }

查找某个节点:

//查找某个节点
 public Node find(int iData)
 {
 Node current = root ;
 while(current.iData != iData)
 {
 if(current.iData<iData)
 current = current.right ;
 else
 current = current.left ;
 if(current == null)
 return null ;
 }
 return current ;
 }

查找树中关键字的最大值和最小值:

最大值:不断地寻找右子节点

最小值:不断地寻找左子节点

//查找关键字最小的节点
 public Node findMinNode()
 {
 Node current , last ;
 last = null ;
 current = root ;
 if(current.left == null)
 return current ;
 else
 {
 while(current != null)
 {
 last = current ;
 current = current.left ;
 }
 return last ;
 }
 }

 删除某个节点:

 思考:

1).先找到要删除的节点:

public boolean delete(int key)
 {
 //先找到需要删除的节点
 Node current = root ;
 Node parent = root ;
 boolean isLeftChild = false ;
 while(current.iData != key)//显然,当current.iData == key 时,current 就是要找的节点
 {
 parent = current ;
 if(key < current.iData)
 {
 isLeftChild = true ;
 current = current.left ;
 }
 else
 {
 isLeftChild = false ;
 current = current.right ;
 }
 if(current == null)//找不到key时返回false
 return false ;
 }
 //continue ........
 }

2).再考虑要删除的节点是怎样的节点,经分析,有三种情况:叶节点、有一个节点的节点、有两个节点的节点

A).如果删除的是一个叶子节点,直接删除即可

//接上................
 //分情况考虑删除的节点
 //删除的节点为叶节点时
 if(current.left == null && current.right == null)
 {
 if(current == root)
 root = null ;
 else
 if(isLeftChild)
 parent.left = null ;
 else
 parent.right = null ;
 }
//continue...........

B).如果删除的节点有一个节点时:分两种情况,删除的节点只有一个左子节点,或者只有一个右子节点

//接上.......
//删除的节点有一个子节点
 else
 if(current.right == null)//删除的节点只有一个左子节点时
 {
 if(current == root)//要删除的节点为根节点
 root = current.left ;
 else
 if(isLeftChild)//要删除的节点是一个左子节点
 parent.left = current.left ;
 else
 parent.right = current.left ;//要删除的节点是一个右子节点
 }
 else
 if(current.left == null)//删除的节点只有一个右子节点时
 {
 if(current == root)//要删除的节点为根节点
 root = current.right ;
 else
 if(isLeftChild)//要删除的节点是一个左子节点
 parent.left = current.right ;
 else
 parent.right = current.right ;//要删除的节点是一个右子节点
 }
//continue.......

c).如果删除的节点有两个节点时:

这种情况就比较复杂,需要去寻找一个节点去替代要删除的节点。这个节点应该是什么节点呢?

据书本介绍,最合适的节点是后继节点,即比要删除的节点的关键值次高的节点是它的后继节点。

说得简单一些,后继节点就是比要删除的节点的关键值要大的节点集合中的最小值。

以上面的为例,40的后继节点为74,10的后继节点是13,19的后继节点时26

以下是寻找后继节点的代码:

 //返回后继节点
 private Node getSuccessor(Node delNode)
 {
 Node successorParent = delNode ;//后继节点的父节点
 Node successor = delNode ;//后继节点
 Node current = delNode.right ;//移动到位置节点位置
 while(current != null)
 {
 successorParent = successor ;
 successor = current ;
 current = current.left ;
 }
 if(successor != delNode.right)
 {
 successorParent.left = successor.right ;
 successor.right = delNode.right ;
 }
 return successor ;
 }

找到了后继节点,接着就要讨论如何用后继节点替代药删除的节点

a)如果后继节点是刚好是要删除节点的右子节点(此时可以知道,这个右子节点没有左子点,如果有,就不该这个右子节点为后继节点)

//要删除的节点为左子节点时
parent.left = successor ;
successor.left = current.left ;
//要删除的节点是右子节点时
parent.right = successor ;
successor.left = current.left ;

b)如果后继节点为要删除节点的右子节点的左后代:

//假如要删除的节点为右子节点
successorParent.left = successor.right ;//第一步
successor.right = current.right ;//第二步
parent.right = successor ;
successor.left = current.left ;
//假设要删除的节点为左子节点
successorParent.left = successor.right ;
successor.right = current.right ;
parent.left = successor ;
successor.left = current.left ;

注意:第一步和第二步在getSuccessor()方法的最后的if语句中完成

以下是删除的节点有连个节点的代码:

//接上
 //删除的节点有两个子节点
 else
 {
 Node successor = getSuccessor(current) ;//找到后继节点
 if(current == root)
 root = successor ;
 else
 if(isLeftChild)
 parent.left = successor ;
 else
 parent.right = successor ;
 successor.left = current.left ;
 }
 //continue....

综合上述,给出delete()方法的代码:

//删除某个节点
 public boolean delete(int key)
 {
 //先找到需要删除的节点
 Node current = root ;
 Node parent = root ;
 boolean isLeftChild = false ;
 while(current.iData != key)//显然,当current.iData == key 时,current 就是要找的节点
 {
 parent = current ;
 if(key < current.iData)
 {
 isLeftChild = true ;
 current = current.left ;
 }
 else
 {
 isLeftChild = false ;
 current = current.right ;
 }
 if(current == null)//找不到key时返回false
 return false ;
 }
 //分情况考虑删除的节点
 //删除的节点为叶节点时
 if(current.left == null && current.right == null)
 {
 if(current == root)
 root = null ;
 else
 if(isLeftChild)
 parent.left = null ;
 else
 parent.right = null ;
 }
 //删除的节点有一个子节点
 else
 if(current.right == null)//删除的节点只有一个左子节点时
 {
 if(current == root)//要删除的节点为根节点
 root = current.left ;
 else
 if(isLeftChild)//要删除的节点是一个左子节点
 parent.left = current.left ;
 else
 parent.right = current.left ;//要删除的节点是一个右子节点
 }
 else
 if(current.left == null)//删除的节点只有一个右子节点时
 {
 if(current == root)//要删除的节点为根节点
 root = current.right ;
 else
 if(isLeftChild)//要删除的节点是一个左子节点
 parent.left = current.right ;
 else
 parent.right = current.right ;//要删除的节点是一个右子节点
 }
 //删除的节点有两个子节点
 else
 {
 Node successor = getSuccessor(current) ;//找到后继节点
 if(current == root)
 root = successor ;
 else
 if(isLeftChild)
 parent.left = successor ;
 else
 parent.right = successor ;
 successor.left = current.left ;
 }
 return true ;
 }

进一步考虑:

删除那么复杂,那删除是必要的吗?我们可以给每个节点定义一个标志,该标志用于记录该节点是否已经删除了,显示树时,先判断该节点是否已经删除,如果没有,则显示。

这样的结果是,节点其实是没有删除的,这样显然逃避责任了。当树中没有那么多的删除操作时,这也不失为一种好方法,例如:

已经离职的员工的档案要永久地保存在员工的记录中。

以上所述是小编给大家介绍的Java数据结构与算法之树(动力节点java学院整理),希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对我们网站的支持!

(0)

相关推荐

  • Java数据结构与算法之栈(动力节点Java学院整理)

    stack,中文翻译为堆栈,其实指的是栈,heap,堆.这里讲的是数据结构的栈,不是内存分配里面的堆和栈. 栈是先进后出的数据的结构,好比你碟子一个一个堆起来,最后放的那个是堆在最上面的. 队列就是排队买苹果,先去的那个可以先买. 栈 public class Stack { private int array[]; private int max; private int top; public Stack(int max){ this.max = max; array = new int[m

  • Java数据结构之队列(动力节点Java学院整理)

    队列的定义: 队列(Queue)是只允许在一端进行插入,而在另一端进行删除的运算受限的线性表. (1)允许删除的一端称为队头(Front). (2)允许插入的一端称为队尾(Rear). (3)当队列中没有元素时称为空队列. (4)队列亦称作先进先出(First In First Out)的线性表,简称为FIFO表. 队列的修改是依先进先出的原则进行的.新来的成员总是加入队尾,每次离开的成员总是队列头上的(不允许中途离队). 队列的存储结构及实现 队列的顺序存储结构 (1) 顺序队列的定义: 队列

  • Java数据结构之散列表(动力节点Java学院整理)

    基本概念 散列表(Hash table,也叫哈希表),是根据关键字(key value)而直接进行访问的数据结构. 说的具体点就是它通过吧key值映射到表中的一个位置来访问记录,从而加快查找的速度. 实现key值映射的函数就叫做散列函数 存放记录的数组就就叫做散列表 实现散列表的过程通常就称为散列(hashing),也就是常说的hash 散列 这里的散列的概念不仅限于数据结构了,在计算机科学领域中,散列-哈希是一种对信息的处理方法,通过某种特定的函数/算法(散列函数/hash()方法)将要检索的

  • Java数据结构和算法之冒泡排序(动力节点Java学院整理)

    冒泡排序(Bubble Sort)是一种简单的排序算法.它重复地走访过要排序的数列,一次比较两个元素,如果他们的顺序错误就把他们交换过来.走访数列的工作是重复地进行直到没有再需要交换,也就是说该数列已经排序完成.这个算法的名字由来是因为越小的元素会经由交换慢慢"浮"到数列的顶端. 冒泡排序算法的运作如下: 1. 比较相邻的元素.如果第一个比第二个大,就交换他们两个. 2. 对每一对相邻元素作同样的工作,从开始第一对到结尾的最后一对.在这一点,最后的元素应该会是最大的数. 3. 针对所有

  • Java数据结构之数组(动力节点之Java学院整理)

    数组的用处是什么呢?--当你需要将30个数进行大小排列的时候,用数组这样的数据结构存储是个很好的选择,当你是一个班的班主任的时候,每次要记录那些学生的缺勤次数的时候,数组也是很有用.数组可以进行插入,删除,查找等. 1)创建和内存分配 Java中有两种数据类型,基本类型和对象类型,也有人称为引用类型,Java中把数组当成对象,创建数组时使用new操作符. int array[] = new int[10]; 既然是对象,那么array便是数组的一个引用,根据Java编程思想(一) -- 一切都是

  • Java数据结构之图(动力节点Java学院整理)

    1,摘要: 本文章主要讲解学习如何使用JAVA语言以邻接表的方式实现了数据结构---图(Graph).从数据的表示方法来说,有二种表示图的方式:一种是邻接矩阵,其实是一个二维数组:一种是邻接表,其实是一个顶点表,每个顶点又拥有一个边列表.下图是图的邻接表表示. 从图中可以看出,图的实现需要能够表示顶点表,能够表示边表.邻接表指是的哪部分呢?每个顶点都有一个邻接表,一个指定顶点的邻接表中,起始顶点表示边的起点,其他顶点表示边的终点.这样,就可以用邻接表来实现边的表示了.如顶点V0的邻接表如下: 与

  • Java数据结构与算法之选择排序(动力节点java学院整理)

    每一趟从待排序的数据元素中选出最小(或最大)的一个元素,顺序放在已排好序的数列的最后,直到全部待排序的数据元素排完. 代码 public class ChoseSort { //constructor without parameters public ChoseSort(){}; //constructor with parameters public int[] ChoseSort(int[] intArr){ for(int i=0;i<intArr.length-1;i++){ int

  • Java数据结构之链表(动力节点之Java学院整理)

    单链表: insertFirst:在表头插入一个新的链接点,时间复杂度为O(1) deleteFirst:删除表头的链接点,时间复杂度为O(1) find:查找包含指定关键字的链接点,由于需要遍历查找,平均需要查找N/2次,即O(N) remove:删除包含指定关键字的链接点,由于需要遍历查找,平均需要查找N/2次,即O(N) public class LinkedList { private class Data{ private Object obj; private Data next =

  • Java数据结构与算法之树(动力节点java学院整理)

    为什么使用树: 树结合了两种数据结构的有点:一种是有序数组,树在查找数据项的速度和在有序数组中查找一样快:另一种是链表,树在插入数据和删除数据项的速度和链表一样.既然这样,就要好好去学了.... (最主要讨论的是二叉树中的二叉搜索树,即一个节点的左子节点关键值小于这个节点,右子节点的关键值大于这个节点) 设计前的思考: 树-->元素(节点) class Node { public int iData ; public float fData ; public Node left ; public

  • Java设计模式之迭代器模式_动力节点Java学院整理

    定义:提供一种方法访问一个容器对象中各个元素,而又不暴露该对象的内部细节. 类型:行为类模式 类图: 如果要问Java中使用最多的一种模式,答案不是单例模式,也不是工厂模式,更不是策略模式,而是迭代器模式,先来看一段代码吧: public static void print(Collection coll){ Iterator it = coll.iterator(); while(it.hasNext()){ String str = (String)it.next(); System.out

  • Java多线程的其他知识_动力节点Java学院整理

    一.线程组 /** * A thread group represents a set of threads. In addition, a thread * group can also include other thread groups. The thread groups form * a tree in which every thread group except the initial thread group * has a parent. * <p> * A thread

  • Java Collections集合继承结构图_动力节点Java学院整理

    面向对象语言对事物的体现都是以对象的形式,所以为了方便对多个对象的操作,就对对象进行存储,集合就是存储对象最常用的一种方式. 数组虽然也可以存储对象,但长度是固定的:集合长度是可变的,数组中可以存储基本数据类型,集合只能存储对象. 集合类的特点:集合只用于存储对象,集合长度是可变的,集合可以存储不同类型的对象. 集合框架图 Collection (集合的最大接口)继承关系 --List 可以存放重复的内容 --Set 不能存放重复的内容,所以的重复内容靠hashCode()和equals()两个

  • Java异常继承结构解析_动力节点Java学院整理

    Java异常类层次结构图: 异常的英文单词是exception,字面翻译就是"意外.例外"的意思,也就是非正常情况.事实上,异常本质上是程序上的错误,包括程序逻辑错误和系统错误.比如使用空的引用.数组下标越界.内存溢出错误等,这些都是意外的情况,背离我们程序本身的意图.错误在我们编写程序的过程中会经常发生,包括编译期间和运行期间的错误,在编译期间出现的错误有编译器帮助我们一起修正,然而运行期间的错误便不是编译器力所能及了,并且运行期间的错误往往是难以预料的.假若程序在运行期间出现了错误

  • Java设计模式之备忘录模式_动力节点Java学院

    定义:在不破坏封装性的前提下,捕获一个对象的内部状态,并在该对象之外保存这个状态.这样就可以将该对象恢复到原先保存的状态 类型:行为类 类图: 我们在编程的时候,经常需要保存对象的中间状态,当需要的时候,可以恢复到这个状态.比如,我们使用Eclipse进行编程时,假如编写失误(例如不小心误删除了几行代码),我们希望返回删除前的状态,便可以使用Ctrl+Z来进行返回.这时我们便可以使用备忘录模式来实现. 备忘录模式的结构 发起人:记录当前时刻的内部状态,负责定义哪些属于备份范围的状态,负责创建和

  • 深入理解Java中的final关键字_动力节点Java学院整理

    Java中的final关键字非常重要,它可以应用于类.方法以及变量.这篇文章中我将带你看看什么是final关键字?将变量,方法和类声明为final代表了什么?使用final的好处是什么?最后也有一些使用final关键字的实例.final经常和static一起使用来声明常量,你也会看到final是如何改善应用性能的. final关键字的含义? final在Java中是一个保留的关键字,可以声明成员变量.方法.类以及本地变量.一旦你将引用声明作final,你将不能改变这个引用了,编译器会检查代码,如

  • Java Date类常用示例_动力节点Java学院整理

    Date类 在JDK1.0中,Date类是唯一的一个代表时间的类,但是由于Date类不便于实现国际化,所以从JDK1.1版本开始,推荐使用Calendar类进行时间和日期处理.这里简单介绍一下Date类的使用. 1.使用Date类代表当前系统时间 Date d = new Date(); System.out.println(d); 使用Date类的默认构造方法创建出的对象就代表当前时间,由于Date类覆盖了toString方法,所以可以直接输出Date类型的对象,显示的结果如下: Sun Ma

随机推荐