数据结构和算法 – 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)查找算法-二叉查找树
二叉查找树 二叉查找树,又叫二叉排序树,二叉搜索树,是一种有特定规则的二叉树,定义如下: 它是一颗二叉树,或者是空树. 左子树所有节点的值都小于它的根节点,右子树所有节点的值都大于它的根节点. 左右子 ...
随机推荐
- laravel打印sql语句
打印sql语句,直接在你执行SQL语句后输出 方法一: $queries = DB::getQueryLog(); $a = end($queries); $tmp = str_replace('?' ...
- Failed to resolve: junit:junit:4.12
在Android Studio创建项目之后,提示一个junit错误. 解决方案: 第一步:找到build.gradle的file,如图: 第二步: 第三步:把中间行代码"testCompi ...
- jQuery lazyload插件详解和问题解答
lazyload插件用于图片延迟加载,节省服务器带宽,减少服务器请求次数,提高网站的页面加载速度,用于网站性能优化,只有当图片在窗口可视范围内时才向服务器请求: 参数: threshold:设置距离窗 ...
- Struts2中的OGNL通配符
<action name="*_*" class="action.{1}Action" method="{2}"> 匹配,第一个 ...
- 7.4---加法替代运算(CC150)
注意:1,除法那里b+=b是错的.b一直在改变. 2,仔细一点. import java.util.*; public class AddSubstitution { public int cal ...
- atom 折腾记(转载的)
http://www.bkjia.com/webzh/999078.html
- POJ 3728
http://poj.org/problem?id=3278 题目大意就是在同一坐标轴上给你一个人的坐标,一个牛的坐标,而人的运动每一次运动有三种方式,一种是后退1,一种是前进1,还有一种是坐标翻倍, ...
- 解读Unity中的CG编写Shader系列三
转自http://www.itnose.net/detail/6096068.html 在上一个例子中,我们得到了由mesh组件传递的信息经过数学转换至合适的颜色区间以颜色的形式着色到物体上.这篇文章 ...
- win8内置管理员用户无法激活此应用
在运行中输入:“gpedit.msc”,就会启动组策略编辑器, 计算机配置 --> Windows设置 --> 安全设置 --> 本地策略 --> 安全选项 :::: 用 ...
- alv中编辑的时候quan字段小数位数被截取掉
alv中编辑的时候quan字段小数位数被截取掉:位数太多(最大只能为 0) 解决方法: ls_fcat-fieldname = 'ZKBSL'. ls_fcat-coltext = '卡板数 ...