数据结构学习笔记二:二叉查找树

二叉查找树的特点:

(1)左边的所有节点都要小于根节点的值

(2)右边的所有节点都要不小于根节点的值

(3)原树的子树都符合(1)(2)条件。

二叉查找树又名二叉排序树,因为只需中序遍历它就可以得到排序的树了,对于二叉排序树的操作主要有:

(1)插入:通过插入来初始化树。本文是用链式,而不是存储在数组中。

(2)遍历:有三种方法分别为:先序,中序,后序。

(3)查找:有三种查找分别为:查找最大值,最小值,与某种特定的值的节点。

(4)删除:删除一个节点类型有三种分别为:无左右孩子的节点,有一个孩子的节点,有两个孩子的节点。

树是由节点和节点之间的边组成的,所以首先要声明一个节点类Node,一个节点有两个孩子和节点的值。

public class Node
{
    public  int value;
    public   Node leftChild;
    public  Node rightChild;
    public Node(int value)
    {
        leftChild = null;
        rightChild = null;
        this.value = value;
    }
}

节点类声明完成后,还需要一个树类,里面包含树的初始化以及一些对树的操作,一个默认构造函数来初始化一个空树和一个输出接点的值的函数。

class BinarySearchTree
{
    public  Node root;

    public BinarySearchTree()
    {
        root = null;
    }

    public void Dispaly(Node node)
    {
        Console.Write(node.value + " ");
    }
}

要构成一棵树,就得插入数值了,根据二叉排序树的性质,左边孩子的值全部小于根节点的值,所以在插入一个数值的时候

(1)首先判断是否为空树,也就是根节点是否为空,如果为空,就将插入的值放入根节点中。

(2)判断值与根节点(本树的根,或者其子数)值的大小,来判断放在左孩子还是右孩子。

public void Insert(int v)
{
    Node newNode = new Node(v);
    Node parent;
    Node current;
    if (root == null)
        root = newNode;
    else
    {
        current=root;
        while (true)
        { 
            parent=current;
            if (v < current.value)
            {
                current = current.leftChild;
                if (current == null)
                {
                    parent.leftChild = newNode;
                    break;
                }
            }
            else
            {
                current = current.rightChild;
                if (current == null)
                {
                    parent.rightChild = newNode;
                    break;
                }
            }
        }
    }
}

插入完成之后,一棵树就形成了,现在来遍历一下,三种遍历用递归实现,使逻辑更清晰

public void InOrder(Node root)
{
    Node node = root;
    if (node != null)
    {
        InOrder(node.leftChild);
        this.Dispaly(node);
        InOrder(node.rightChild);
    }
}
public void PreOrder(Node root)
{
    Node node = root;
    if (node != null)
    {
        this.Dispaly(node);
        PreOrder(node.leftChild);
        PreOrder(node.rightChild);
    }
}
public void PostOrder(Node root)
{
    Node node = root;
    if (node != null)
    {
        PostOrder(node.leftChild);
        PostOrder(node.rightChild);
        this.Dispaly(node);
    }
}
三种遍历是根节点的位序不同。接着就是查找树中的最大值,最小值,以及特定的值。
(1)最大值:在树的最右边的孩子
(2)最小值:在树的最左边的孩子
(3)特定值:用循环,判断与根值的大小。
public Node FindMax()
{ 
    Node current=root;
    while (current.rightChild != null)
        current = current.rightChild;
    return current;
}
public Node FindMin()
{
    Node current = root;
    while (current.leftChild != null)
        current = current.leftChild;
    return current;
}
public Node Find(int v)
{
    Node current = root;
    while (true)
    {
        if (v < current.value)
            current = current.leftChild;
        else if (v > current.value)
            current = current.rightChild;
        else
            return current;
        if (current == null)
            return null;
    }
}

最后就是删除了,由于使用链式构成的数,所以在树中删除一个节点的方法和在链表中删除一个节点的方法类似,只不过改动的“指针”更多一点,因为树的节点是一对多的。用if-else语句分别区分删除的三种类型的节点。

public bool Delete(int v)
{ 
    Node current=root;
    Node parent = current;
    //找到要删除的节点
    while (current.value != v)
    {
        parent = current;
        if (v < current.value)
        {
            current = current.leftChild;
        }
        else
        {
            current = current.rightChild;
        }
        if (current == null)
            return false;
    }
    //如果是没有孩子的节点
    if (current.leftChild == null && current.rightChild == null)
    {
        if (current == root)
            root = null;
         if (current ==parent.leftChild)
            parent.leftChild = null;
         if (current ==parent.rightChild)
            parent.rightChild = null;
    }
     //如果是有右孩子的节点
    else if (current.rightChild != null)
    {
        if (current == root)
            root = current.leftChild;
        if (current == parent.leftChild)
            parent.leftChild = current.rightChild;
        if (current == parent.rightChild)
            parent.rightChild = current.rightChild;
    }
    //如果是有左孩子的节点
    else if(current.leftChild!=null)
    {
        if(current==root)
            root=current.rightChild;
        if(current==parent.leftChild)
            parent.leftChild=current.leftChild;
        if(current==parent.rightChild)
            parent.rightChild=current.leftChild;
    }
    //有两个孩子的节点
    else
    {
        Node newNode=GetSuccessor(current);
        newNode.rightChild=current.rightChild;
        newNode.leftChild=current.leftChild;
        if(current==parent.leftChild)
            parent.leftChild=newNode;
        if(current==parent.rightChild)
            parent.rightChild=newNode;
    }
    return true;
}

其中查找successor节点的方法如下

/// <summary>
/// 删除一个有两个孩子的节点时,应该以中序遍历时将删节点delNode
/// 右邻节点successor(以删除的节点为子树的根节点,最左边的
/// 节点来替代被删的节点)来代替,因为successor的值是大于delNode 值中
/// 最小的
/// </summary>
/// <param name="delNode"></param>
/// <returns></returns>
private Node GetSuccessor(Node delNode)
{
    Node current = delNode;
    while (current.leftChild != null)
        current = current.leftChild;
    return current;
}

测试如下:

static void Main(string[] args)
{
    BinarySearchTree bs = new BinarySearchTree();
    bs.Insert(2);
    bs.Insert(5);
    bs.Insert(6);
    bs.Insert(10);
    bs.Insert(12);
    bs.Insert(15);
    bs.Insert(18);
    bs.InOrder(bs.root);
    Console.WriteLine("最大的数:{0}", bs.FindMax().value);
    Console.WriteLine("最小的数:{0}", bs.FindMin().value);
    Console.WriteLine("找到节点的值:{0}",bs.Find(6).value);
    bs.Delete(5);
    bs.InOrder(bs.root);
    Console.ReadKey();
}

输出:

image

总结:

(1)了解二叉排序树的基本原理,就要将其应用。

(2)应该还有顺序存储方式,本文是用链式。

(3)大道至简,从简至繁。

(4)如有错误或者建议请在回复中说明或者发送zabery@126.com

posted @ 2010-05-05 22:40  zabery  阅读(444)  评论(0编辑  收藏  举报