Java进阶07集合(续)

Java进阶07 集合(续)

一、数据结构(树)

1、关于树

1.1 相关概念
  • 节点:树中每个单独的分支

  • 节点的度:每个节点的子节点数量

  • 树高:树的总层数

  • 根节点:最顶层节点

  • 左子节点:左下方的节点

  • 右子节点:右下方的节点

  • 左子树:节点左下方连接的全部节点

  • 右子树:节点右下方连接的全部节点

1.2 二叉树&二叉查找树

  • 普通二叉树

    树中任意节点的度都<=2

  • 二叉查找树

    又称二叉排序树、二叉搜索树。其特点是:①首先必须是二叉树;②任意节点左子树上的值均小于当前节点值;③任意节点右子树上的值都大于当前节点值;

    二叉查找树的节点如果都挂在同一边,会产生瘸腿现象,那么它的查询效率就和普通的单链表一样了,为了解决这个问题,引入平衡二叉树

1.3 平衡二叉树

在是二叉查找树的前提下,任意节点的左右子树高度差不超过1,即为平衡二叉树。由于平衡二叉树的这个特点,每次往该树中加入新的节点时,就可能会对其平衡性有影响。当添加了某个节点后,改变了该树的平衡性,则会触发旋转机制来保证平衡性

  • 旋转规则

    确定支点:从添加的结点开始,不断往父节点找不平衡的节点,这个点就是支点

    • 左旋(右右加入造成不平衡)

      case1.支点没有左子节点

      把支点左旋降级,变成左子节点;晋升原来的右子节点

      case2.支点有左子节点

      将根节点的右侧往左拉;原先的右子节点变成新的父节点,并把多余的左子节点让出,给已经降级的根节点当右子节点

    • 右旋(左左加入造成不平衡)

      case1.支点没有右子节点

      把支点右旋降级,变成右子节点;晋升原来的左子节点

      case2.支点有右子节点

      将根节点的左侧往右拉;原先的左子节点变成新的父节点,并把多余的右子节点让出,给已经降级的根节点当左子节点

  • 需要旋转的四种情况

    左左意思为根节点的左子树的左子树加入导致的不平衡,其余三种情况类似

    • 左左:一次右旋

    • 左右:先局部左旋,再整体右旋

    • 右右:一次左旋

    • 右左:先局部右旋,再整体左旋

2、红黑树

2.1 红黑树的概念

红黑树是一种自平衡的二叉查找树,是一种特殊的二叉查找树,红黑树的每一个节点上都有存储位表示节点的颜色,节点颜色非黑即红。红黑树不是高度平衡的,它的平衡是通过”红黑规则“实现的。

2.2 红黑规则

①每一个节点是红色或黑色

根节点必须是黑色

③如果一个节点没有子节点或父节点,则该节点相应的指针属性值为Nil,这些Nil视为叶节点,每个叶节点(Nil)是黑色的

④如果某一个节点是红色,那么它的子节点必须是黑色(不能出现两个相邻的红节点

⑤对每一个节点,从该节点到其所有后代叶节点的简单路径上,均包含相同数目的黑色节点

助记口诀:左根右、根叶黑、不红红、黑路同
  • 左根右:左子树节点值<=根节点值<=右子树节点值

  • 根叶黑:根节点和叶节点(外部节点Nil)均是黑色的

  • 不红红:不能出现两个相邻的红节点

  • 黑路同:从某个节点出发,一条路走到黑(即走到它的后代叶节点上)经过的黑色节点数都相同

2.3 添加节点规则

3、动画演示网站

Data Structure Visualization (usfca.edu)

二、TreeSet集合

1、作用

对集合中的元素进行排序操作(底层红黑树实现);且属于Set派系,不允许存储重复数据,可以去重

1.1 综合案例

需求:键盘录入一个字符串,对字符串中的字符去重,并排序

public class TreeSetTest1 {
    public static void main(String[] args) {
        System.out.println("请输入:");
        String content = new Scanner(System.in).nextLine();
​
        //1、将字符串拆分为字符数组,方便获取每一个字符
        char[] chars = content.toCharArray();
        //2、准备TreeSet集合用于排序和去重
        TreeSet<Character> set = new TreeSet<>();
​
        //3、遍历字符数组,并将每一个字符添加到集合
        for (char c : chars) {
            set.add(c);
        }
​
        //4、多次拼接且不知道拼接次数,创建StringBuilder拼接
        StringBuilder sb = new StringBuilder();
        
        //注意:Set集合遍历方式只有三种(迭代器、增强for、foreach方法)
        set.forEach(c->sb.append(c));
​
        //展示拼接后的内容
        System.out.println(sb);
    }
}

2、排序方式

2.1 默认排序方式
  • Integer类,默认按数值大小排序

  • String类,默认按照其对应的编码表中的整数值逐个比较,均相同的会按照长度比较,短的在前

    TreeSet<String> set1 = new TreeSet<>();
     set1.add("aaa");
     set1.add("aaaa");
     set1.add("aab");
     set1.add("cde");
    ​
     System.out.println(set1);
    ​
    //排序结果为:aaa aaaa aab cde
2.2 自然排序
  • 类实现Comparable接口

  • 重写CompareTo方法

  • 根据方法的返回值,来组织排序规则(返回负数往左,返回正数往右,返回0不存)

    public class TreeSetDemo2 {
        public static void main(String[] args) {
            TreeSet<Student> set = new TreeSet<>();
    ​
            set.add(new Student("张三",23));
            set.add(new Student("李四",24));
            set.add(new Student("王五",25));
    ​
            System.out.println(set);
        }
    }
    ​
    //Student类要实现Comparable接口,重写compareTo方法的内部逻辑,此处年龄为主要排序依据,姓名为次要排序依据
    @Override  //年龄正序
    public int compareTo(Student o) {
        int ageResult = this.age-o.age;   //按年龄正序排列   倒序为o.age-this.age
        int nameResult = ageResult == 0 ? this.name.compareTo(o.name) : ageResult;
        //同名同年龄的需要保留,返回1或-1都无所谓,因为内容一样,谁前谁后不重要
        return nameResult == 0 ? 1 : nameResult;
    }
2.3 比较器排序(覆盖自然排序)
  • 在TreeSet的构造方法中,传入Compartor接口的实现类对象

  • 重写compare方法

  • 根据方法的返回值,来组织排序规则(返回负数往左,返回正数往右,返回0不存)

    public class TreeSetDemo3 {
        /*
           需求:对字符串进行排序,根据字符串的长度,从大到小排序
        */
        public static void main(String[] args) {
            //String类为Java写好的类,已具备自然排序,但不是我想要的排序规则,需要比较器排序
            TreeSet<String> set = new TreeSet<>(new Comparator<String>() {
                
                //重写compare方法
                @Override
                public int compare(String o1, String o2) {
                    //按长度倒序排列    o1.xxx-o2.xxx  正序  
                    int lengthResult = o2.length()-o1.length();
                    return lengthResult==0?o2.compareTo(o1):lengthResult;
                }
            });
    ​
            set.add("aaa");
            set.add("aa");
            set.add("bbbb");
            set.add("bb");
            set.add("aaaa");
    ​
            System.out.println(set);
        }
    }

3、排序如何选择

Java已经写好的类(如String、Integer、Double...)大多数都具有自然排序的规则(String默认按字典顺序、Integer默认升序、Double默认升序),这些规则放在源码中,我们无法修改其比较规则,如果我们要实现的排序规则跟自然排序不一样,此时需要使用比较器排序来覆盖自然排序规则。因为当同时具备自然排序和比较器排序时,会优先按照比较器进行排序操作;

4、利用TreeSet集合降序排列ArrayList

方法说明
static <T> void sort(T[] a,Comprtor<? super T> c)根据指定比较器产生的顺序指定对象数组进行排序
public class ArraysDemo {
    public static void main(String[] args){
        //利用集合TreeSet集合方法对数组降序排列
        Integer[] arr3 ={11,55,33,22,44};
        Arrays.sort(arr3, new Comparator<Integer>() {
            @Override
            public int compare(Integer o1, Integer o2) {
                return o2-o1;     //o2-o1降序
            }
        });
        System.out.println(Arrays.toString(arr3));
    }
}

三、HashSet集合

1、关于HashSet底层

集合底层采取哈希表存储数据,JDK8之前的哈希表是数组+链表;JDK8之后的哈希表是数组+链表+红黑树。哈希表是一种对于增删改查数据性能都比较好的结构

2、配合去重

HashSet特点就是去重。为了保证元素唯一,必须同时重写HashCode和equals方法!!!

2.1 配合去重流程

往集合中添加对象→→→调用对象的HashCode方法**,计算出一个应存入的索引地址→→→查看该位置上是否已存在元素→→→不存在则直接存;存在,调用equals方法比较内容→→→内容不同,存入集合;内容相同,不存入。

2.2 HashCode方法
  • 该方法底层调用了C++代码计算出一个随机数(常被人称为地址值)

  • 是JDK根据某种规则算出来的int类型的整数

  • 我们不必太过关注其内部实现,可以把它简单的理解为会计算出某个对象对应的整数值

  • 为了避免存入的时候元素都堆积在同一个索引位置,因此hashcode方法的重写就显得格外重要

    ⭐重写改造原则(降低索引冲突)⭐

    将对象的所有属性值都带入运算

    • 对象的属性相同,返回哈希值一定相同

    • 对象的属性不同,返回哈希值尽量不同

  • 40
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值