算法复杂度分析、List相关面试题、HashMap

 1. **算法复杂度分析**:

- **为什么要进行复杂度分析**:指导编写性能更优的代码,评判别人代码的好坏。

- **时间复杂度**:通过案例介绍了时间复杂度的分析过程,包括大O表示法、常见复杂度表示形式(常数、对数、线性、线性对数、平方、立方、k次方、指数、阶乘)以及不同时间复杂度(O(1)、O(n)、O(logn)、O(n * log n))的实例代码分析。

- **空间复杂度**:表示算法占用的额外存储空间与数据规模之间的增长关系,常见的空间复杂度有O(1)、O(n)、O(n ^2)。 2. **List相关面试题**:

- **数组**:

- **数组概述**:是一种用连续的内存空间存储相同数据类型数据的线性数据结构,通过寻址公式可以根据下标快速访问元素。

- **寻址公式**:arr[i] = baseAddress + i * dataTypeSize。

- **操作数组的时间复杂度**:随机查询(根据索引查询)为O(1),未知索引查询(未排序数组为O(n),排序数组通过二分查找为O(log2n)),插入和删除在最好情况下是O(1),最坏情况下是O(n),平均情况下是O(n)。

- **ArrayList源码分析**:

- **成员变量**:包括默认初始容量、空数组实例、存储元素的数组缓冲区和ArrayList的大小。

- **构造方法**:带初始化容量的构造函数和无参构造函数。

- **添加数据的流程**:底层是用动态的数组实现,初始容量为0,当第一次添加数据时初始化容量为10,扩容时是原来容量的1.5倍,每次扩容都需要拷贝数组。添加逻辑包括确保数组有足够空间存储新元素,计算容量,若需要扩容则进行扩容(原来的1.5倍),将新元素添加到指定位置并返回添加成功布尔值。

- **面试题**:ArrayList list = new ArrayList(10)中的list未扩容;数组转List使用Arrays.asList方法,List转数组使用List的toArray方法,Arrays.asList转List后修改数组内容会影响List,List用toArray转数组后修改List内容不会影响数组。

- **链表**:

- **单向链表**:每个结点包括数据域和指针域,支持查询(头节点时间复杂度为O(1),其他结点为O(n))、插入和删除(头节点时间复杂度为O(1),其他结点为O(n))操作。

- **双向链表**:每个结点有后继指针next和前驱指针prev,支持双向遍历,查询头尾结点的时间复杂度是O(1),平均查询时间复杂度是O(n),给定节点找前驱节点的时间复杂度为O(1),增删头尾结点的时间复杂度为O(1),其他部分结点增删的时间复杂度是O(n),给定节点增删的时间复杂度为O(1)。

- **面试题**:ArrayList和LinkedList的区别包括底层数据结构(ArrayList是动态数组,LinkedList是双向链表)、操作数据效率(ArrayList按下标查询为O(1),LinkedList不支持下标查询,查找未知索引都为O(n),ArrayList尾部插入和删除为O(1),其他部分为O(n),LinkedList头尾节点增删为O(1),其他为O(n))、内存空间占用(ArrayList内存连续节省内存,LinkedList需要存储数据和两个指针更占用内存)和线程安全(都不是线程安全的)。

3. **HashMap相关面试题**:

- **二叉树**: - **二叉树概述**:每个节点最多有两个子节点,分为左子节点和右子节点,Java中有数组存储和链式存储两种实现方式。

- **二叉搜索树**:是一种常见的二叉树,要求左子树节点值小于根节点值,右子树节点值大于根节点值,时间复杂度因树的形态而异,最好情况为O(logn),最坏情况为O(n)。

- **红黑树**:是一种自平衡的二叉搜索树,具有五条性质(节点颜色、根节点颜色、叶子节点、红色节点子节点颜色、路径黑色节点数),查找、添加和删除的时间复杂度均为O(log n)。

- **散列表**: - **散列表概述**:是根据键直接访问在内存存储位置值的数据结构,由数组演化而来,利用数组支持按照下标进行随机访问数据的特性。

- **散列函数和散列冲突**:散列函数将键映射为数组下标,基本要求是计算得到的散列值必须是大于等于0的正整数,且对于相同的键计算得到的哈希值也必相同,不同的键计算得到的哈希值也必不相同,但实际中很难避免散列冲突。

- **散列冲突 - 链表法(拉链)**:数组的每个下标位置对应一条链表,所有散列值相同的元素放到相同槽位对应的链表中。

- **时间复杂度 - 散列表**:插入和平均情况下基于链表法解决冲突时查询的时间复杂度是O(1),散列表可能会退化为链表,查询的时间复杂度就从O(1)退化为O(n),将链表法中的链表改造为红黑树后查询的时间复杂度是O(logn),还可以防止DDos攻击。

- **面试题**:

- **说一下HashMap的实现原理**:底层使用hash表数据结构,即数组和链表或红黑树,put元素时根据key的hashCode计算下标,存储时若hash值相同分两种情况处理(key相同覆盖原始值,key不同放入链表或红黑树),获取时通过hash值找到下标进一步判断key找到对应值,jdk1.7和jdk1.8的区别在于1.8在链表长度大于8且数组长度大于64时将链表转化为红黑树,扩容时红黑树拆分成的树的结点数小于等于临界值6个则退化成链表。

- **HashMap的put方法的具体流程**:判断键值对数组是否为空进行初始化,根据键值计算hash值得到数组索引,判断数组下标位置是否有数据进行相应处理(无数据直接新建节点添加,有数据判断key是否相同进行覆盖或在红黑树、链表中进行操作),插入成功后判断键值对数量是否超过扩容阈值进行扩容。

- **讲一讲HashMap的扩容机制**:添加元素或初始化时调用resize方法进行扩容,第一次添加数据初始化数组长度为16,以后每次扩容为原来容量的2倍,扩容后创建新数组并挪动老数组中的数据,没有hash冲突的节点通过位与运算计算新数组的索引位置,红黑树走红黑树的添加,链表需要遍历可能拆分,根据(e.hash & oldCap)是否为0确定元素位置。

- **hashMap的寻址算法**:通过hash(key)方法计算key的hash值,使用位与运算代替取模计算数组下标,提高计算索引和扩容时重新计算索引的效率。

- **hashmap在1.7情况下的多线程死循环问题**:在数组扩容时,由于链表是头插法,可能导致死循环,JDK 8将扩容算法调整为尾插法避免了该问题。

- **HashSet与HashMap的区别**:HashSet实现了Set接口仅存储对象,底层用HashMap实现存储(利用hashMap的key键存储元素值,value值默认为Object对象),不允许出现重复值,判断标准和HashMap相同(两个元素的hashCode相等并且通过equals()方法返回true)。

- **HashTable与HashMap的区别**:数据结构(HashTable是数组 + 链表,HashMap在1.8之后改为数组 + 链表 + 红黑树)、是否可以为null(HashTable的Key和value都不能为null,HashMap可以为null)、hash算法(HashTable使用key的hashCode(),HashMap进行二次hash)、扩容方式(HashTable当前容量翻倍 + 1,HashMap当前容量翻倍)、线程安全(HashTable是同步的线程安全,HashMap非线程安全)。

4. **真实面试还原**:

- **Java常见的集合类**:候选人介绍了Java中的集合框架,包括Collection(单列集合,子接口有List和Set,常见实现类有ArrayList、LinkedList、HashSet和TreeSet)和Map(双列集合,常见实现类有HashMap、TreeMap和ConcurrentHashMap)。

- **List**:候选人回答了ArrayList底层实现(通过add方法确保数组有足够空间,计算容量并扩容,将新元素添加到指定位置并返回布尔值)、ArrayList list = new ArrayList(10)中的list扩容次数(未扩容)、数组和List之间的转换(数组转List使用Arrays.asList方法,List转

  • 19
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值