在有序数组中,可以快速找到特定的值,但是想在有序数组中插入一个新的数据项,就必须首先找出新数据项插入的位置,然后将比新数据项大的数据项向后移动一位,来给新的数据项腾出空间,删除同理,这样移动很费时。显而易见,如果要做很多的插入和删除操作和删除操作,就不该选用有序数组。
另一方面,链表中可以快速添加和删除某个数据项,但是在链表中查找数据项可不容易,必须从头开始访问链表的每一个数据项,直到找到该数据项为止,这个过程很慢。
树这种数据结构,既能像链表那样快速的插入和删除,又能想有序数组那样快速查找。这里主要实现一种特殊的树——二叉(搜索)树。二叉搜索树有如下特点:一个节点的左子节点的关键字值小于这个节点,右子节点的关键字值大于或等于这个节点。插入一个节点需要根据这个规则进行插入。
删除节点时二叉搜索树中最复杂的操作,但是删除节点在很多树的应用中又非常重要,所以详细研究并总结下特点。删除节点要从查找要删的节点开始入手,首先找到节点,这个要删除的节点可能有三种情况需要考虑:
·该节点是叶节点,没有子节点
·该节点有一个子节点
·该节点有两个子节点
第一种最简单,第二种也还是比较简单的,第三种就相当复杂了。
下面分析这三种删除情况:
1.要删除叶节点,只需要改变该节点的父节点对应子字段的值即可,由指向该节点改为null就可以了。垃圾回收器会自动回收叶节点,不需要自己手动删掉;
2.当节点有一个子节点时,这个节点只有两个连接:连向父节点和连向它唯一的子节点。需要从这个序列中剪断这个节点,把它的子节点直接连到它的父节点上即可,这个过程要求改变父节点适当的引用(左子节点还是右子节点),指向要删除节点的子节点即可;
3.第三种情况最复杂,如果要删除有两个子节点的节点,就不能只用它的一个子节点代替它,比如要删除节点25,如果用35取代它,那35的左子节点是15呢还是30?
因此需要考虑另一种方法,寻找它的中序后继来代替该节点。下图显示的就是要删除节点用它的后继代替它的情况,删除后还是有序的。(这里还有更麻烦的情况,即它的后继自己也有右子节点,下面再讨论。)
那么如何找后继节点呢?首先得找到要删除的节点的右子节点,它的关键字值一定比待删除节点的大。然后转到待删除节点右子节点的左子节点那里(如果有的话),然后到这个左子节点的左子节点,以此类推,顺着左子节点的路径一直向下找,这个路径上的最后一个左子节点就是待删除节点的后继。如果待删除节点的右子节点没有左子节点,那么这个右子节点本身就是后继。寻找后继的示意图如下:
找到了后继节点,现在开始删除了,先看第一种情况,后继节点是delNode右子节点的做后代,这种情况要执行以下四个步骤:
·把后继父节点的leftChild字段置为后继的右子节点;
·把后继的rightChild字段置为要删除节点的右子节点;
·把待删除节点从它父节点的leftChild或rightChild字段删除,把这个字段置为后继;
·把待删除的左子节点移除,将后继的leftChild字段置为待删除节点的左子节点。
如下图所示:
如果后继节点就是待删除节点的右子节点,这种情况就简单了,因为只需要把后继为跟的子树移到删除的节点的位置即可。如下图所示:
看到这里,就会发现删除时相当棘手的操作。实际上,因为它非常复杂,一些程序员都尝试着躲开它,他们在Node类中加了一个Boolean字段来标识该节点是否已经被删除,在其他操作之前会先判断这个节点是不是已经删除了,这样删除节点不会改变树的结构,。当然树中还保留着这种已经删除的节点,对存储造成浪费,但是如果没有那么多删除的话,这也不失为一个好方法。下面是二叉搜索树的主要代码:
- public class BinaryTree {
- private BNode root; //根节点
- public BinaryTree() {
- root = null;
- }
- //二叉搜索树查找的时间复杂度为O(logN)
- public BNode find(int key) { //find node with given key
- BNode current = root;
- while(current.key != key) {
- if(key < current.key) {
- current = current.leftChild;
- }
- else {
- current = current.rightChild;
- }
- if(current == null) {
- return null;
- }
- }
- return current;
- }
- //插入节点
- public void insert(int key, double value) {
- BNode newNode = new BNode();
- newNode.key = key;
- newNode.data = value;
- if(root == null) { //if tree is null
- root = newNode;
- }
- else {
- BNode current = root;
- BNode parent;
- while(true) {
- parent = current;
- if(key < current.data) { //turn left ?:应为current.key
- current = current.leftChild;
- if(current == null) {
- parent.leftChild = newNode;
- newNode.parent = parent;
- return;
- }
- }
- else { //turn right
- current = current.rightChild;
- if(current == null) {
- parent.rightChild = newNode;
- newNode.parent = parent;
- return;
- }
- }
- }
- }
- }
- //遍历二叉树
- public void traverse(int traverseType) {
- switch(traverseType)
- {
- case 1: System.out.println("Preorder traversal:");
- preOrder(root);//前向遍历
- break;
- case 2: System.out.println("Inorder traversal:");
- inOrder(root);//中向遍历
- break;
- case 3: System.out.println("Postorder traversal:");
- postOrder(root);//后向遍历
- break;
- default: System.out.println("Inorder traversal:");
- inOrder(root);
- break;
- }
- System.out.println("");
- }
- //前向遍历 :(右子树,左子树)
- private void preOrder(BNode localRoot) {
- if(localRoot != null) {
- System.out.print(localRoot.data + " ");
- preOrder(localRoot.leftChild);
- preOrder(localRoot.rightChild);
- }
- }
- //中向遍历 :(左,节点,右)
- private void inOrder(BNode localRoot) {
- if(localRoot != null) {
- inOrder(localRoot.leftChild);
- System.out.print(localRoot.data + " ");
- inOrder(localRoot.rightChild);
- }
- }
- //后向遍历 :(左子树,右子树)
- private void postOrder(BNode localRoot) {
- if(localRoot != null) {
- postOrder(localRoot.leftChild);
- postOrder(localRoot.rightChild);
- System.out.print(localRoot.data + " ");
- }
- }
- //查找最小值
- /*根据二叉搜索树的存储规则,最小值应该是左边那个没有子节点的那个节点*/
- public BNode minNumber() {
- BNode current = root;
- BNode parent = root;
- while(current != null) {
- parent = current;
- current = current.leftChild;
- }
- return parent;
- }
- //查找最大值
- /*根据二叉搜索树的存储规则,最大值应该是右边那个没有子节点的那个节点*/
- public BNode maxNumber() {
- BNode current = root;
- BNode parent = root;
- while(current != null) {
- parent = current;
- current = current.rightChild;
- }
- return parent;
- }
- //删除节点
- /*
- * 删除节点在二叉树中是最复杂的,主要有三种情况:
- * 1. 该节点没有子节点(简单)
- * 2. 该节点有一个子节点(还行)
- * 3. 该节点有两个子节点(复杂)
- * 删除节点的时间复杂度为O(logN)
- */
- public boolean delete(int key) {
- BNode current = root;
- // BNode parent = root;
- boolean isLeftChild = true;
- if(current == null) {
- return false;
- }
- //寻找要删除的节点
- while(current.data != key) {
- // parent = current;
- if(key < current.key) {
- isLeftChild = true;
- current = current.leftChild;
- }
- else {
- isLeftChild = false;
- current = current.rightChild;
- }
- if(current == null) {
- return false;
- }
- }
- //找到了要删除的节点,下面开始删除
- //1. 要删除的节点没有子节点,直接将其父节点的左子节点或者右子节点赋为null即可
- if(current.leftChild == null && current.rightChild == null) {
- return deleteNoChild(current, isLeftChild);
- }
- //3. 要删除的节点有两个子节点
- else if(current.leftChild != null && current.rightChild != null) {
- return deleteTwoChild(current, isLeftChild);
- }
- //2. 要删除的节点有一个子节点,直接将其砍断,将其子节点与其父节点连起来即可,要考虑特殊情况就是删除根节点,因为根节点没有父节点
- else {
- return deleteOneChild(current, isLeftChild);
- }
- }
- public boolean deleteNoChild(BNode node, boolean isLeftChild) {
- if(node == root) {
- root = null;
- return true;
- }
- if(isLeftChild) {
- node.parent.leftChild = null;
- }
- else {
- node.parent.rightChild = null;
- }
- return true;
- }
- public boolean deleteOneChild(BNode node, boolean isLeftChild) {
- if(node.leftChild == null) {
- if(node == root) {
- root = node.rightChild;
- node.parent = null;
- return true;
- }
- if(isLeftChild) {
- node.parent.leftChild = node.rightChild;
- }
- else {
- node.parent.rightChild = node.rightChild;
- }
- node.rightChild.parent = node.parent;
- }
- else {
- if(node == root) {
- root = node.leftChild;
- node.parent = null;
- return true;
- }
- if(isLeftChild) {
- node.parent.leftChild = node.leftChild;
- }
- else {
- node.parent.rightChild = node.leftChild;
- }
- node.leftChild.parent = node.parent;
- }
- return true;
- }
- public boolean deleteTwoChild(BNode node, boolean isLeftChild) {
- BNode successor = getSuccessor(node);
- if(node == root) {
- successor.leftChild = root.leftChild;
- successor.rightChild = root.rightChild;
- successor.parent = null;
- root = successor;
- }
- else if(isLeftChild) {
- node.parent.leftChild = successor;
- }
- else {
- node.parent.rightChild = successor;
- }
- successor.leftChild = node.leftChild;//connect successor to node's left child
- return true; //不需要加successor.rightChild = node.rightChild;是因为下面获取后继时处理过了
- }
- //获得要删除节点的后继节点(中序遍历的下一个节点)
- public BNode getSuccessor(BNode delNode) {
- BNode successor = delNode;
- BNode current = delNode.rightChild;
- while(current != null) {
- successor = current;
- current = current.leftChild;
- }
- if(successor != delNode.rightChild) {
- successor.parent.leftChild = successor.rightChild;
- if(successor.rightChild != null) {
- successor.rightChild.parent = successor.parent;//删除后续节点在原来的位置
- }
- successor.rightChild = delNode.rightChild;//将后续节点放到正确位置,与右边连上
- }
- return successor;
- }
- }
- class BNode {
- public int key;
- public double data;
- public BNode parent;
- public BNode leftChild;
- public BNode rightChild;
- public void displayNode() {
- System.out.println("{" + key + ":" + data + "}");
- }
- }
相关推荐
数据结构与算法是计算机科学的基础,对于理解和设计高效的软件至关重要。这个由廖荣贵和许正宪编著的“数据结构与算法”书籍的PPT版本,提供了对这些关键概念的深入讲解。虽然书中可能未包含源代码,但PPT的形式仍然...
数据结构与算法树与二叉树 树是一种非线性数据结构,常用于存储和管理大量数据。它由节点和边组成,每个节点都包含一个值和一个或多个子节点的指针。树的基本信息包括定义、基本术语、森林与树的关系、树结构与线性...
java数据结构与算法之平衡二叉树的设计与实现分析.pdf
北大数据结构与算法课件_二叉树.ppt
java数据结构与算法之平衡二叉树AVL树的设计与实现分析.doc
在IT领域,数据结构与算法是编程基础的重要组成部分,尤其对于Java开发者来说,掌握二叉树这一经典数据结构及其相关的算法至关重要。本资料主要聚焦于Java实现的二叉树问题,特别是那些常出现在面试和在线编程挑战...
《数据结构与算法》(二叉树)家谱管理系统是一个基于计算机科学技术的项目,它涉及到网络化数据结构、算法和二叉树的概念。这个系统旨在处理和管理家谱信息,利用计算机的优势进行信息存储、检索和展示。在这个课程...
数据结构课程设计报告-二叉树的遍历 本报告基于二叉树的遍历方法,旨在通过递归和非递归两种方法创建一棵二叉树,并对其进行先序遍历、中序遍历、后序遍历及层次遍历,并求出该二叉树的深度和叶子结点数。同时,...
根据提供的文件信息,这里主要关注的是“C++数据结构与算法(第4版)”这一主题,虽然实际内容并未给出具体章节或知识点,但我们可以基于标题、描述以及部分已知内容来推测书中可能涵盖的关键知识点。 ### C++数据...
数据结构与算法是计算机科学的基础,对于理解和设计高效的软件至关重要。本课程的资源"恋上数据结构与算法课件.rar"涵盖了多个核心概念,旨在...爱数据结构与算法,就立即行动,打开这个压缩包,开始你的学习之旅吧!
数据结构与算法(C#).PDF及代码 第1章 Collections类、泛型类和Timing类概述 第2章 数组和ArrayList 第3章 基础排序算法 第4章 基础查找算法 第5章 栈和队列 第6章 BitArray类 第7章 字符串、String类和StringBuioder...
数据结构与算法-二叉树上结点的路径 本文将详细讲解数据结构与算法中二叉树上结点的路径,包括二叉树的创建、结点路径的查找、前序、中序、后序遍历等知识点。 二叉树的创建 在上面的代码中,我们可以看到二叉树...
标题中的“Python数据结构与算法”指向了文档内容的核心主题,即通过Python语言来探索和实现数据结构与算法的相关知识。这部分内容主要涵盖Python编程语言中数据结构的实现方式、算法的设计技巧以及递归等编程概念的...
数据结构与算法分析 C语言版 二叉树,平衡树,图论
Python 数据结构与算法分析 Python 数据结构与算法分析是计算机科学中的一门重要课程,涵盖了数据结构和算法两个方面的内容。在 Python 中,数据结构是指对数据的组织、存储和管理方式,而算法则是指解决问题或完成...
在数据结构与算法设计领域,有若干核心概念与知识点,包括线性结构、树形结构、图结构、查找与排序等。以下是对这些主题的深入分析与解释。 ### 线性结构 在数据结构中,线性结构是最基本的一种数据组织形式。它...