1. 说⼀下⼏种常⻅的排序算法和分别的复杂度

【快速排序】

原理:快速排序采⽤的是⼀种分治的思想,它先找⼀个基准数(⼀般选择第⼀个值),然后将⽐这个基准数⼩的数字都放到它的左边,然后再递归调⽤,分别对左右两边快速排序,直到每⼀边只有⼀个数字.整个排序就完成了.

  1. 选定⼀个合适的值(理想情况中值最好,但实现中⼀般使⽤数组第⼀个值),称为“枢轴”(pivot)。
  2. 基于这个值,将数组分为两部分,较⼩的分在左边,较⼤的分在右边。
  3. 可以肯定,如此⼀轮下来,这个枢轴的位置⼀定在最终位置上。
  4. 对两个⼦数组分别重复上述过程,直到每个数组只有⼀个元素。
  5. 排序完成。

复杂度:O(n)

特点:快速排序是我们平常最常使⽤的⼀种排序算法,因为它速度快,效率⾼,是最优秀的⼀种排序算法.

【冒泡排序】

原理:冒泡排序其实就是逐⼀⽐较交换,进⾏⾥外两次循环,外层循环为遍历所有数字,逐个确定每个位置,⾥层循环为确定了位置后,遍历所有后⾯没有确定位置的数字,与该位置的数字进⾏⽐较,只要⽐该位置的数字⼩,就和该位置的数字进⾏交换.

复杂度:O(n^2),最佳时间复杂度为O(n)

特点:冒泡排序在我们实际开发中,使⽤的还是⽐较少的.它更加适合数据规模⽐较少的时候,因为它的效率是⽐较低的,但是优点是逻辑简单,容易让我们记得.

直接插⼊排序:

原理:直接插⼊排序是将从第⼆个数字开始,逐个拿出来,插⼊到之前排好序的数列⾥.

复杂度:O(n^2),最佳时间复杂度为O(n)

直接选择排序:

原理:直接选择排序是从第⼀个位置开始遍历位置,找到剩余未排序的数据⾥最⼩的,找到最⼩的后,再做交换

复杂度:O(n^2)

特点:和冒泡排序⼀样,逻辑简单,但是效率不⾼,适合少量的数据排序。

2. ⽤java写⼀个冒泡排序算法

Java面试题 - 数据结构与算法_# 面试题

3. 描述⼀下链式存储结构

a. 线性结构的优点是可以实现随机读取,时间复杂度为O(1),空间利⽤率⾼,缺点是进⾏插⼊和删除操作时⽐较麻烦,时间复杂度为O(n),同时容量受限制,需要事先确定容量⼤⼩,容量过⼤,浪费空间资源,过⼩不能满⾜使⽤要求,会产⽣溢出问题。

b. 链式存储结构的优点主要是插⼊和删除⾮常简单,前提条件是知道操作位置,时间复杂度是O(1),但如果不知道操作位置则要定位元素,时间复杂度为O(n),没有容量的限制,可以使⽤过程中动态分配的分配内存空间,不⽤担⼼溢出问题,但是它并不能实现随机读取,同时空间利⽤率不⾼。

4. 如何遍历一颗二叉树

Java面试题 - 数据结构与算法_# 面试题_02

树节点:
Java面试题 - 数据结构与算法_# 面试题_03
b. 递归先序遍历:先输出节点的值,再递归遍历左右⼦树。中序和后序的递归类似,改变根节点输出位置即可。
Java面试题 - 数据结构与算法_# 面试题_04
c. 递归中序遍历:过程和递归先序遍历类似
Java面试题 - 数据结构与算法_# 面试题_05
d. 递归后序遍历:
Java面试题 - 数据结构与算法_# 面试题_06

5. 倒排⼀个LinkedList

Collections.reverse(linkedList);

6. ⽤java写⼀个递归遍历⽬录下⾯的所有⽂件(directory.listFiles())

Java面试题 - 数据结构与算法_# 面试题_07

7. ⼆叉树与红⿊树

⼆叉树

Java面试题 - 数据结构与算法_# 面试题_08
特性:

  • 左⼦树上所有结点的值均⼩于或等于它的根结点的值。
  • 右⼦树上所有结点的值均⼤于或等于它的根结点的值。
  • 左、右⼦树也分别为⼆叉排序树。

查找:

  • ⼆分查找(通过⼀层⼀层的⽐较⼤⼩来查找位置):如查找值为10的节点:9–13–11–10

缺陷:

  • 插⼊容易变成线性形态,查找性能⼤打折扣,这时需要引⼊红⿊树来解决

红黑树
Java面试题 - 数据结构与算法_# 面试题_09

特点:是⼀种⾃平衡的⼆叉查找树,除了符合⼆叉树的特点之外,还符合以下⼏点:

  • 节点是红⾊或⿊⾊。
  • 根节点是⿊⾊。
  • 每个叶⼦节点都是⿊⾊的空节点(NIL节点)。
  • 每个红⾊节点的两个⼦节点都是⿊⾊。(从每个叶⼦到根的所有路径上不能有两个连续的红⾊节点)
  • 从任⼀节点到其每个叶⼦的所有路径都包含相同数⽬的⿊⾊节点。

这些规则保证了红⿊树的⾃平衡。
红⿊树从根到叶⼦的最长路径不会超过最短路径的2倍。
提⾼寻址效率。

添加删除:通过⾃旋来保证平衡

8. b-tree、b+tree多叉树

b-tree(⽂件系统)

B树也称B-树,它是⼀颗多路平衡查找树。我们描述⼀颗B树时需要指定它的阶数,阶数表示了⼀个结点最多有多少个孩⼦结点,⼀般⽤字⺟m表示阶数。当m取2时,就是我们常⻅的⼆叉搜索树。

定义:

  • 1)每个结点最多有m-1个关键字。
  • 2)根结点最少可以只有1个关键字。
  • 3)⾮根结点⾄少有Math.ceil(m/2)-1个关键字。
  • 4)每个结点中的关键字都按照从⼩到⼤的顺序排列,每个关键字的左⼦树中的所有关键字都⼩于它,⽽右⼦树中的所有关键字都⼤于它。
  • 5)所有叶⼦结点都位于同⼀层,或者说根结点到每个叶⼦结点的⻓度都相同。
    Java面试题 - 数据结构与算法_# 面试题_10
    插⼊数据,向兄弟节点借,兄弟节点不够则向⽗节点借;

b+tree(mysql索引)

定义:

  • 1)B+树包含2种类型的结点:内部结点(也称索引结点)和叶⼦结点。根结点本身即可以是内部结点,也可以是叶⼦结点。根结点的关键字个数最少可以只有1个。
  • 2)B+树与B树最⼤的不同是内部结点不保存数据,只⽤于索引,所有数据(或者说记录)都保存在叶⼦结点中。
  • 3)m阶B+树表示了内部结点最多有m-1个关键字(或者说内部结点最多有m个⼦树),阶数m同时限制了叶⼦结点最多存储m-1个记录。
  • 4)内部结点中的key都按照从⼩到⼤的顺序排列,对于内部结点中的⼀个key,左树中的所有key都⼩于它,右⼦树中的key都⼤于等于它。叶⼦结点中的记录也按照key的⼤⼩排列。
  • 5)每个叶⼦结点都存有相邻叶⼦结点的指针,叶⼦结点本身依关键字的⼤⼩⾃⼩⽽⼤顺序链接。

Java面试题 - 数据结构与算法_# 面试题_11

9. 谈谈数据结构,⽐如TreeMap

TreeMap实现了红⿊树的结构。

10. 图的深度遍历和⼴度遍历

1、深度优先遍历:
Java面试题 - 数据结构与算法_# 面试题_12

2、⼴度优先遍历:
Java面试题 - 数据结构与算法_# 面试题_13

11. 介绍⼀下红⿊树、⼆叉平衡树

12. 说说java集合,每个集合下⾯有哪些实现类,及其数据结构。

HashMap:

Java面试题 - 数据结构与算法_# 面试题_14

  1. 在HashMap内部,采⽤了数组+链表的形式来组织键值对Entry<Key,Value>(利⽤数组的查询快+链表的插⼊删除快)
  2. HashMap在存储键值对Entry<Key,Value>的时候,会根据Key的hashcode值,以某种映射关系,决定应当将这对键值对Entry<Key,Value>存储在HashMap中的什么位置上
  3. 在JDK1.7进⾏多线程put操作,之后遍历,直接死循环,CPU飙到100%,在JDK 1.8中进⾏多线程操作会出现节点和value值丢失,为什么JDK1.7与JDK1.8多线程操作会出现很⼤不同,是因为JDK 1.8的作者对resize⽅法进⾏了优化不会产⽣链表闭环。

结构:
Java面试题 - 数据结构与算法_# 面试题_15

HashMap扩容:
Java面试题 - 数据结构与算法_# 面试题_16

1、很简单的计算:由于默认的加载因⼦是0.75 ,那么,此时map的阀值是 16*0.75 = 12,即添加第13个键值对<Key,Value>的时候,map的容量会扩充⼀倍。
2、确实如此,但是为了尽可能第减少桶中的Entry<Key,Value>链表的长度,以提⾼HashMap的存取性能,确定的这个经验值。如果读者你对存取效率要求的不是太⾼,想省点空间的话,你可以new HashMap(int initialCapacity, float loadFactor)构造⽅法将这个因⼦设置得⼤⼀些也⽆妨。

扩容步骤:
Java面试题 - 数据结构与算法_# 面试题_17
2. 为何扩容为原来的两倍(性能):

  • 在HashMap通过键的哈希值进⾏定位桶位置的时候,调⽤了⼀个indexFor(hash, table.lengthJava面试题 - 数据结构与算法_# 面试题_18
  • 通过限制length是⼀个2的幂数,h & (length-1)和h % length结果是⼀致的。
  • 如果length是⼀个2的幂的数,那么length-1就会变成⼀个mask, 它会将hashcode低位取出来,hashcode的低位实际就是余数,和取余操作相⽐,与操作会将性能提升很多。

put流程:

a. 获取这个Key的hashcode值,根据此值确定应该将这⼀对键值对存放在哪⼀个桶中,即确定要存放桶的索引;
b.遍历所在桶中的Entry<Key,Value>链表,查找其中是否已经有了以Key值为Key存储的Entry<Key,Value>对象,
c1. 若已存在,定位到对应的Entry<Key,Value>,其中的Value值更新为新的Value值;返回旧值;
c2.若不存在,则根据键值对<Key,Value>创建⼀个新的Entry<Key,Value>对象,然后添加到这个桶的Entry<Key,Value>
d.当前的HashMap的⼤⼩(即Entry<key,Value>节点的数⽬)是否超过了阀值,若超过了阀值(threshold), 则增⼤HashMap的容量(即Entry[] table 的⼤⼩),并且重新组织内部各个Entry<Key,Value>排列。

get流程:

a. 获取这个Key的hashcode值,根据此hashcode值决定应该从哪⼀个桶中查找;
b.遍历所在桶中的Entry<Key,Value>链表,查找其中是否已经有了以Key值为Key存储的Entry<Key,Value>对象,
c1. 若已存在,定位到对应的Entry<Key,Value>,返回value c2. 若不存在,返回null;