面试必备常用数据结构详解+例题解析(持续更新。。。)
常用数据结构
- 数组、字符
- 链表
- 栈
- 队列
- 双端队列
- 树
一、数组、字符串
掌握一种数据结构,就必须要懂得分析它的优点和缺点。
数组的优点
- 构建非常简单
- 能在O(1)的时间里根据数组的下标(index)查询某个元素
数组的缺点
- 构建时必须分配一段连续的空间
- 查询某个元素是否存在时需要遍历整个数组,耗费O(n)的时间
- 删除和添加某个元素时,同样需要耗费O(n)的时间
二、链表(LinkedList)
- 单链表:链表中的每个元素实际上是一个单独的对象,而所有对象都通过每个元素的引用字段链接在一起。
- 双链表:与单链表不同的是,双链表的每个结点中都含有两个引用字段。
链表的优点
- 链表能灵活地分配内存空间;
- 能在O(1)时间内删除或者添加元素,前提是该元素的前一个元素已知,当然也取决于是单链表还是双链表,在双链表中,如果已知该元素的后一个元素,同样可以在O(1)时间内删除或者添加该元素。
链表的缺点
- 不想数组能通过下标迅速读取元素,每次都要从链表头开始一个一个读取;
- 查询第k个元素需要O(k)时间。
应用场景
如果问题需要很多快速查询,链表可能并不适合;如果问题中,数据的元素个数不确定,经常进行数据的添加或删除,那链表会比较合适。而数组适合数据元素大小确定,删除插入的元素操作并不多。
经典解题方式
- 利用快慢指针(有时候需要用到是哪个指针)
- 构建一个虚假的链表头
例题
LeetCoded第21题题解–合并两个有序链表
LeetCoded第206题题解–反转链表
LeetCoded第2题题解–两数相加
三、栈(Stack)
特点
栈最大的特点就是后进先出。对于栈中的数据来说,所有操作都是在栈顶完成的。
实现
利用一个单链表来实现栈的数据结构。而且,因为我们都只针对栈顶元素进行操作,所以借用单链表的头就能让所有栈的操作在O(1)的使劲内完成。
应用场景
在解决某个问题的时候,只要求关心最近一次的操作,并且在操作完成了之后,需要向前查找到更前一次的操作。
如果打算用一个数组外加一个指针来实现类似的功能,那么当数组的长度发生变化时,哪怕只是在最后添加一个新的元素,时间复杂度都不再是O(1),并且,空间复杂度也得不到优化。
例题
LeetCoded第20题题解–有效的括号
LeetCoded第739题题解–每日温度
四、队列(Queue)
特点
队列最大的特点是先进先出,就像顺序排队一样。对于队列,我们只能在队尾查看和添加数据,在队头查看和删除数据。
实现
可以借助双链表来实现队列。双链表的头指针允许在队头查看和删除数据,而双链表的尾指针允许我们在队尾查看和添加数据。
应用场景
直观来看,当我们需要按照一定的顺序来处理数据,而数据的数据量在不断发生变化的时候,则需要对来来帮助解题。在算法面试中,广度优先搜索是运用队列最多的地方。
五、双端队列(Deque)
特点
双端队列和普通队列最大的不同在于,它允许我们在队列的头尾两队都能在O(1)的时间内进行数据的查看、添加和删除。
实现
与队列相似,我们可以利用一个双链表实现双端队列。
应用场景
双端队列最常用的地方是实现一个长度动态变化的窗口或者连续空间,而动态窗口这种数据结构在很多题目里都有运用。
六、树(Tree)
常见的树有:普通二叉树、平衡二叉树、完全二叉树、二叉搜索树、四叉树(Quadtree)、多叉树(N-ary Tree)。对于一些特殊的树,例如红黑树(Red-Black Tree)、自平衡二叉搜索树(AVL Tree),一般在面试中不会被问到,除非你所涉及的研究领域跟他们相关或者你十分感兴趣,否则不需要特别着重准备。
树的遍历
1. 前序遍历
方法: 先访问根节点,然后访问左子树,最后访问右子树。在访问左子树、右子树的时候,同样,先访问子树的根节点,再访问子树根节点的左子树和右子树,这是一个不断递归的过程。
应用场景: 运用最多的场合包括在树里进行搜索以及创建一棵新的树。
2. 中序遍历
方法: 先访问左子树,然后访问根节点,最后访问右子树,在访问左、右子树的时候,同样,先访问子树的左边,再访问子树的根节点,最后再访问子树的右边。
应用场景: 最常见的是二叉搜索树,由于儿茶搜索树的性质就是左孩子小于根节点,根节点小于右孩子,对二叉搜索树进行中序遍历的时候,被访问到的节点大小是按照顺序进行的。
3. 后序遍历
方法: 先访问左子树,然后访问右子树,最后访问根节点。
应用场景 在对某个节点进行分析的时候,需要来自左子树和右子树的信息。收集信息的操作是从树的底部不断往上进行,好比你在修剪一科树的叶子,修剪的方法时从外面不断地往根部将叶子一片片地修剪掉。