第八章_集合(2)_set集合&二叉树&红黑树

一、Set集合

  • Set集合概述和特点
    • 不可以存储重复元素,可以去除重复
    • 存取顺序不一致
    • 没有带索引的方法,不能使用普通for循环遍历,也不能通过索引来获取/删除set集合的元素
import java.util.Iterator;
import java.util.Set;
import java.util.TreeSet;

public class MySet1 {
    public static void main(String[] args) {
        Set<String> set = new TreeSet<>();
        set.add("ccc");
        set.add("aaa");
        set.add("aaa");
        set.add("bbb");
//        for (int i = 0; i < set.size(); i++) {
//            //Set集合是没有索引的,所以不能使用通过索引获取元素的方法
//        }
        Iterator<String> it = set.iterator();
        while (it.hasNext()){
            String s = it.next();
            System.out.println(s);
        }
        System.out.println("-----------------------------------");
        for (String s : set) {
            System.out.println(s);
        }
    }
}

二、TreeSet

  • TreeSet集合概述和特点:底层实现是红黑树
    • 不可以存储重复元素
    • 没有索引
    • 可以将元素按照规则进行排序
      • TreeSet():根据其元素的自然排序进行排序
      • TreeSet(Comparator comparator) :根据指定的比较器进行排序

如果想要使用TreeSet,需要制定排序规则

  • 案例:TreeSet集合来存储Integer类型
import java.util.TreeSet;

public class MyTreeSet1 {
    public static void main(String[] args) {
        TreeSet<Integer> ts = new TreeSet<>();
        ts.add(5);
        ts.add(3);
        ts.add(4);
        ts.add(1);
        ts.add(2);
        System.out.println(ts);
    }
}
  • 自然排序Comparable的使用
    • a)使用空参构造创建TreeSet集合:用TreeSet集合存储自定义对象,无参构造方法使用的是自然排序对元素进行排序的
    • b)自定义的Student类实现Comparable接口:自然排序,就是让元素所属的类实现Comparable接口,重写compareTo(T o)方法
    • c)重写接口中的compareTo方法:重写方法时,一定要注意排序规则必须按照要求的主要条件和次要条件来写
Student
public class Student implements Comparable<Student>{
    private String name;
    private int age;

    public Student() {
    }

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

	//GET...SET...

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

    @Override
    public int compareTo(Student o) {
        //按照对象的年龄进行排序
        //主要判断条件
        int result = this.age - o.age;
        //次要判断条件
        result = result == 0 ? this.name.compareTo(o.getName()) : result;
        return result;
    }
}
测试类
import java.util.TreeSet;

/**
 * TreeSet集合来存储Student类型
 */
public class MyTreeSet2 {
    public static void main(String[] args) {
        TreeSet<Student> ts = new TreeSet<>();
        Student s1 = new Student("zhangsan",28);
        Student s2 = new Student("lisi",27);
        Student s3 = new Student("wangwu",29);
        Student s4 = new Student("zhaoliu",28);
        Student s5 = new Student("qianqi",30);
        ts.add(s1);
        ts.add(s2);
        ts.add(s3);
        ts.add(s4);
        ts.add(s5);
        System.out.println(ts);
    }
}
  • 比较器排序Comparator
    • 用TreeSet集合存储自定义对象,带参构造方法使用的是比较器排序对元素进行排序的
    • 比较器排序,就是让集合构造方法接收Comparator的实现类对象,重写compare(T o1,T o2)方法
    • 重写方法时,一定要注意排序规则必须按照要求的主要条件和次要条件来写
public class Teacher {
    private String name;
    private int age;

    public Teacher() {
    }

    public Teacher(String name, int age) {
        this.name = name;
        this.age = age;
    }
	//GTT...SET...
    @Override
    public String toString() {
        return "Teacher{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }
}
  • 自然排序和比较器排序的总结:在使用的时候,默认使用自然排序,当自然排序不满足现在的需求时,必须使用比较器排序
    • 自然排序:自定义类实现Comparable接口,重写compareTo方法,根据返回值进行排序
    • 比较器排序:创建TreeSet对象的时候传递Comparator的实现类对象,重写compare方法,根据返回值进行排序
  • 两种方式中关于返回值的规则
    • 如果返回值为负数,表示当前存入的元素是较小值,存左边
    • 如果返回值为0,表示当前存入的元素跟集合中元素重复了,不存
    • 如果返回值为正数,表示当前存入的元素是较大值,存右边
  • String类排序规则是Java已经写好的,需要修改就只能使用比较器排序修改
import java.util.Comparator;
import java.util.TreeSet;

public class MyTreeSet5 {
    public static void main(String[] args) {
//        TreeSet<String> ts = new TreeSet<>(new Comparator<String>() {
//            @Override
//            public int compare(String o1, String o2) {
//                int result = o1.length() - o2.length();
//                result = result == 0 ? o1.compareTo(o2) : result;
//                return result;
//            }
//        });
        TreeSet<String> ts = new TreeSet<>(
                (String o1, String o2) -> {
                    int result = o1.length() - o2.length();
                    result = result == 0 ? o1.compareTo(o2) : result;
                    return result;
                }
        );
        ts.add("c");
        ts.add("ab");
        ts.add("df");
        ts.add("qwer");
        System.out.println(ts);
    }
}

三、二叉树

1、二叉树

TreeSet底层实现是红黑树

  • 二叉树的特点:二叉树中,任意一个节点的度要小于等于2
    • 节点:在树结构中,每一个元素称之为节点
    • 度:每一个节点的子节点数量称之为度

在这里插入图片描述
在这里插入图片描述

  • 二叉查找树的特点:二叉查找树,又称二叉排序树或者二叉搜索树
    • 每一个节点上最多有两个子节点
    • 左子树上所有节点的值都小于根节点的值
    • 右子树上所有节点的值都大于根节点的值

在这里插入图片描述

  • 二叉查找树添加节点规则:从根节点开始比较
    • 小的存左边
    • 大的存右边
    • 一样的不存

在这里插入图片描述

2、平衡二叉树

  • 平衡二叉树的特点
    • 二叉树左右两个子树的高度差不超过1(这样查询效率才是最高的)
    • 任意节点的左右两个子树都是一颗平衡二叉树
  • 平衡二叉树旋转触发时机:当添加一个节点之后,该树不再是一颗平衡二叉树
  • 平衡二叉树左旋:就是将根节点的右侧往左拉,原先的右子节点变成新的父节点,并把多余的左子节点出让,给已经降级的根节点当右子节点

在这里插入图片描述
在这里插入图片描述

  • 平衡二叉树右旋:就是将根节点的左侧往右拉,左子节点变成了新的父节点,并把多余的右子节点出让,给已经降级根节点当左子节点

在这里插入图片描述
在这里插入图片描述

3、平衡二叉树旋转的四种情况

  • 左左: 当根节点左子树的左子树有节点插入,导致二叉树不平衡:直接对整体进行右旋即可

在这里插入图片描述

  • 左右: 当根节点左子树的右子树有节点插入,导致二叉树不平衡:先在左子树对应的节点位置进行左旋,在对整体进行右旋

在这里插入图片描述

  • 右右: 当根节点右子树的右子树有节点插入,导致二叉树不平衡:直接对整体进行左旋即可

在这里插入图片描述

  • 右左:当根节点右子树的左子树有节点插入,导致二叉树不平衡:先在右子树对应的节点位置进行右旋,在对整体进行左旋

在这里插入图片描述

四、红黑树

  • 红黑树概念:红黑树是一种自平衡的二叉查找树,是计算机科学中用到的一种数据结构;1972年出现,当时被称之为平衡二叉B树;后来1978年被修改为如今的“红黑树”
    • 红黑树是一种特殊的二叉查找树,每一个节点或是红色的,或者是黑色的,根节点必须是黑色
    • 红黑树不是高度平衡的,它的平衡是通过"自己的红黑规则"进行实现的
  • 红黑树的红黑规则
    • a)每一个节点或是红色的,或者是黑色的
    • b)根节点必须是黑色
    • c)如果一个节点没有子节点或者父节点,则该节点相应的指针属性值为Nil,这些Nil视为叶节点,每个叶节点(Nil)是黑色的
    • d)如果某一个节点是红色,那么它的子节点必须是黑色(不能出现两个红色节点相连 的情况)
    • e)对每一个节点,从该节点到其所有后代叶节点的简单路径上,均包含相同数目的黑色节点

在这里插入图片描述

  • 红黑树添加节点的默认颜色:添加节点时,默认为红色,效率高

在这里插入图片描述

  • 红黑树添加节点后如何保持红黑规则

在这里插入图片描述

  • 案例:成绩排序案例
///学生类
public class Student implements Comparable<Student>{
    private String name;
    private int chinese;
    private int math;
    private int english;

    public Student() {
    }

    public Student(String name, int chinese, int math, int english) {
        this.name = name;
        this.chinese = chinese;
        this.math = math;
        this.english = english;
    }
	//GET...SET...
    @Override
    public String toString() {
        return "Student{" +
                "name='" + name + '\'' +
                ", chinese=" + chinese +
                ", math=" + math +
                ", english=" + english +
                '}' + "总分为" + getSum();
    }

    public int getSum(){
        return chinese + math + english;
    }

    @Override
    public int compareTo(Student o) {
        //按照总分进行排序
        //int result = this.getChinese() + this.getMath() + this.getEnglish()
        int result = o.getSum() - this.getSum();
        //次要条件
        //总分一样,就比较语文成绩
        result = result == 0 ? o.getChinese() - this.getChinese() : result;
        //语文成绩一样,就比较数学成绩
        result = result == 0 ? o.getMath() - this.getMath() : result;
        //如果数学成绩一样,就比较英文成绩
        result = result == 0 ? o.getEnglish() - this.getEnglish() : result;
        //如果成绩都一样,则按照姓名进行排序
        result = result == 0 ? o.getName().compareTo(this.getName()) : result;
        return result;
    }
}
//测试类
import java.util.TreeSet;

public class TreeSetTest {
    public static void main(String[] args) {
        TreeSet<Student>  ts = new TreeSet<>();
        Student s1 = new Student("dahei",80,80,80);
        Student s2 = new Student("erhei",90,90,90);
        Student s3 = new Student("xiaohei",100,100,100);
        ts.add(s1);
        ts.add(s2);
        ts.add(s3);
        for (Student student : ts) {
            System.out.println(student);
        }
    }
}

五、HashSet

  • HashSet集合概述和特点
    • 底层数据结构是哈希表
    • 不能保证存储和取出的顺序完全一致
    • 没有带索引的方法,所以不能使用普通for遍历
    • 由于是set集合,所以元素唯一
  • HashSet集合的基本应用
import java.util.HashSet;
import java.util.Iterator;

public class HashSetDemo1 {
    public static void main(String[] args) {
        HashSet<String> hs = new HashSet<>();
        hs.add("hello");
        hs.add("world");
        hs.add("java");
        hs.add("java");
        hs.add("java");
        Iterator<String> it = hs.iterator();
        while(it.hasNext()){
            String s = it.next();
            System.out.println(s);
        }
        System.out.println("=============================");
        for (String s : hs) {
            System.out.println(s);
        }
    }
}

1、哈希值

  • 哈希值简介:是JDK根据对象的地址或者字符串或者数字算出来的int类型的数值
  • 如何获取哈希值:Object类中的public int hashCode():返回对象的哈希码值
  • 哈希值的特点
    • 同一个对象多次调用hashCode()方法返回的哈希值是相同的
    • 默认情况下,不同对象的哈希值是不同的。而重写hashCode()方法,可以实现让不同对象的哈希值相同
      • 如果没有重写hashCode方法,那么是根据对象的地址值计算出的哈希值
      • 如果重写了hashCode方法,一般都是通过对象的属性值计算出哈希值
/Student类
public class Student {
    private String name;
    private int age;

    public Student() {
    }

    public Student(String name, int age) {
        this.name = name;
        this.age = age;
    }
	
	//GET...SET...
	
    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;

        Student student = (Student) o;

        if (age != student.age) return false;
        return name != null ? name.equals(student.name) : student.name == null;
    }

    //我们可以对Object类中的hashCode方法进行重写
    //在重写之后,就一般是根据对象的属性值来计算哈希值的。
    //此时跟对象的地址值就没有任何关系了。
    @Override
    public int hashCode() {
        int result = name != null ? name.hashCode() : 0;
        result = 31 * result + age;
        return result;
    }

    @Override
    public String toString() {
        return "Student{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }
}
/测试类
public class HashSetDemo2 {
    public static void main(String[] args) {
        Student s1 = new Student("xiaozhi",23);
        Student s2 = new Student("xiaomei",22);
        //因为在Object类中,是根据对象的地址值计算出来的哈希值。
        System.out.println(s1.hashCode());//1060830840
        System.out.println(s1.hashCode());//1060830840
        System.out.println(s2.hashCode());//2137211482
    }
}

2、哈希表

  • JDK8以前不包括8:数组+链表
  • JDK8以后包括8
    • 节点个数少于等于8个:数组+链表
    • 节点个数多于8个:数组+红黑树

在这里插入图片描述

在这里插入图片描述

  • HashSet1.8版本的存储流程

在这里插入图片描述

  • 案例:HashSet集合存储学生对象并遍历
    • 案例需求:创建一个存储学生对象的集合,存储多个学生对象,使用程序实现在控制台遍历该集合;要求:学生对象的成员变量值相同,我们就认为是同一个对象
Student类
public class Student {
    private String name;
    private int age;

    public Student() {
    }

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

	//GET...SET...

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;

        Student student = (Student) o;

        if (age != student.age) return false;
        return name != null ? name.equals(student.name) : student.name == null;
    }

    @Override
    public int hashCode() {
        int result = name != null ? name.hashCode() : 0;
        result = 31 * result + age;
        return result;
    }

    @Override
    public String toString() {
        return "Student{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }
}
测试类
import java.util.HashSet;

public class HashSetTest1 {
    public static void main(String[] args) {
        HashSet<Student> hs = new HashSet<>();
        Student s1 = new Student("xiaohei",23);
        Student s2 = new Student("xiaohei",23);
        Student s3 = new Student("xiaomei",22);
        hs.add(s1);
        hs.add(s2);
        hs.add(s3);
        for (Student student : hs) {
            System.out.println(student);
        }
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

无休止符

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值