数据结构和算法 – 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)查找算法-二叉查找树
二叉查找树 二叉查找树,又叫二叉排序树,二叉搜索树,是一种有特定规则的二叉树,定义如下: 它是一颗二叉树,或者是空树. 左子树所有节点的值都小于它的根节点,右子树所有节点的值都大于它的根节点. 左右子 ...
随机推荐
- NSFileManager
//返回一个字符串数组(子路径), 并且不包含文件夹 [[NSFileManager defaultManager] contentsOfDirectoryAtPath:folderPath erro ...
- 不安装Oracle客户端使用PLSQL
要连接Oracle往往需要安装上百兆的ORACLE客户端,显得十分麻烦.先就介绍如何通过使用精简客户端,且不需要安装的客户端,配合PLSQL连接oracle数据库. 其实这些操作都很简单,写在这里 ...
- centos7 关闭firewall安装iptables并配置
一.配置防火墙,开启80端口.3306端口 CentOS 7.0默认使用的是firewall作为防火墙,这里改为iptables防火墙. 1.关闭firewall: systemctl stop fi ...
- HTTP协议详解篇(待续)
1.工作流程 HTTP通信机制是在一次完整的HTTP通信过程中,Web浏览器与Web服务器之间将完成下列7个步骤: (1)建立TCP连接 在HTTP工作开始之前,Web浏览器首先要通过网络与Web服务 ...
- COGS 2416.[HZOI 2016]公路修建 & COGS 2419.[HZOI 2016]公路修建2 题解
大意: [HZOI 2016]公路修建 给定一个有n个点和m-1组边的无向连通图,其中每组边都包含一条一级边和一条二级边(连接的顶点相同),同一组边中的一级边权值一定大于等于二级边,另外给出一个数k( ...
- 解决maven项目移动
解决使用maven的工程移动到另一台电脑(电脑无法访问maven中央仓库问题) 移动后出现下述结果: Publishing failedPublishing failed with multiple ...
- 一致性哈希算法以及其PHP实现
在做服务器负载均衡时候可供选择的负载均衡的算法有很多,包括: 轮循算法(Round Robin).哈希算法(HASH).最少连接算法(Least Connection).响应速度算法(Respons ...
- css3创建3D场景
浏览器本身是一个2维平面,对于3D的情况,实际上是增加了一个维度(深度),所以我们需要创建一个3D场景.这时浏览器不仅仅是一个平面了,更像是一个窗口,我们透过这个窗口去观察里面的三维世界.所谓的创建3 ...
- C#之系统自带保存属性
源代码下载链接 程序开发很多时候需要根据运行环境做不通的参数配置,通过写ini之类的文本文件是一种方法,但这种方法也同时会把数据暴露 Winform开发中可以将需要配置的字段属性保存到程序中(其实也是 ...
- delphi控件属性大全-详解-简介
http://blog.csdn.net/u011096030/article/details/18716713 button 组件: CAPTION 属性 :用于在按钮上显示文本内容 Cancel ...
