查找算法总结(3)--二叉查找树

转载 2017年05月26日 21:00:04

转自http://hxraid.iteye.com/blog/609312

一、简介

当所有的静态查找结构添加和删除一个数据的时候,整个结构都需要重建。这对于常常需要在查找过程中动态改变数据而言,是灾难性的。因此人们就必须去寻找高效的动态查找结构,我们在这讨论一个非常常用的动态查找树——二叉查找树 。
二叉查找树的特点:
(1) 若它的左子树不空,则左子树上所有结点的值均小于它的根结点的值
(2) 若它的右子树不空,则右子树上所有结点的值均大于它的根结点的值
(3) 它的左、右子树也分别为二叉查找树
这里写图片描述
我们中序遍历这两棵树发现一个有序的数据序列: 【1 2 3 4 5 6 7 8 】

二、二叉查找树的操作

1、查找操作
在a图中查找关键字6
1)如果数为空,查找失败
2)比较6和根节点,如果相等,查找成功,如果6大于根节点,转向节点的右子树,如果小于6,转向节点的左子树;
3)递归查找每一个节点,直到找到和6相等的节点,否则查找失败。

2、插入操作:
现在我们要查找一个数9,如果不存在则,添加进a图。我们看看二叉查找树动态添加的过程:
1). 数9和根节点4比较(9>4),则9放在节点4的右子树中。
2). 接着,9和节点5比较(9>5),则9放在节点5的右子树中。
3). 依次类推:直到9和节点8比较(9>8),则9放在节点8的右子树中,成为节点8的右孩子。
这个过程我们能够发现,动态添加任何一个数据,都会加在原树结构的叶子节点上,而不会重新建树。 由此可见,动态查找结构确实在这方面有巨大的优势。

3、删除操作:假设p指向待删除的节点,parent指向p的父母节点。删除p节点分为3中情况,根据不同的情况有不同的操作:
1)p是叶节点,直接删除

  • p是parent的左子节点,删除p节点并设置parent.left为空
  • p是parent的右子节点,删除p节点并设置parent.right为空

2)需要删除的节点只有一个左子树或右子树,用子树代替该点

  • 如果p是parent的左子节点并且p有左子节点,设置parent.left指向p的左子节点
  • 如果p是parent的左子节点并且p有右子节点,设置parent.left指向p的右子节点
  • 如果p是parent的右子节点并且p有左子节点,设置parent.right指向p的左子节点
  • 如果p是parent的右子节点并且p有右子节点,设置parent.right指向p的右子节点

3)需要删除的节点左、右子树都存在,此时不直接删除p节点,而是用p节点在中序遍历下的第一个后续节点s代替p节点,然后删除s节点。这样将3)转化为1)或者2)的问题。因为p在中序遍历下的后续节点要么是其右子节点且为叶子结点,要么是右子树中最左边的一个子孙节点,度为0或者1.

三、二叉查找树的实现

package binarytree;
/*
 * 二叉树查找树的表示和操作
 * 1、构建二叉树查找树
 * 2、二叉树查找树的查找,包括任意一个值,最大值,最小值,任意值的前驱和后继
 * 3、二叉树查找树的插入和删除
 * */
public class BSTDemo {
    private BSTDemo tree;
    private Node root;


    public BSTDemo(){}

    public Node getRoot(){
        return root;
    }

    //查找,递归
    public int find(Node node,int value){
        if(node==null) {
            System.out.println("查找失败");
            return value;
        }
        if(node.getKey()==value){
            return value;
        }
        if(node.getKey()>value){
            return find(node.getLChild(),value);
        }
        if(node.getKey()<value){
            return find(node.getRChild(),value);
        }

        return value;
    }

    //查找,非递归
    public int loopFind(Node node,int value){
        while(node!=null){
            if(node.getKey()==value){
                return value;
            }
            if(node.getKey()>value){
                node=node.getLChild();
            }
            else{
                node=node.getRChild();
            }
        }
        System.out.println("查找失败");
        return value;
    }

    //查找最小值
    public Node getMin(Node node){
        while(node.getLChild()!=null){
            node=node.getLChild();
        }
        return node;
    }

    //查找最大值
    public Node getMax(Node node){
        while(node.getRChild()!=null){
            node=node.getRChild();
        }
        return node;
    }

    //获取给定点的前驱
    public Node getPredecessor(Node node){
        //如果左子树不空,前驱是左子树的最大值
        if(node.getLChild()!=null){
            return getMax(node.getLChild());
        }
        //如果左子树为空
        Node p=node.getParent();
        Node x;
        while(p!=null && node==p.getLChild()){
            x=p;
            p=p.getParent();
        }
        return p;
    }
    //获取给顶点的后继
    public Node getSuccessor(Node node){
        //如果右子树不空,后继是右子树的最小值
        if(node.getRChild()!=null){
            return getMin(node.getRChild());
        }
        //如果右子树空
        Node p=node.getParent();
        Node x;
        while(p!=null && node==p.getRChild()){
            x=p;
            p=p.getParent();
        }
        return p;
    }

    //插入,非递归
    public void insert(int value){
        //如果树根为空,则树为空,插入的元素作为根
        Node temp=new Node(value);
        if(this.root==null){
            this.root=temp;
            return;
        }
        //如果树不为空,则查找插入合适的位置
        Node node=root;
        while(node!=null){
            if(node.getKey()==value){
                node.setKey(value);
            }
            if(node.getKey()>value){
                if(node.getLChild()==null){
                    node.setLChild(temp);
                    temp.setParent(node);
                    return;
                }else node=node.getLChild();
            }
            else{
                if(node.getRChild()==null){
                    node.setRChild(temp);
                    temp.setParent(node);
                    return;
                }else node=node.getRChild();
            }
        }
    }

    public void insertAll(int[] array){
        for(int x:array){
            insert(x);
        }
    }

    //给定一个节点,并删除
    public void remove(Node node){
        if(node==null) return;
        if(node.getLChild()==null){
            //左子树为空,用右子树代替
            trans(node,node.getRChild());
        }else if(node.getRChild()==null){
            //如果右子树空,用左子树代替
            trans(node,node.getLChild());
        }else{
            //如果都不空,首先找到该点的直接后继
            Node suc=getSuccessor(node);
            if(suc.getParent()!=node){
                //如果直接后继的父节点不是node,因为suc没有左子树,所有用右子树代替
                trans(suc,suc.getRChild());
                suc.setRChild(node.getRChild());
                suc.getRChild().setParent(suc);
            }
            //如果直接后继是node的右子树,因为suc没有左子树,所以用suc直接代替node
            trans(node,suc);
            suc.setLChild(node.getLChild());
            suc.getLChild().setParent(node);

        }
    }

    //用y替换x
    public void trans(Node x,Node y){
        if (x.getParent()==null){
            //如果是根节点
            this.root=y;
        }else if(x==x.getParent().getLChild()){
            //如果是左子树
            x.getParent().setLChild(y);
        }else x.getParent().setRChild(y);

        if(y==null){
            //如果y是空
            x.setParent(null);
        }
    }
//判断一个二叉树是否为二叉查找树
public boolean isSorted(Node node){
    if(node==null) return true;
    return (node.getLChild()==null || node.getLChild()!=null && node.getKey()>node.getLChild().getKey()&& node.getRChild()==null || node.getRChild()!=null && node.getKey()<node.getRChild().getKey()
&& isSorted(node.getLChild()) && isSorted(node.getRChild()));
    }
}

四、性能分析

由于二叉查找树的插入和删除操作花费时间的主要部分是查找操作,所以二叉查找树的各操作性能有查找效率决定。
在一棵有n个节点的二叉差值奥数中查找一个节点,一次成功的查找刚好做过一条从根节点到该节点的路径,比较次数为该节点的高度level,1levelh,其中h是树的高度。所以二叉查找树查找成功的平均查找长度不大于该树的高度。而二叉查找树的高度和其形态有关,n个节点的文案二叉树高度最小,为logn+1,n个节点的单支二叉树高度最大,高度为n。所以二叉查找树的平均查找长度为O(logn)O(n)
二叉查找树的查找效率与二叉树的高度有关,高度越低,查处效率越高。因此,提高二叉查找树查找效率的办法是尽量降低二叉查找树的高度。

查找算法 之 二叉查找树

动态查找技术一般用树来存储查找集合,如二叉查找树。而二叉查找树的查找效率又与自身形态密切相关,需要对二叉查找树平衡化,保持其查找高效。本文就二叉查找树和平衡二叉树作简单介绍和分析以及对查找二叉树、AV...
  • whucyl
  • whucyl
  • 2013年12月04日 12:10
  • 9440

红黑二叉查找树

#include //红黑二叉查找树 //红黑二叉查找树是一种平衡二叉树,是基于2-3查找树的基础上演变的 //这里不对2-3查找树的算法进行描述,感兴趣的朋友可以自行了解一下 //2-3查找树...
  • liujianfeng1984
  • liujianfeng1984
  • 2015年08月09日 19:18
  • 1010

浅析数据结构与算法7--二叉查找树 及Java实现

Java实现代码: /** * * 此程序实现一个二叉查找树的功能,可以进行动态插入、删除关键字; * 查询给定关键字、最小关键字、最大关键字;转换为有序列表(用于排序) * ...
  • baidu_35561918
  • baidu_35561918
  • 2016年08月07日 16:03
  • 277

C#实现二叉查找树

using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.T...
  • qq_28221881
  • qq_28221881
  • 2016年11月25日 22:11
  • 533

LeetCode总结 -- 二叉查找树篇

这篇总结主要介绍一个比较常见的数据结构--二叉查找树。二叉查找树既是一颗树,又带有特别的有序性质,所以考察的方式比较多而且灵活,属于面试题目中的常客。LeetCode中关于二叉查找树的题目有以下几道:...
  • linhuanmars
  • linhuanmars
  • 2014年09月25日 09:37
  • 14009

二叉查找树-总结

定义: 二叉排序树或者是一棵空树,或者是具有下列性质的二叉树: (1)若左子树不空,则左子树上所有结点的值均小于它的根结点的值; (2)若右子树不空,则右子树上所有结点的值均大于或等...
  • aggble
  • aggble
  • 2015年07月10日 15:56
  • 533

查找算法——二叉查找树

概要        本章先对二叉树的相关理论知识进行介绍,然后给出C语言的详细实现。关于二叉树的学习,需要说明的是:它并不难,不仅不难,而且它非常简单。初次接触树的时候,我也觉得它似乎很难;而之所产...
  • ustcyy91
  • ustcyy91
  • 2017年08月29日 16:13
  • 132

Java数据结构和算法——二叉查找树

简介二叉查找树(Binary Search Tree), 又称为二叉搜索树。先学习下树的知识吧。树树的定义树是一种数据结构,它是由n(n>=1)个有限节点组成一个具有层次关系的集合。树具有以下特点: ...
  • jdsjlzx
  • jdsjlzx
  • 2016年07月02日 23:31
  • 2033

查找算法总结(顺序、折半、分块、二叉排序树)

一、基本概念: 1、  列表:待搜索的数据集合。 2、  关键字:要查找的那个数据。 3、  查找,检索:一种算法过程。给出一个key值(关键字),在含有若干个结点的序列中找出它。 4、  查找表:同...
  • daijin888888
  • daijin888888
  • 2016年11月11日 17:30
  • 718

7. 二叉排序树的搜索、插入、删除,时间复杂度

二叉排序树又称二叉查找树,它或是一棵空的er
  • xie294777315
  • xie294777315
  • 2014年08月08日 15:31
  • 7152
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:查找算法总结(3)--二叉查找树
举报原因:
原因补充:

(最多只允许输入30个字)