数据结构
专题-数据结构
数据结构相关基本是现场面试中出现频率最高的问题。
因为现场面试的时间限制,更难的问题需要大量的思考时间,所以一般只要求需要阐述思路(比如动态规划);
而数据结构相关的问题,因为有很强的先验知识,通常要求手写代码。
本专题只收录基础数据结构相关问题,不包括高级数据结构及数据结构的设计,比如线段树或者 LRU 缓存,这些问题可以参考数据结构_Advanced。
Index
二叉树
二叉树的深度
二叉树的深度 - 牛客
C++
二叉树的宽度
思路
层序遍历(队列)
C++
二叉树最大宽度(LeetCode)
LeetCode - 662. 二叉树最大宽度
问题描述
思路
本题在二叉树宽度的基础上加入了满二叉树的性质,即每层都有 2 ^ (n-1)个节点。某节点的左孩子的标号是2n, 右节点的标号是2n + 1。
注:如果在循环中会增删容器中的元素,则不应该在
for循环中使用size()方法,该方法的返回值会根据容器的内容动态改变。
C++
二叉树中的最长路径
思路
基于二叉树的深度
对任一子树而言,则经过该节点的一条最长路径为其
左子树的深度 + 右子树的深度 + 1遍历树中每个节点的最长路径,其中最大的即为整个树的最长路径
为什么最长路径不一定是经过根节点的那条路径?
判断平衡二叉树 TODO
判断树 B 是否为树 A 的子结构
树的子结构 - 牛客
题目描述
图示
思路
递归
有两个递归的点:一、递归寻找树 A 中与树 B 根节点相同的子节点;二、递归判断子结构是否相同
Code(递归)
利用前序和中序重建二叉树
重建二叉树 - 牛客
题目描述
思路
前序遍历的第一个值为根节点的值,使用这个值将中序遍历结果分成两部分,左部分为左子树的中序遍历结果,右部分为右子树的中序遍历的结果。
根据左右子树的长度,可以从前序遍历的结果中划分出左右子树的前序遍历结果
接下来就是递归过程
注意:必须序列中的值不重复才可以这么做
示例
Code(Python)
C++ 版本 > 题解-剑指Offer/重建二叉树
```Python
class TreeNode:
def init(self, x):
self.val = x
self.left = None
self.right = Noneclass Solution:
请实现两个函数,分别用来序列化和反序列化二叉树。 接口如下:
空节点用 '#' 表示,节点之间用空格分开
最近公共祖先
《剑指 Offer》 7.2 案例二
问题描述
如果树是二叉搜索树
找到第一个满足
p1 < root < p2的根节点,即为它们的最小公共父节点;如果寻找的过程中,没有这样的
root,那么p1和p2的最小公共父节点必是它们之一,此时遍历到p1或p2就返回。
如果树的节点中保存有指向父节点的指针
问题等价于求两个链表的第一个公共节点
如果只是普通的二叉树
236. 二叉树的最近公共祖先 - LeetCode
利用两个辅助链表/数组,保存分别到
p1和p2的路径;则
p1和p2的最小公共父节点就是这两个链表的最后一个公共节点C++
获取节点的路径
二叉树
非二叉树
链表
旋转链表(Rotate List)
LeetCode/61. 旋转链表
问题描述
思路
双指针
l, r记录两个位置,其中l指向倒数第k+1个节点,r指向最后一个非空节点;然后将
r指向头结点h,h指向l的下一个节点,最后断开l与下一个节点;注意
k可能大于链表的长度,此时可能需要遍历两次链表
代码 1
比较直观的写法,代码量稍大
```python
class ListNode:
def init(self, x):
self.val = x
self.next = None
class Solution: def rotateRight(self, h, k): """ :type h: ListNode :type k: int :rtype: ListNode """ if not h or k == 0: return h
反转链表
反转链表 - 牛客
题目描述
要求:不使用额外空间
思路
辅助图示思考
Code(迭代)
Code(递归)
合并排序链表
合并两个排序的链表 - 牛客
问题描述
迭代
递归
两个链表的第一个公共节点
思路 1
先求出两个链表的长度
l1和l2,然后让长的链表先走|l1-l2|步,此时两个指针距离第一个公共节点的距离相同,再走相同的步数即可在第一个公共节点相遇时间复杂度
O(m + n)代码(未测试)
思路 2
两个指针同时开始遍历,
当其中一个指针到达尾节点时,转到另一个链表继续遍历;
当另一个指针也到达尾节点时,也转到另一个链表继续遍历;
此时两个指针距离第一个公共节点的距离相同,再走相同的步数即可在第一个公共节点相遇
时间复杂度
O(m + n)代码(未测试)
链表排序
链表快排
LeetCode/148. 排序链表
问题描述
思路
与数组快排几乎一致,只是 partition 操作需要从左向右遍历;
因为涉及指针,还是用 C++ 写比较方便;
另外 LeetCode 讨论区反映 Python 可能会超时;
时间复杂度:最好
O(NlogN),最坏O(N^2)
代码 1 - 只交换节点内的值
参考数组快排中的写法,这里选取第一个元素作为枢纽
```C++
/**
Definition for singly-linked list.
struct ListNode {
int val;
ListNode *next;
ListNode(int x) : val(x), next(NULL) {}
};
*/
class Solution {
public: ListNode sortList(ListNode head) { if (head == nullptr || head->next == nullptr) return head;
};
链表归并
LeetCode/148. 排序链表
问题描述
思路
用快慢指针的方法找到链表中间节点,然后递归的对两个子链表排序,把两个排好序的子链表合并成一条有序的链表
归并排序比较适合链表,它可以保证了最好和最坏时间复杂度都是
O(NlogN),而且它在数组排序中广受诟病的空间复杂度在链表排序中也从O(n)降到了O(1)因为链表快排中只能使用第一个节点作为枢纽,所以不能保证时间复杂度
还是使用 C++
时间复杂度:最好/最坏
O(NlogN)
C++
链表插入排序
LeetCode/147. 对链表进行插入排序
注意:以下代码在148. 排序链表也能 AC(要求时间复杂度
O(NlogN))
问题描述
插入排序的动画演示如上。从第一个元素开始,该链表可以被认为已经部分排序(用黑色表示)。
每次迭代时,从输入数据中移除一个元素(用红色表示),并原地将其插入到已排好序的链表中。
思路
见代码注释
时间复杂度:最好/最坏
O(N^2)
代码 1 - 非原地
实际上,对链表来说,不存在是否原地的问题,不像数组
这里所谓的非原地是相对数组而言的,因此下面的代码只针对链表,不适用于数组。
代码 2 - 原地
即不使用新链表,逻辑与数组一致;
此时链表拼接的逻辑会复杂一些
链表选择排序
LeetCode/147. 对链表进行插入排序
注意:以下代码在148. 排序链表也能 AC(要求时间复杂度
O(NlogN))
思路
见代码注释
时间复杂度:最好/最坏
O(N^2)
C++
链表冒泡排序
LeetCode/147. 对链表进行插入排序
思路
见代码注释
时间复杂度:最好
O(N),最坏O(N^2)
C++
以下代码不能 AC 148. 排序链表
二维数组
二分查找
搜索二维矩阵 1
LeetCode - 74. 搜索二维矩阵
问题描述
思路
当做一维有序数组二分查找
C++
搜索二维矩阵 2
LeetCode - 240. 搜索二维矩阵 II
思路
1)从右上角开始查找,时间复杂度
O(M+N)2)每一行二分查找,时间复杂度
O(MlogN)
打印二维数组
回形打印
蛇形打印
堆
堆的调整(自上而下)
栈
用两个栈模拟队列
用两个栈实现队列 - 牛客
题目描述
思路
假设
stack_in用于处理入栈操作,stack_out用于处理出栈操作stack_in按栈的方式正常处理入栈数据;关键在于出栈操作
当
stack_out为空时,需要先将每个stack_in中的数据出栈后压入stack_out反之,每次弹出
stack_out栈顶元素即可
Code(C++)
最后更新于
这有帮助吗?