数据结构和算法 – 9.二叉树和二叉查找树
9.1.树的定义
9.2.二叉树
人们把每个节点最多拥有不超过两个子节点的树定义为二叉树。由于限制子节点的数量为 2,人们可以为插入数据、删除数据、以及在二叉树中查找数据编写有效的程序了。
在考虑一种更加特殊的二叉树——二叉查找树的时候,鉴别子节点是很重要的。二叉查找树是一种较小数据值存储在左节点内而较大数据值存储在右节点内的二叉树。正如即将看到的那样,这种属性可以使查找非常有效。
9.2.1.构造二叉查找树
二叉查找树由节点组成,所以需要一个 Node 类,这个类类似于链表实现中用到的 Node 类。
public class Node
{
public int Data;
public Node Left;
public Node Right;
public void DisplayNode()
{
Console.WriteLine(Data + " ");
}
}
下面是插入方法
public class BinarySearchTree
{
//根
public Node root; public BinarySearchTree()
{
root = null;
}
} public void Insert(int i)
{
Node newNode = new Node();
newNode.Data = i;
//如果根节点没数据
if (root == null)
{
root = newNode;
}
else
{
Node current = root;
Node parent;
while (true)
{
parent = current;
if (i < current.Data)
{
current = current.Left;
if (current == null)
{
parent.Left = newNode;
break;
}
}
else
{
current = current.Right;
if (current == null)
{
parent.Right = newNode;
break;
}
}
}
}
}
9.2.2.遍历二叉查找树
中序遍历
既然这个方法是按照升序方式访问每一个节点,所以此方法必须访问到每棵子树的左节点和右节点,
跟着是访问根节点的左子节点下的子树,再接着是访问根节点的右子节点下的子树
//中序遍历
public void InOrder(Node theRoot)
{
if (theRoot != null)
{
InOrder(theRoot.Left);
theRoot.DisplayNode();//显示
InOrder(theRoot.Right);
}
}
先序遍历
//先序遍历
public void PreOrder(Node theRoot)
{
if (!(theRoot == null))
{
theRoot.DisplayNode();
PreOrder(theRoot.Left);
PreOrder(theRoot.Right);
}
}
后序遍历
//后续遍历
public void PostOrder(Node theRoot)
{
if (!(theRoot == null))
{
PostOrder(theRoot.Left);
PostOrder(theRoot.Right);
theRoot.DisplayNode();
}
}
9.2.3 在二叉查找树中查找节点和最大/最小值
人们总可以在根节点左子树的最左侧子节点上找到 BST 内的最小值。
public int FindMin()
{
Node current = root;
while (current.Left != null)
current = current.Left;
return current.Data;
}
最大值同理
public int FindMax()
{
Node current = root;
while (current.Right != null)
current = current.Right;
return current.Data;
}
查找指定值
public Node Find(int key)
{
Node current = root;
while (current.Data != key)
{
if (key < current.Data)
current = current.Left;
else
current = current.Right;
if (current == null)
return null;
}
return current;
}
9.2.4.删除叶子节点
step1”叶子节点:直接删除该节点,再修改其父节点的指针(注意分是根节点和不是根节点)
(例:删除72)
step2”单支节点(即只有左子树或右子树):让p的子树与p的父亲节点相连,再删除p即可;(注意分是根节点和不是根节点两种情况)
(例:删除79)
step3”节点p的左子树和右子树均不为空:首先找到p的后继y,因为y一定没有左子树,所以可以删除y,并让y的父亲节点成为y的右子树的父亲节点,并用y的值代替p的值;或者可以先找到p的前驱x,x一定没有右子树,所以可以删除x,并让x的父亲节点成为y的左子树的父亲节点。
9.2.4.1.删除带有一个子节点的节点
当要删除的节点有一个子节点的时候,需要检查四个条件:
1.这个节点的子节点可能是左子节点;
2.这个节点的子节点可能是右子节点;
3.要删除的这个节点可能是左子节点;
4.要删除的这个节点可能是右子节点。
//如果右子节点为空null
else if (current.Right == null)
{
//查看是否在根节点上
if (current == root)
root = current.Left;
else if (isLeftChild)
parent.Left = current.Left;
else
parent.Right = current.Right;
}
//如果左子节点为空
else if (current.Left == null)
{
if (current == root)
root = current.Right;
else if (isLeftChild)
parent.Left = parent.Right;
else
parent.Right = current.Right;
}
说明:
9.2.4.2.删除带有两个子节点的节点
为了找到后继节点,要到原始节点的右子节点上。根据定义这个节点必须比原始节点大。然后,开始沿着左子节点路径走直到用完节点为止。既然子树(像一棵树)内的最小值必须是在左子节点路径的末端,沿着这条路径到达末端就会找到大于原始节点的最小节点。
后继节点的代码
public Node GetSuccessor(Node delNode)
{
//把删除的节点作为继任者的父节点
Node successorParent = delNode;
//把删除的节点作为继任者的节点
Node successor = delNode;
//当前节点的右子节点
Node current = delNode.Right; while (current != null)
{
successorParent = current;
successor = current;
current = current.Left;
} //current.Left = null; if (successor != delNode.Right)
{
//1.把后继节点的右子节点赋值为后继节点的父节点的左子节点
successorParent.Left = successor.Right;
//2.要删除节点的右子节点赋值为后继节点的右子节点
successor.Right = delNode.Right; //后继节点的右子节点的左子节点必须设置为空,不然会死循环
successor.Right.Left = null;
}
return successor;
}
两个节点的代码
else
{ Node successor = GetSuccessor(current); if (current == root)
{
root = successor;
}
else if (isLeftChild)
{
parent.Left = successor;
}
else
{
//3.从父节点的右子节点中移除当前节点,并且把它指向后继节点
parent.Right = successor;
}
//4.从当前节点中移除当前节点的左子节点,并且把它指向后继节点的左子节点
successor.Left = current.Left;
}
完整的删除代码
public bool Delete(int key)
{
Node current = root;
Node parent = root;//让父节点也等于根
//判断是否为左子节点
bool isLeftChild = true;
//循环删除节点
while (current.Data != key)
{ parent = current;
//值小于当前节点的值,在左字节点查找
if (key < current.Data)
{
//取左子节点的数据
isLeftChild = true;
current = current.Left;
}
else
{
isLeftChild = false;
current = current.Right;
}
}
//如果查找不到,就退出
if (current == null)
{
return false;
}
//查看这个节点的左子节点和右子节点是否为空
if ((current.Left == null) & (current.Right == null))
{
//如果是根节点,就把它设置为空
if (current == root)
root = null;
//如果为左子节点,就把父节点的左子节点设置为空
else if (isLeftChild)
parent.Left = null; else//如果为右子节点,就把父节点的右子节点设置为空
parent.Right = null;
}
//如果右子节点为空null
else if (current.Right == null)
{
//查看是否在根节点上
if (current == root)
root = current.Left;
else if (isLeftChild)
parent.Left = current.Left;
else
parent.Right = current.Right;
}
//如果左子节点为空
else if (current.Left == null)
{
if (current == root)
root = current.Right;
else if (isLeftChild)
parent.Left = parent.Right;
else
parent.Right = current.Right;
}
//左子节点和右子节点都有的情况
else
{ Node successor = GetSuccessor(current); if (current == root)
{
root = successor;
}
else if (isLeftChild)
{
parent.Left = successor;
}
else
{
//3.从父节点的右子节点中移除当前节点,并且把它指向后继节点
parent.Right = successor;
}
//4.从当前节点中移除当前节点的左子节点,并且把它指向后继节点的左子节点
successor.Left = current.Left;
}
return true;
}
数据结构和算法 – 9.二叉树和二叉查找树的更多相关文章
- 【数据结构与算法】二叉树的 Morris 遍历(前序、中序、后序)
前置说明 不了解二叉树非递归遍历的可以看我之前的文章[数据结构与算法]二叉树模板及例题 Morris 遍历 概述 Morris 遍历是一种遍历二叉树的方式,并且时间复杂度O(N),额外空间复杂度O(1 ...
- Java数据结构和算法(六)--二叉树
什么是树? 上面图例就是一个树,用圆代表节点,连接圆的直线代表边.树的顶端总有一个节点,通过它连接第二层的节点,然后第二层连向更下一层的节点,以此递推 ,所以树的顶端小,底部大.和现实中的树是相反的, ...
- JS数据结构第五篇 --- 二叉树和二叉查找树
一.二叉树的基本概念 从逻辑结构角度来看,前面说的链表.栈.队列都是线性结构:而今天要了解的“二叉树”属于树形结构. 1.1 多叉树的基本概念,以上图中“多叉树”为例说明 节点:多叉树中的每一个点都叫 ...
- 数据结构与算法之二叉树 ——in dart
用dart语言实现的二叉树,实现了插入.查找.删除,中序遍历.前序.后序遍历等功能. class BinaryTree<E extends Comparable> { Node<E& ...
- Java数据结构和算法(七)--AVL树
在上篇博客中,学习了二分搜索树:Java数据结构和算法(六)--二叉树,但是二分搜索树本身存在一个问题: 如果现在插入的数据为1,2,3,4,5,6,这样有序的数据,或者是逆序 这种情况下的二分搜索树 ...
- 二叉树和二叉查找树--数据结构与算法JavaScript描述(10)
二叉树和二叉查找树 概念 树是一种非线性的数据结构,以分层的方式存储数据. 树被用来存储具有层级关系的数据,比如文件系统的文件: 树还被用来存储有序列表. 一棵树最上面的节点称为根节点. 如果一个节点 ...
- javascript数据结构与算法-- 二叉树
javascript数据结构与算法-- 二叉树 树是计算机科学中经常用到的一种数据结构.树是一种非线性的数据结构,以分成的方式存储数据,树被用来存储具有层级关系的数据,比如文件系统的文件,树还被用来存 ...
- 常见基本数据结构——树,二叉树,二叉查找树,AVL树
常见数据结构——树 处理大量的数据时,链表的线性时间太慢了,不宜使用.在树的数据结构中,其大部分的运行时间平均为O(logN).并且通过对树结构的修改,我们能够保证它的最坏情形下上述的时间界. 树的定 ...
- 数据结构和算法(Golang实现)(27)查找算法-二叉查找树
二叉查找树 二叉查找树,又叫二叉排序树,二叉搜索树,是一种有特定规则的二叉树,定义如下: 它是一颗二叉树,或者是空树. 左子树所有节点的值都小于它的根节点,右子树所有节点的值都大于它的根节点. 左右子 ...
随机推荐
- JavaScript的作用域与作用域链
作用域 作用域就是变量与函数的可访问范围,即作用域控制着变量与函数的可见性和生命周期.可以说,变量和函数在什么时候可以用,什么时候被摧毁,这都与作用域有关. JavaScript中,变量的作用域有全局 ...
- WebStorm设置手机测试服务器-局域网内其他设备访问
前端开发中,经常需要将做好的页面给其他同事预览或手机测试,之前一直用的第三方本地服务器usbwebserver,偶然了解到WebStorm内置服务器也可以满足此需求,来看看如何设置吧~~ 1.端口更改 ...
- 解决 QtCreator 3.5(4.0)无法输入中文的问题
解决 QtCreator 3.5.1无法输入中文的问题 [TOC] 环境是ubuntu 15.10 ubuntu软件源中下载安装的fctix-libs-qt5现在没有用,版本太旧了. 自己下载fcti ...
- qmake的使用
[TOC] 本文由乌合之众 lym瞎编,欢迎转载 blog.cnblogs.net/oloroso 本文由乌合之众 lym瞎编,欢迎转载 my.oschina.net/oloroso *** 还是先说 ...
- MyBatis 中 Result Maps collection already contains value for xxx 错误原因
出现此类错误的原因是同一个DAO操作多个Mapper导致的,将Mapper配置文件的 ResultMap的ID修改成不相同即可解决
- Tomcat启动服务报错:Unknown version string [3.1]. Default version will be used.
用Intellij IDEA 部署Web项目,Tomcat启动后报错Unknown version string [3.1]. Default version will be used. 作者的问题出 ...
- nginx配置多域名映射方法(本地hosts)
本地测试网站的时候如果不想用localhost/xxxx的形式访问,可能就需要修改hosts文件来映射了,但是一个网站还好,假如有多个网站的话就不行了. 这时就需要配置多域名映射 比如hosts中配置 ...
- jQuery数组($.each,$.grep,$.map,$.merge,$.inArray,$.unique,$.makeArray)处理函数详解
1. $.each(array, [callback]) 遍历[常用] 解释: 不同于例遍jQuery对象的$().each()方法,此方法可用于例遍任何对象.回调函数拥有两个参数:第一个为对象的成员 ...
- 《转载》化繁为简 如何向老婆解释MapReduce?
本文转载自http://server.zol.com.cn/329/3295529.html 昨天,我在Xebia印度办公室发表了一个关于MapReduce的演说.演说进行得很顺利,听众们都能够理解M ...
- 基础知识《七》---Java多线程详解