Set一大本领:去重
Set集合一大特点:存取顺序不一致
存储字符串并遍历
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.print(s+" ");//"aaa bbb ccc " 重复的aaa只留下了一个 天然去重
}
System.out.println("\n------------");
//Collection层面就实现了Iterable接口 所以单列集合都可以使用增强for
for (String s : set) {
System.out.print(s+" ");//"aaa bbb ccc " 存取的顺序也变了
}
}
存储Integer类型的整数并遍历
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);//"[1, 2, 3, 4, 5]" 默认自然序排好了
}
案例需求
实现步骤
代码实现
学生类
此例不指定比较规则(实现Comparable接口,重写compareTo方法)直接add,运行时会报错: ClassCastException
代码:
//实现接口
public class Student implements Comparable<Student>{//泛型也要写Student 臃肿了
private String name;
private Integer age;
//此处空参构造
//此处全参构造
//此处所有get/set
//此处toString
//实现接口方法
@Override
public int compareTo(Student o) {
//根据年龄升序排序
int result = this.age - o.age;
return result;
}
//不实现接口,重写方法,add时就会报错 TreeSet就是BST的一种,插入即开始排序,必须有比较依据
}
public static void main(String[] args) {
TreeSet<Student> tss=new TreeSet<>();
Student s1 = new Student("小米", 18);
Student s2 = new Student("小米蜜", 17);
Student s3 = new Student("小米米", 19);
Student s4 = new Student("小黑", 18);//排序规则下年龄相同返回0,即认为重复,不存入(得增强规则才行)
tss.add(s1);tss.add(s2);tss.add(s3);tss.add(s4);
System.out.println(tss);//年龄升序排序
}
案例需求
实现步骤
代码实现
学生类
public class Student implements Comparable<Student>{//泛型也要写Student 臃肿了
private String name;
private Integer age;
//此处空参构造
//此处全参构造
//此处所有get/set
//此处toString
@Override
public int compareTo(Student o) {
//首要条件年龄:根据年龄升序排序
int result = this.age - o.age;
//次要判断条件姓名:年龄相同时根据姓名字母序排序
if(result==0) result=this.name.compareTo(o.getName());//遇到不熟悉api,查chm文档啊
return result;
}
测试类
public static void main(String[] args) {
TreeSet<Student> tss=new TreeSet<>();
Student s1 = new Student("zhangsan", 18);
Student s2 = new Student("lisi", 17);
Student s3 = new Student("wangwu", 19);
Student s4 = new Student("zhaoliu", 18);
Student s5 = new Student("qianqi", 20);
Student s6 = new Student("zhaoliu", 18);//与s4重复不存
tss.add(s1);tss.add(s2);tss.add(s3);tss.add(s4);tss.add(s5);tss.add(s6);
for (Student student : tss) {
System.out.println(student);
}
}
案例需求
实现步骤
代码实现
老师类
public class Teacher {
private String name;
private Integer age;
//此处空参构造
//此处全参构造
//此处所有get/set
//此处toString
}
测试类1:匿名内部类
public static void main(String[] args) {
//说明TreeSet有对应的构造器
TreeSet<Teacher> ts = new TreeSet<>(new Comparator<Teacher>() {//new接口 也就是匿名内部类实现一下
@Override
public int compare(Teacher o1, Teacher o2) {
int result = o1.getAge()-o2.getAge();
if(result==0) result = o1.getName().compareTo(o2.getName());
return result;
}
});
Teacher t1 = new Teacher("zhangsan",23);
Teacher t2 = new Teacher("lisi",22);
Teacher t3 = new Teacher("wangwu",24);
Teacher t4 = new Teacher("zhaoliu",24);
Teacher t5 = new Teacher("wangwu",24);//重复的不存
ts.add(t1); ts.add(t2); ts.add(t3); ts.add(t4); ts.add(t5);
for (Teacher t : ts) {
System.out.println(t);
}
}
测试类2:lambda表达式
public static void main(String[] args) {
//说明TreeSet有对应的构造器
TreeSet<Teacher> ts = new TreeSet<>((Teacher o1,Teacher o2)->{
int result = o1.getAge()-o2.getAge();
if(result==0) result=o1.getName().compareTo(o2.getName());
return result;
});
Teacher t1 = new Teacher("zhangsan",23);
Teacher t2 = new Teacher("lisi",22);
Teacher t3 = new Teacher("wangwu",24);
Teacher t4 = new Teacher("zhaoliu",24);
Teacher t5 = new Teacher("wangwu",24);//重复的不存
ts.add(t1); ts.add(t2); ts.add(t3); ts.add(t4); ts.add(t5);
for (Teacher t : ts) {
System.out.println(t);
}
}
运行结果同上
TreeSet比较器排序: “c” , “ab” , “df” , “qwer”
无法改String源码的compareTo()方法,只能用TreeSet<>(Comparator比较器)
public static void main(String[] args) {
TreeSet<String> ts = new TreeSet<>((String s1,String s2)->{
if(s1.length()!=s2.length()) return s1.length()-s2.length();
return s1.compareTo(s2);
});
ts.add("c"); ts.add("qwer"); ts.add("df"); ts.add("ab");
for (String t : ts) {
System.out.print(t+" ");// "c ab df qwer "
}
}
树的基础:参考 树专题
二叉查找树的特点
二叉查找树和二叉树对比结构图
二叉查找树添加节点规则
参考: 算法笔记9.5 平衡二叉树(AVL树)
平衡二叉树的特点
平衡二叉树旋转
旋转触发时机
左旋
右旋
平衡二叉树旋转的四种情况
左左
左左: 当根节点左子树的左子树有节点插入,导致二叉树不平衡
如何旋转: 直接对整体进行右旋即可
左右
左右: 当根节点左子树的右子树有节点插入,导致二叉树不平衡
如何旋转: 先在左子树对应的节点位置进行左旋,在对整体进行右旋
右右
右右: 当根节点右子树的右子树有节点插入,导致二叉树不平衡
如何旋转: 直接对整体进行左旋即可
右左
右左:当根节点右子树的左子树有节点插入,导致二叉树不平衡
如何旋转: 先在右子树对应的节点位置进行右旋,在对整体进行左旋
每个结点有红或者黑两种颜色
avl的改进,不必每次失衡都要左右旋调整,偶尔调整,(不严格平衡) 相对也是平衡的,效率还进一步提高了(减少调整次数)
红黑树的特点
红黑树的红黑规则有哪些
每一个节点或是红色的,或者是黑色的
根节点必须是黑色
如果一个节点没有子节点或者父节点,则该节点相应的指针属性值为Nil,这些Nil视为叶节点,每个叶节点(Nil)是黑色的
如果某一个节点是红色,那么它的子节点必须是黑色(不能出现两个红色节点相连 的情况)
对每一个节点,从该节点到其所有后代叶节点的简单路径上,均包含相同数目的黑色节点
根叶黑
不红红
黑路同
(左根右)
红黑树添加节点的默认颜色
红黑树添加节点后如何保持红黑规则
TreeSet底层就是红黑树
案例需求
代码实现
学生类
public class Student implements Comparable<Student>{
private String name;
private Integer chinese;//语文成绩
private Integer math;//数学成绩
private Integer english;//英语成绩
//空参构造
//全参构造
//所有get/set
public Integer getSum(){
return chinese+math+english;
}
@Override
public String toString() {
return "Student{" +
"name='" + name + '\'' +
", chinese=" + chinese +
", math=" + math +
", english=" + english +
", 总分=" + getSum() +
'}';
}
@Override
public int compareTo(Student o) {
int result=this.getSum()-o.getSum();
//总分一样依次比较数学,英语
if(result==0) result=this.getMath()-o.getMath();
if(result==0) result=this.getEnglish()-o.getEnglish();
//分数完全一样 姓名序排序
if(result==0) result=this.getName().compareTo(o.getName());
return -result;//result是升序 -result是降序
}
}
测试类:
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);
//根据提供的比较规则,按照红黑树的性质依次插入到RBT中
//遍历时底层自动帮你中序遍历
for (Student t : ts) {
System.out.println(t);//ClassCastException 不添加比较条件 直接add后输出 有问题报错
}
}
存储int并遍历
public static void main(String[] args) {
HashSet<Integer> hs=new HashSet<>();
hs.add(10); hs.add(20); hs.add(15); hs.add(5); hs.add(8); hs.add(20);
Iterator<Integer> it = hs.iterator();
while (it.hasNext()){
Integer next = it.next();
System.out.print(next+" ");//20 5 8 10 15 无序(只去重)
}
System.out.println("\n===============================");
for (Integer h : hs) {
System.out.print(h+" ");//20 5 8 10 15 无序(只去重)
}
}
哈希值简介
是JDK根据对象的地址或者字符串或者数字算出来的int类型的数值
如何获取哈希值
Object类中的public int hashCode():返回对象的哈希码值
哈希值的特点
hashcode就是key,根据key直接计算出地址值
冲突(两个元素计算到同一个地址值),equals出马看是否是同一个元素(重复丢弃,不同则探测法或者拉链法)
JDK1.8以前
数组 + 链表 ★
JDK1.8以后
拉链节点个数少于等于8个
数组 + 链表
拉链节点个数多于8个
数组 + 红黑树
(拉链过长,每次比较遍历很长的一个链表,时间代价过大,这时将拉链变成红黑树,存储结构没变,时间复杂度却降下来了)
案例需求
代码实现
学生类
public class Student {
private String name;
private Integer age;
//空参构造
//全参构造
//所有get/set
//tostring
//HashSet存储自定义对象 必须重写equals、hashCode
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Student student = (Student) o;
return Objects.equals(name, student.name) &&
Objects.equals(age, student.age);
}
@Override
public int hashCode() {
return Objects.hash(name, age);
}
}
查找:
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 h : hs) {
System.out.println(h);//去重 但 无序
}
}
总结
HashSet集合存储自定义类型元素,要想实现元素的唯一,要求必须重写hashCode方法和equals方法
遇到不熟悉api,查chm文档啊
if(result==0) result=this.name.compareTo(o.getName());//遇到不熟悉api,查chm文档啊
TreeSet:
1、首先是Set 元素不重复
2、其实底层是红黑树(前提也是BST),天然有序(中序遍历),查找效率O(2log2n)
红黑树插入时既可以有序,有可以去重(相等不插入就是了)
HashSet:
1、首先是Set 元素不重复
2、底层是Hash表,equals方法,Hash[n]表记录是否出现过,来去重了下。
3、元素打乱,无序(不会自动给你排序,没有那个机制)
Hash表,只能去重,无法排序,因此只能排序