数据结构与Java

红黑树

  • 左旋右旋速记口令:
    父与左子为右旋,
    旋完子右变父左,
    其他都不变。
    父与右子为左旋,
    旋完子左变父右,
    其他都不变。
  • 红黑树之插入
    将新插入结点染为红色
    WHILE若新插入点不为null,不是根结点,并且其父结点颜色为红色
    IF若当前结点的父结点是当前结点的爷爷结点的左子结点
    IF若当前结点的叔叔结点为红
    将当前结点的父结点染为黑
    将叔叔结点染为黑
    将爷爷结点染为红
    将爷爷结点置为当前结点
    ELSE若当前结点的叔叔结点颜色为黑或NULL
    若当前结点为右子结点
    将父结点置为当前结点
    左旋当前结点
    将当前结点的父结点染为黑
    将当前结点的爷爷结点染为红
    右旋当前结点的爷爷结点
    ELSE对称…
    将根结点染为黑
  • 红黑树之删除(按照Java中TreeMap的逻辑进行书写,顺序为由上至下):
    若要删除的结点左右子结点均存在:
    寻找右子结点往下的最后的左子结点,
    以该结点作为后继结点(即下文中的当前结点)。
    若要删除的结点只存在唯一结点,
    则以唯一结点作为后继结点。
    若要删除的结点为黑色,则需对该树进行修复操作。
    修复:
    WHILE若当前结点为黑色,且不是根结点:
    IF若当前结点为左子结点:
    若当前结点的兄弟结点为红色
    将兄弟结点染为黑色,将父结点染为红色,左旋父结点。
    IF若当前节点的兄弟结点有左右两个子结点,并且均为黑色
    将兄弟结点染为红色,将当前结点的父结点置为当前结点。
    ELSE若当前结点的兄弟结点只有右子结点为黑
    将兄弟结点的左子结点染为黑色,将兄弟结点染为红色,右旋兄弟结点。
    将当前兄弟结点染为父节点的颜色,将父节点染为黑色,将兄弟结点的右子结点染为黑色,
    左旋父节点,将根节点赋值给当前结点。
    ELSE对称。。。
    将当前结点染为黑色。

时间复杂度

常数阶O(1)<对数阶O(log2n)<线性阶O(n)<线性对数阶O(nlog2n)<平方阶O(n2)`<`k次方阶O(nk)
<指数阶O(2n)`<`O(n!)`<`O(nn)

HashMap

从结构实现来讲,HashMap是数组+链表+红黑树实现的,HashMap底层是由数组组成Node[],哈希桶数组,Node是HashMap的一个内部类,实现了Map.Entry接口,该类中有几个重要字段:hash字段,key字段,value字段,next字段,在往HashMap中存储键值对时,过程大概是这样的:

  1. 判断键值对数组table[i]是否为空或为null,否则执行resize()进行扩容
  2. 根据键key计算hash值,得到插入的数组索引i,如果table[i] == null,直接新建结点添加,转向6,若果table[i]不为空,转向3;
  3. 判断table[i]的首个元素是否和key一样,如果相同直接覆盖value,否则转向4,这里的相同指的是hashCode以及equals;
  4. 判断table[i]是否为treeNode,即table[i]是否是红黑树,如果是红黑树,则直接在树种插入键值对,否则转向5;
  5. 遍历table[i],判断链表长度是否大于8,大于8的话把链表转换为红黑树,在红黑树中执行插入操作,否则进行链表的插入操作;遍历过程中若发现key已经存在直接覆盖value即可;
  6. 插入成功后,判断实际存在的键值对数量size是否超过了最大容量threshold,如果超过,进行扩容。
  • HashMap默认初始长度length为16,Load factor为负载因子(默认值是0.75),threshold是HashMap所能容纳的最大数据了的Node个数。threshold = length * Load factor(默认为12)。若实际存在的键值对数量超过了12,就会进行一次扩容,扩容大小为原来的一倍,也就是扩大到了32.
  • 为什么初始长度会设置为16呢?以及为什么哈希桶数组的长度为什么必须是2的次幂呢?(程序中有处理,若开发人员手动传入的长度不为2的次幂,也会将其转换为大于这个数,且离这个数最近的2的次幂)主要是为了在取模和扩容时做优化,同时为了减少冲突,HashMap定位哈希桶索引位置时,也加入了高位参与运算的过程,当开发人员存储一个key,value的键值对进入HashMap时,首先会将key值转为二进制的形式,然后和length-1(默认值即15二进制为1111)进行&运算,得到hash值假设为5,创建对应的Node对象后存储到数组索引为5的地方。
  • HashMap进行扩容resize时,会伴随着rehash的过程,而这个过程比较耗费性能,因此若能预知该HashMap需要存储的数据量,最好给HashMap指定初始值。(指定初始值时需考虑其threshold的值).

ArrayList

  • ArrayList默认容量为10,若容量满时,会扩充为原来的1.5倍,若扩充后不是整数,则取整数部分。因为ArrayList每次扩容都会伴随着深拷贝的发生,因此若能预知该ArrayList需要存储的数据量,最好给ArrayList指定初始值

List

  • ArrayList 采用索引(随机访问方式)遍历效率最高
  • LinkedList 采用for增强循环遍历效率最高,采用索引遍历效率最低,不能用!
  • Vector 采用索引(随机访问方式)遍历效率最高,若容量满时,容量大小会增加一倍
  • Stack(栈)继承于Vector
  • 实际使用的时候, 若需批量插入数据,可以使用LinkedList, 若数据全部插入后,在另外一个地方要对数据进行遍历, 可通过new ArrayList<>(linkedList) 将LinkedList的引用对象转为ArrayList之后进行遍历
  • 在JDK8中,若是小数据量的插入LinkedList快,若是大数据量的插入ArrayList快
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值