10 道数据结构算法题,不看答案你会几道题

在这里插入图片描述

文章首将对应问题罗列,可以根据目录找到感兴趣的

  1. ArrayList 和 LinkedList 区别?结合数据结构说明
  2. B Tree 和 B+ Tree 区别?两个 Tree 对比各自优势
  3. Mysql 数据库为什么要使用树结构充当索引结构
  4. LinkedList 的插入时间复杂度
  5. 冒泡排序的 3 种方式?哪种性能最好
  6. 冒泡排序的平均时间复杂度以及空间复杂度分别是多少
  7. 平衡二叉树和红黑树的区别
  8. HashMap 中的 key 可以存储可变引用类型么?有什么坏处?有什么解决方案
  9. 什么是Trie树(字典树)
  10. 为什么 hashMap 的大小是 2 的 n 次方?(结合二进制)

微信搜索【源码兴趣圈】,关注龙台,回复【资料】领取涵盖 GO、Netty、SpringCLoud Alibaba、Seata、开发规范、面试宝典、数据结构等电子书 or 视频学习资料!


ArrayList 和 LinkedList 区别?结合数据结构说明

ArrayList 底层是使用动态数组存储的数据结构,如果查询采用的定位方式是索引下标定位(还有根据元素定位 O(n)),时间复杂度为 O(1),所以查询效率会很快,但是增、删效率不好,如果是向尾部添加元素效率高

因为 ArrayList 类采用的存储结构是连续的内存存储,如果进行删改元素将会对涉及到的数组原有元素进行重排,所以特点是查询快,增删慢;并且在长度大于初始长度的话,会对数组进行扩容

LinkedList 底层是使用双向链表的形式进行存储,查询时间复杂度是 O(n),所以查询会比较慢,但是增删会很快,因为不涉及到对现有元素的重排

如果是增加元素的话,则对所增加元素的前序节点和后序节点改为插入位置的前后元素,将前后元素的前序节点和后序节点改为所增加的元素即可


B-Tree 和 B+ Tree 区别?两个 Tree 对比各自优势

B-tree 算法减少定位记录时所经历的中间过程,从而加快存取速度。普遍运用在数据库和文件系统


B-Tree:

  1. 跟节点至少有两个节点

  2. 首先从根节点进行二分查找,如果找到则返回对应节点的 data,否则对相应区间的指针指向的节点递归进行查找,直到找到节点或找到 null 指针,前者查找成功,后者查找失败


B+Tree:

  1. 有 k 个子结点的结点必然有 k 个关键码

  2. 非叶结点仅具有索引作用,跟记录有关的信息均存放在叶结点中

  3. 树的所有叶结点构成一个有序链表,可以按照关键码排序的次序遍历全部记录

这个优化的目的是为了提高区间访问的性能


B-Tree 优点:

由于 B-Tree 的每一个节点都包含 key 和 value,因此经常访问的元素可能离根节点更近,因此访问也更迅速


B+Tree 优点:

  1. B+Tree 的非叶子结点只包含导航信息,不包含实际的值,所有的叶子结点和相连的节点使用链表相连,便于区间查找和遍历

  2. 由于 B+Tree 在内部节点上不包含数据信息,因此在内存页中能够存放更多的 key。 数据存放的更加紧密,具有更好的空间局部性。因此访问叶子节点上关联的数据也具有更好的缓存命中率

  3. B+Tree 的叶子结点都是相链的,因此对整棵树的便利只需要一次线性遍历叶子结点即可。而且由于数据顺序排列并且相连,所以便于区间查找和搜索。而 B 树则需要进行每一层的递归遍历。相邻的元素可能在内存中不相邻,所以缓存命中性没有 B+Tree 好


Mysql 数据库为什么要使用树结构充当索引结构

索引本身也很大,不可能全部存储在内存中,因此索引往往以索引文件的形式存储的磁盘上

这样的话,索引查找过程中就要产生磁盘 I/O 消耗,相对于内存存取,I/O 存取的消耗要高几个数量级,所以评价一个数据结构作为索引的优劣最重要的指标就是在查找过程中磁盘 I/O 操作次数的渐进复杂度。换句话说,索引的结构组织要尽量减少查找过程中磁盘 I/O 的存取次数


LinkedList 的插入时间复杂度

如果执行 add(E e) 时间复杂度为 O(1),只需要获取 last 元素,修改节点引用即可,如果使用 add(int index, E element)时间复杂度为 O(n),假如插入位置为 5,需要先查找到第 5 个节点,然后只需要修改前后引用即可,不用进行元素移动

与 Arraylist 的最大特点,而是内存空间上的节省,因为 ArrayList 进行增删元素时,会造成数组元素重排,会将元素移动到新的数组


冒泡排序的 3 种方式?

什么是冒泡排序

  1. 将一个数与它后面的那一个数进行比较,如果前面>后面,则两者交换位置

  2. 对数组中的每一个数都进行这样的操作,一个循环下来最大的数值就会到达数组的最后面

  3. 再将数组范围缩小一个(即再次比较时不看数组最后且最大的那个),再次循环上面的步骤

  4. 当数组的范围缩小到只剩下一个数的时候,那么数组就已经有序了,冒泡排序结束


最常见第一版冒泡排序

public static void sortFunc1(int[] arr) {
    if (arr == null || arr.length == 0) {
        return;
    }
    for (int i = arr.length - 1; i > 0; i--) {
        for (int j = 0; j < arr.length - 1; j++) {
            if (arr[j] > arr[j + 1]) {
                swap(arr, j + 1, j);
            }
        }
    }
}

第二版冒泡排序

public static void sortFunc2(int[] arr) {
    if (arr == null || arr.length == 0) {
        return;
    }
    boolean flag = true;
    while (flag) {
        flag = false;
        for (int i = arr.length - 1; i > 0; i--) {
            for (int j = 0; j < arr.length - 1; j++) {
                if (arr[j] > arr[j + 1]) {
                    swap(arr, j + 1, j);
                    flag = true;
                }
            }
        }
    }
}

第三版冒泡排序

public static void sortFunc2(int[] arr) {
    if (arr == null || arr.length == 0) {
        return;
    }
    int flag = arr.length - 1;
    while (flag > 0) {
        int end = flag;
        flag = 0;
        for (int j = 0; j < end; j++) {
            if (arr[j] > arr[j + 1]) {
                swap(arr, j + 1, j);
                flag = j;
            }
        }
    }
}

swap 交换算法

public static void swap(int[] arr, int i, int j) {
    arr[i] = arr[i] ^ arr[j];
    arr[j] = arr[i] ^ arr[j];
    arr[i] = arr[i] ^ arr[j];
}

冒泡排序的平均时间复杂度以及空间复杂度分别是多少

平均时间复杂度:O(n2)

空间复杂度:O(1)


平衡二叉树和红黑树的区别

  1. 红黑树放弃了追求完全平衡,追求大致平衡,在与平衡二叉树的时间复杂度相差不大的情况下,保证每次插入最多只需要三次旋转就能达到平衡,实现起来也更为简单
  2. 平衡二叉树追求绝对平衡,条件比较苛刻,实现起来比较麻烦,每次插入新节点之后需要旋转的次数不能预知

HashMap 中的 key 可以存储可变引用类型么?有什么坏处?有什么解决方案

可以定义为可变引用类型

假定一个 Student 对象为一个 hashMap 的 key,如果存入后,对对象进行了 set 操作,导致 hashCode 值改变,如果在使用 hashMap 的 get 操作查找时,是查找不到的

这种方式可以将 Student 对象的 hashCode 和 equals 方法来进行避免


什么是Trie树(字典树)

trie,又称前缀树,是一种有序树,用于保存关联数组,其中的键通常是字符串。与二叉查找树不同,键不是直接保存在节点中,而是由节点在树中的位置决定。一个节点的所有子孙都有相同的前缀,也就是这个节点对应的字符串,而根节点对应空字符串。一般情况下,不是所有的节点都有对应的值,只有叶子节点和部分内部节点所对应的键才有相关的值

在图示中,键标注在节点中,值标注在节点之下。每一个完整的英文单词对应一个特定的整数。Trie 可以看作是一个确定有限状态自动机,尽管边上的符号一般是隐含在分支的顺序中的

键不需要被显式地保存在节点中。图示中标注出完整的单词,只是为了演示 trie 的原理

优点:利用字符串的公共前缀来节约存储空间,最大限度地减少无谓的字符串比较,查询效率比哈希表高

缺点:Trie树是一种比较简单的数据结构.理解起来比较简单,正所谓简单的东西也得付出代价.故Trie树也有它的缺点,Trie树的内存消耗非常大


为什么 hashMap 的大小是 2 的 n 次方?(结合二进制)

HashMap是根据key的hash值决定key放到哪个桶中,通过tab[i = (n - 1) & hash]公式计算得出

这里的n是HashMap的容量,因为n永远是2的次幂,所以n - 1通过二进制表示,永远都是末尾连续1的形式表示(如00001111、00000011),当(n - 1) 和hash做与运算时,会保留hash中后x位的1

例如00001111 & 10000011 = 00000011

这样做的好处在于:

  • &运算速度快,至少比%取模运算快
  • 能保证索引值肯定在HashMap的容量大小范围内
  • (n - 1) & hash的值是均匀分布的,可以减少hash冲突

结言

由于作者水平有限, 欢迎大家能够反馈指正文章中错误不正确的地方, 感谢 🙏

小伙伴的喜欢就是对我最大的支持, 如果读了文章有所收获, 希望能够 点赞、评论、关注三连!


推荐阅读:

  1. 【强烈推荐】1w 字,18 张图,彻底说清 springboot starter
  2. 【强烈推荐】谨慎使用 JDK 8 新特性并行流 ParallelStream
  3. 【强烈推荐】一文快速掌握 Redisson 如何实现分布式锁原理
  4. 【大厂面试真题】JDK 线程池中如何不超最大线程数快速消费任务
  5. 【大厂面试真题】JDK 线程池如何保证核心线程不被销毁

作者麻花,坐标帝都 Java 后端研发,励志成为架构师的一枚处女座程序员,专注高并发、框架底层源码、分布式等知识分享

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值