zoukankan      html  css  js  c++  java
  • Java中的TreeSet

    TreeSet特点:

    保证元素唯一
    可以给元素进行排序
    没有索引,不能用普通for循环,
    查找效率高
    结构为二叉排序树(也叫二叉查找树)

    自然排序Comparable:

    自然排序要求类实现了自然排序接口



    这里出现异常的原因是我们的Aniki并没有实现Comparable接口,TreeSet并不知道如何排序所以出问题。


    我们给Aniki类添加Comparable接口,必须重写compareTo方法,当return 0的时候:

    输出只有:
    这是因为第一个元素添加的时候是不需要跟别人比较的,第二个元素的时候是需要跟别人比较的,会按照我们给的排序规则比较,
    而我们给的规则是compareTo方法给的是0,当返回值是0的时候,会认为第二个元素与第一个元素是同一个元素。所以第二个元素添加失败,
    同理第三个也失败。
    当compareTo方法return 1(正数)的时候,会按照输入的顺序输出,也就是认为我们输入的顺序是升序。
    当compareTo方法return -1(负数)的时候,会按照与输入相反的顺序输出,也就是降序。

    compareTo的自定义重写

    我们要想要按照Age排列,要这样写


    是为什么!?

    原因:

    分析:

    注意:以下文字中会出现二叉树,左儿子,右儿子,前序遍历等词汇,请各位同学在《数据结构》中查找含义。

    以上图为例,我们的"99"是第三个要传入的元素,

    "99"进入add(),被调入put,然后我们的"99"赋值给K类型的key,

    然后我们看到了建立root的过程,root就是二叉树的根,指向树根的指针是t(熟悉的C语言表示形式)。

    然后是判断t是否为空,也就是判断根位置有没有元素,如果是空的就把当前的key作为根,

    t以后会作为当前位置指针在循环里继续重复这种判断。

    可想而知第一个add进来的"100",在这里被作为了根root,(这一步非常关键,

    "100"也是之后每次新元素添加的时候,调用下面compareTo(参数)中do——while循环的第一轮的参数,

    第一轮循环回让新元素判为处于根节点左子树或者右子树中,第二轮循环会让根节点左儿子或者右儿子

    为根,再以新的根持续判断,但是唯一不变的是每个元素第一次进来都会先与整个二叉树的根root作比较)

    与我们"99"无关,我们是第三个进来的元素,

    然后看到了一个conparator构造器的判断语句,毫无疑问我们并没有new一个构造器,我们使用了无参的构造方法,

    因此我们的构造器cpr是null,不进入这个if语句。

    来到了else部分,判断了我们存储"99"的key是否是null,是的话抛出空指针异常,我们的key不空。

    之后是一个类型转换,我们的key元素是K类型,K类型继承不继承Comparable不知道,但是我们add(99)中使用的类型

    毫无疑问是implements Comparable的,否则我们会出现之前的异常,原因是:Aniki并没有实现Comparable接口

    因此我们可以强制类型转换为Comparable的k,这里是多态的向上转型。

    然后我们就进入了构建二叉树的do-while循环(循环内的含义参考《数据结构》)这颗二叉树的元素从左往右依次增大。

    我们先与根节点比较,再次提一下这个根节点是我们第一个add的"100",当前的t是指向root根的指针,t.key就是100,

    这里二叉树构建的依据是,compareTo(100)方法的返回值int类型的cmp,而我们就可以在这里做手脚。

    k是我们转换好的key,"99",是我们的"99"调用的compareTo(100),因此this指的是新传入值为"99"的变量。

    在我们的哲♂学例子,更改compareTo(参数)方法,来概变二叉树的真实构建情况,
    Age从小到大如图:

    我们比较Aniki年龄来排序的时候,

    “k”是我们的第二个元素Bili的Age——"50",而根root,也就是“t.key”,是我们第一个元素Van的Age——"51"。

    “k”调用compareTo(51),那么“k”就是this,this.Age就是Bili的Age——"50"

    aniki.Age就是Van的Age——"51"

    return this.Age - aniki.Age;这句话的意思就成了

    return Bili.Age - Van.Age;结果是-1,负数,Bili进入根Van的左儿子,前序遍历的结果是年龄从小到大。

    因此如果我们想从大到小遍历,那么我们写:

    return aniki.Age - this.Age;结果是:+1,Bili被放到Van的右儿子,前序遍历的结果是从大到小。

    Age从大到小:

    Comparator比较器排序:

    我们在新建TreeSet的时候,可以不执着于使用自然排序(实现Comparable接口然后重写compareTo方法),我们可以使用
    Comparator比较器来完成排序(此时就不会使用自然排序)。

    Comparator比较器使用方法:


    我们新建一个TreeSet并且这样new一个Comparator比较器,然后使用匿名内部类重写compare方法,


    简单的修改参数含义,第一个参数是新元素,第二个元素就是我们之前提过的(t指针)元素,它在do——while第一轮中是根节点。
    一开始默认return 0,与compareTo(){return 0;}的时候是一样的。

    我们来讲解下原理:


    根据上图的例子来讲,
    我们new出一个Comparator之后,这个Comparator会被传入final最终态的comparator变量中,之后我们通过
    这个comparator变量来使用比较器。

    我们以("王五",15)这个元素来看整个过程,("王五",15)被add方法调用的put方法中,值传递给了K类型的key。

    然后与之前一样,第一个add进TreeSet来的元素("张三",13)被当作根节点。只不过这次我们判断有没有comparator时,

    我们具备了comparator。于是进入do——while循环,这里构造器调用compare(key,t.key),

    第一个位置是当前传入的元素,第二个位置是指针指向的节点,与之前的compareTo(参数)方法大同小异。

    只不过compareTo是k(值为key的变量)调用的,k.compareTo(t.key)方法中,

    this.key就是compare(key,t.key)中的key,t.key的话两个方法里指的都是当前被比较的节点。

    因此我们:


    含义相同,结果都是:

  • 相关阅读:
    JavaScript 为字符串添加样式 【每日一段代码80】
    JavaScript replace()方法 【每日一段代码83】
    JavaScript for in 遍历数组 【每日一段代码89】
    JavaScript 创建用于对象的模板【每日一段代码78】
    html5 css3 新元素简单页面布局
    JavaScript Array() 数组 【每日一段代码88】
    JavaScript toUTCString() 方法 【每日一段代码86】
    位运算
    POJ 3259 Wormholes
    POJ 3169 Layout
  • 原文地址:https://www.cnblogs.com/maomaodesu/p/11880193.html
Copyright © 2011-2022 走看看