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.二叉树和二叉查找树的更多相关文章

  1. 【数据结构与算法】二叉树的 Morris 遍历(前序、中序、后序)

    前置说明 不了解二叉树非递归遍历的可以看我之前的文章[数据结构与算法]二叉树模板及例题 Morris 遍历 概述 Morris 遍历是一种遍历二叉树的方式,并且时间复杂度O(N),额外空间复杂度O(1 ...

  2. Java数据结构和算法(六)--二叉树

    什么是树? 上面图例就是一个树,用圆代表节点,连接圆的直线代表边.树的顶端总有一个节点,通过它连接第二层的节点,然后第二层连向更下一层的节点,以此递推 ,所以树的顶端小,底部大.和现实中的树是相反的, ...

  3. JS数据结构第五篇 --- 二叉树和二叉查找树

    一.二叉树的基本概念 从逻辑结构角度来看,前面说的链表.栈.队列都是线性结构:而今天要了解的“二叉树”属于树形结构. 1.1 多叉树的基本概念,以上图中“多叉树”为例说明 节点:多叉树中的每一个点都叫 ...

  4. 数据结构与算法之二叉树 ——in dart

    用dart语言实现的二叉树,实现了插入.查找.删除,中序遍历.前序.后序遍历等功能. class BinaryTree<E extends Comparable> { Node<E& ...

  5. Java数据结构和算法(七)--AVL树

    在上篇博客中,学习了二分搜索树:Java数据结构和算法(六)--二叉树,但是二分搜索树本身存在一个问题: 如果现在插入的数据为1,2,3,4,5,6,这样有序的数据,或者是逆序 这种情况下的二分搜索树 ...

  6. 二叉树和二叉查找树--数据结构与算法JavaScript描述(10)

    二叉树和二叉查找树 概念 树是一种非线性的数据结构,以分层的方式存储数据. 树被用来存储具有层级关系的数据,比如文件系统的文件: 树还被用来存储有序列表. 一棵树最上面的节点称为根节点. 如果一个节点 ...

  7. javascript数据结构与算法-- 二叉树

    javascript数据结构与算法-- 二叉树 树是计算机科学中经常用到的一种数据结构.树是一种非线性的数据结构,以分成的方式存储数据,树被用来存储具有层级关系的数据,比如文件系统的文件,树还被用来存 ...

  8. 常见基本数据结构——树,二叉树,二叉查找树,AVL树

    常见数据结构——树 处理大量的数据时,链表的线性时间太慢了,不宜使用.在树的数据结构中,其大部分的运行时间平均为O(logN).并且通过对树结构的修改,我们能够保证它的最坏情形下上述的时间界. 树的定 ...

  9. 数据结构和算法(Golang实现)(27)查找算法-二叉查找树

    二叉查找树 二叉查找树,又叫二叉排序树,二叉搜索树,是一种有特定规则的二叉树,定义如下: 它是一颗二叉树,或者是空树. 左子树所有节点的值都小于它的根节点,右子树所有节点的值都大于它的根节点. 左右子 ...

随机推荐

  1. [Leetcode19] Remove Nth Node From End of List

    视频讲解  http://v.youku.com/v_show/id_XMTY1MTMzNjAyNA==.html (1)定义两个指针 ListNode fast = head; ListNode s ...

  2. 常用聚类算法(一) DBSCAN算法

    1.DBSCAN简介 DBSCAN(Density-Based Spatial Clustering of Applications with Noise,具有噪声的基于密度的聚类方法)是一种基于密度 ...

  3. ndk学习9: 动态使用共享库

    动态使用共享库函数 dll_main      环境介绍 续上节代码 目录结构:   android.mk如下: LOCAL_PATH := $(call my-dir) include $(CLEA ...

  4. Java 日期加减计算.

    1.用Java.util.Calender来实现 Calendar calendar=Calendar.getInstance();      calendar.setTime(new Date()) ...

  5. ffmpeg解码视频流

    //初始化.注册编解码器 avcodec_init(); av_register_all(); avformat_network_init(); //选取测试文件 char* FileName = & ...

  6. igbinary vs serialize vs json_encode

    igbinary vs serialize vs json_encode 2010-04-18 @ 23:01:58 · 作者 Volcano · 归类于 php 你可能会感兴趣的内容 关于" ...

  7. jQuery工作原理

    jQuery的开篇声明里有一段非常重要的话:jQuery是为了改变javascript的编码方式而设计的.从这段话可以看出jQuery本身并不是UI组件库或其他的一般AJAX类库.jQuery改变ja ...

  8. JavaBean转换为XML的源码

    package com.cmge.utils; import java.util.Iterator; import com.cmge.org.oa.bean.OADepartment; import ...

  9. poj 2378 (dijkstra)

    http://poj.org/problem?id=2387 一个dijkstra的模板题 #include <stdio.h> #include <string.h> #de ...

  10. spring + myBatis 常见错误:SQL语法错误

    在程序运行时,有时候会出现如下错误: 这个错误通常是你的sqlmapper.xml中sql语句语法有错误.所以请仔细查看你sql语句是否正确,比如{#id}这样写就会报上述错误,其实应该#{id}这样 ...