2023黑马Java集合相关面试题

导学

这次课程主要涉及到的是List和Map相关的面试题,比较高频就是

  • ArrayList

  • LinkedList

  • HashMap

  • ConcurrentHashMap

在这里插入图片描述

  • ArrayList底层实现是数组
  • LinkedList底层实现是双向链表
  • HashMap的底层实现使用了众多数据结构,包含了数组、链表、散列表、红黑树等

在讲解这些集合之后,我们会讲解数据结构,知道了数据结构的特点之后,熟悉集合就更加简单了。在讲解数据结构之前,我们也会简单普及一下算法复杂度分析,让大家能够评判代码的好坏,也能更加深入去理解数据结构和集合。

1 算法复杂度分析

1.1 为什么要进行复杂度分析?

我们先来看下面这个代码,你能评判这个代码的好坏吗?

/**
 ** *求**1~n**的累加和
 ** @param* *n
 ** @return
*/
public int sum(int n) {
   
   int sum = 0;
   for ( int i = 1; i <= n; i++) {
   
     sum = sum + i;
   }
   return sum;
}

其实学习算法复杂度的好处就是:

  • 指导你编写出性能更优的代码

  • 评判别人写的代码的好坏

相信你学完了算法复杂度分析,就有能力评判上面代码的好坏了

关于算法复杂度分析,包含了两个内容,一个是时间复杂度,一个是空间复杂度,通常情况下说复杂度,都是指时间复杂度,我们也会重点讲解时间复杂度

1.2 时间复杂度

1.2.1 案例

时间复杂度分析:简单来说就是评估代码的执行耗时的,大家还是看刚才的代码:

/**
 ** *求**1~n**的累加和
 ** @param* *n
 ** @return
*/
public int sum(int n) {
   
   int sum = 0;
   for ( int i = 1; i <= n; i++) {
   
     sum = sum + i;
   }
   return sum;
}

分析这个代码的时间复杂度,分析过程如下:

1.假如每行代码的执行耗时一样:1ms

2.分析这段代码总执行多少行?3n+3

3.代码耗时总时间: T(n) = (3n + 3) * 1ms

T(n):就是代码总耗时

我们现在有了总耗时,需要借助大O表示法来计算这个代码的时间复杂度

1.2.2 大O表示法

大O表示法:不具体表示代码真正的执行时间,而是表示代码执行时间随数据规模增长的变化趋势

刚才的代码示例总耗时公式为:T(n) = (3n + 3) * 1ms

其中 (3n + 3) 是代码的总行数,每行执行的时间都一样,所以得出结论:

T(n)与代码的执行次数成正比(代码行数越多,执行时间越长)

不过,大O表示法只需要代码执行时间与数据规模的增长趋势,公式可以简化如下:

T(n) =O(3n + 3)------------ T(n) = O(n)

当n很大时,公式中的低阶,常量,系数三部分并不左右其增长趋势,因此可以忽略,我们只需要记录一个最大的量级就可以了

下图也能表明数据的趋势

在这里插入图片描述

1.2.3 常见复杂度表示形式

在这里插入图片描述

速记口诀:常对幂指阶

越在上面的性能就越高,越往下性能就越低

下图是一些比较常见时间复杂度的时间与数据规模的趋势:

在这里插入图片描述

1.2.4 时间复杂度O(1)

实例代码:

public int test01(int n){
   
    int i=0;
    int j = 1;
    return i+j;
}

代码只有三行,它的复杂度也是O(1),而不是O(3)

再看如下代码:

public void test02(int n){
   
    int i=0;
    int sum=0;
    for(;i<100;i++){
   
        sum = sum+i;
    }
    System.out.println(sum);
}

整个代码中因为循环次数是固定的就是100次,这样的代码复杂度我们认为也是O(1)

一句话总结:只要代码的执行时间不随着n的增大而增大,这样的代码复杂度都是O(1)

1.2.5 时间复杂度O(n)

实例代码1:

/**
 * 求1~n的累加和
 * @param n
 * @return
 */
public int sum(int n) {
   
    int sum = 0;
    for ( int i = 1; i <= n; i++) {
   
        sum = sum + i;
    }
    return sum;
}

一层for循序时间复杂度就是O(n)

实例代码2:

public static int sum2(int n){
   
    int sum = 0;
    for (int i = 1; i < n; ++i) {
   
        for (int j = 1; j < n; ++j) {
   
            sum = sum + i * j;
        }
    }
    return sum;
}

这个代码的执行行数为:O( 3n^2 + 3n + 3 ),不过,依据大O表示的规则:常量、系数、低阶,可以忽略

所以这个代码最终的时间复杂度为:O(n^2)

1.2.6 时间复杂度O(logn)

对数复杂度非常的常见,但相对比较难以分析,实例代码:

public void test04(int n){
   
    int i=1;
    while(i<=n){
   
        i = i * 2;
    }
}

分析这个代码的复杂度,我们必须要再强调一个前提:复杂度分析就是要弄清楚代码的执行次数和数据规模n之间的关系

以上代码最关键的一行是:i = i * 2,这行代码可以决定这个while循环执行代码的行数,i的值是可以无限接近n的值的。如果i 一旦大于等于了n则循环条件就不满足了。也就说达到了最大的行数。我们可以分析一下i这个值变化的过程

分析过程如下:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-emjcEdoe-1683451730102)(Java集合相关面试题.assets/image-20230427174832858.png)]

由此可知,代码的时间复杂度表示为O(log n)

1.2.7 时间复杂度O(n * log n)

分析完O( log n ),那O( n * log n )就很容易理解了,比如下列代码:

public void test05(int n){
   
    int i=0;
    for(;i<=n;i++){
   
        test04(n);
    }
}

public void test04(int n){
   
    int i=1;
    while(i<=n){
   
        i = i * 2;
    }
}

1.3 空间复杂度

空间复杂度全称是渐进空间复杂度,表示算法占用的额外存储空间数据规模之间的增长关系

看下面代码

public void test(int n){
   
    int i=0;
    int sum=0;
    for(;i<n;i++){
   
        sum = sum+i;
    }
    System.out.println(sum);
}

代码执行并不需要占用额外的存储空间,只需要常量级的内存空间大小,因此空间复杂度是O(1)

再来看一个其他例子:

void print(int n) {
   
    int i = 0;
    int[] a = new int[n];
    for (i; i <n; ++i) 
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值