自平衡二叉树数据结构与TreeMap(TreeSet)排序

SortedMap是Map接口的子接口,而TreeMap是SortedMap接口的实现类。

TreeMap的底层是二叉树,key是无序、不可重复的。

TreeMap的特点是:元素会按照从小到大的顺序排序。

在看TreeMap前,先看看TreeSet,存储在TreeSet中的元素实际上是存储在TreeMap中的key里,而Map集合中起绝对性因素的就是key,所以只要学会了TreeSet,TreeMap也就自然而然的会了。

TreeSet

无序、不可重复;底层为二叉树,元素会排序。

例:

package com.dh.set;

import java.util.Iterator;
import java.util.TreeSet;

public class TreeSet01 {

    public static void main(String[] args) {

        TreeSet<Integer> treeSet = new TreeSet<>();
        treeSet.add(1);
        treeSet.add(3);
        treeSet.add(2);
        for (Integer integer : treeSet) {
            System.out.println(integer);
        }

        //字符串会按照字典顺序逐个字母进行比较
        TreeSet<String> treeSet1 = new TreeSet<>();
        treeSet1.add("hello");
        treeSet1.add("world");
        Iterator<String> iterator = treeSet1.iterator();
        while (iterator.hasNext()){
            System.out.println(iterator.next());
        }
    }
}

结果:

1
2
3
hello
world

那么二叉树是如何进行排序的呢?

自平衡二叉树数据结构

假设现在有一组数据:100,200,50,60,55,120

往集合中添加数据就是在不停的构造二叉树,最后遍历的时候采用中序遍历(左->根->右)。

100是根,左边的那一串是左子树,左子树中50是根,没有左子树,所以输出50,然后右子树中55是左,50是根,所以依次输出55,60,然后输出根100,然后右子树中,120是左,200是根,所以输出120,200

所以最后的顺序就是:50,55,60,100,120,200

自定义对象的排序

如果TreeSet中存放的是自己定义的对象,比如说Person,那TreeSet还可以排序吗?

我们可以试试看:

Person类:

package com.dh.set;

public class Person {
    
    private String name;
    private int age;

    public Person(String name, int age) {
        this.name = name;
        this.age = age;
    }
    
     @Override
    public String toString() {
        return "Person{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }
}

测试:

package com.dh.set;

import java.util.TreeSet;

public class TreeSet02 {

    public static void main(String[] args) {
        TreeSet<Person> people = new TreeSet<>();
        people.add(new Person("zhangsan",18));
        people.add(new Person("lisi",20));
        people.add(new Person("wangwu",19));
        for (Person person : people) {
            System.out.println(person);
        }
    }
}

结果:

Exception in thread "main" java.lang.ClassCastException: class com.dh.set.Person cannot be cast to class java.lang.Comparable (com.dh.set.Person is in unnamed module of loader 'app'; java.lang.Comparable is in module java.base of loader 'bootstrap')
	at java.base/java.util.TreeMap.compare(TreeMap.java:1291)
	at java.base/java.util.TreeMap.put(TreeMap.java:536)
	at java.base/java.util.TreeSet.add(TreeSet.java:255)
	at com.dh.set.TreeSet02.main(TreeSet02.java:9)

结果出现了异常,其实也很容易明白,因为TreeSet不知道怎么比,怎么才是大,怎么才是小,所以就需要我们告诉TreeSet如何去比较Person对象。

异常中出现了java.lang.Comparable,查api文档可以知道这是一个接口,说Person无法转变成java.lang.Comparable,如果Person类实现了java.lang.Comparable接口的话,那是不是就可以转换了呢?

没错!解决方法就是就是实现java.lang.Comparable接口。

实现自定义对象比较规则方法一:实现java.lang.Comparable接口

Person类实现Comparable接口,在compareTo(Object o)中重写比较规则。

package com.dh.set;

public class Person implements Comparable<Person> {

    public String name;
    public int age;

    public Person(String name, int age) {
        this.name = name;
        this.age = age;
    }

    @Override
    public String toString() {
        return "Person{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }

    @Override
    public int compareTo(Person o) {
        //升序
        return this.age - o.age;
        //降序
//        return o.age - this.age;
    }
}

实现自定义对象比较规则方法二:实现java.util.Comparator接口

Person:

package com.dh.set;

import java.util.Comparator;

public class Person {

    public String name;
    public int age;

    public Person(String name, int age) {
        this.name = name;
        this.age = age;
    }

    @Override
    public String toString() {
        return "Person{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }
}

先书写一个比较器,书写一个类实现Comparator接口,并重写compare(Object o1,Object o2),书写比较规则:

package com.dh.set;

import java.util.Comparator;

public class ComparatorRule implements Comparator<Person> {
    @Override
    public int compare(Person o1, Person o2) {
        //升序
        return o1.age - o2.age;
        //降序
        //return o2.age - o1.age;
    }
}

TreeSet测试类:

package com.dh.set;

import java.util.Comparator;
import java.util.TreeSet;

public class TreeSet02 {

    public static void main(String[] args) {
        //在实例化TreeSet集合时,将自定义比较器作为参数传进去
        TreeSet<Person> people = new TreeSet<>(new ComparatorRule());
        people.add(new Person("zhangsan",18));
        people.add(new Person("lisi",20));
        people.add(new Person("wangwu",19));
        for (Person person : people) {
            System.out.println(person);
        }
    }
}

结果:

Person{name='zhangsan', age=18}
Person{name='wangwu', age=19}
Person{name='lisi', age=20}

那么是采用哪种方式更好呢?

采用实现Comparator接口的方式会更加的灵活,因为可以定义多个比较器,需要哪个就传递哪个。

posted @ 2021-02-23 20:09  deng-hui  阅读(257)  评论(0编辑  收藏  举报