每次面试前我都要重新回顾下题型,找下手感。我觉得基础和经典题型必须会写等等,面试时候沉着应对,一步步思考就可以。
以下内容仅供个人复习参考。
首先是最基本的数据结构:
- 数组和字符串(顺序存储)
涉及两大类问题:
查找:1)顺序查找;2)二分查找;3)哈希查找;
排序:八大基础排序
常用技巧:
二分:有序数组,数组元素不重复(因为数组元素一重复返回的元素下标不唯一)或部分有序可采用二分变种。
双指针法(做题下来,感觉可以分为头尾指针or快慢指针,注意下两者的循环终止条件,理解头或尾,快或慢分别代表什么就可以了)。
快慢指针主要是右指针指向待交换的元素,左指针指向插入位置。
滑动窗口(其实是高级点的双指针,可以解决很多子字符串匹配和子数组问题)。其中,滑动窗口的整体思想如下:
int right =0,left =0;
while(right<s.size()){
window.add(s[right]);
right++;
while(valid){
window.remove(s[left]);
left++;
}
}
哈希法,用来快速判断一个元素是否在集合中。
c++的话就涉及set
、unordered_set
和multiset
,map
、unordered_map
和multimap
方法函数的熟练使用。数组本身就可以当作一种哈希表(可用于字符串的情况)。但如果哈希值比较少、特别分散、跨度非常大,使用数组就造成空间的极大浪费,此时可以考虑set或map。
- 链表(链式存储)
包括单链表、双链表、循环链表(可以解决约瑟夫环问题)。
c++版链表定义:
struct ListNode{
int val;
ListNode *next;
ListNode() : val(0), next(nullptr) {}
ListNode(int x) : val(x), next(nullptr) {}
ListNode(int x, ListNode *next) : val(x), next(next) {}
};
python版本链表定义:
class ListNode:
def __init__(self,val=0,next=None):
self.val = val
self.next = next
注意删除头节点的逻辑,可以设置虚拟头节点。
然后是高阶的数据结构:
-
栈和队列
在C++中,stack和queue都是STL中的数据结构。
在Python中,list充当栈,append入栈,pop出栈。from collections import deque
中用deque创建队列,append入队,popleft出队。
其中,栈善于解决匹配类问题。此外还有单调队列,单调栈等特殊数据结构。 -
二叉树
🌲的分类`:
满二叉树指如果一棵二叉树只有度为0的结点和度为2的结点,并且度为0的结点在同一层上,则这棵二叉树为满二叉树。(深度为k,节点数为 2 k − 1 2^k-1 2k−1。
完全二叉树指除了最底层节点可能没填满外,其余每层节点数都达到最大值,并且最下面一层的节点都集中在该层最左边的若干位置。若最底层为第 k 层,则该层包含 1 至 2 k − 1 1至 2^{k -1} 1至2k−1 个节点。
二叉搜索树指若它的左子树不空,则左子树上所有结点的值均小于它的根结点的值;若它的右子树不空,则右子树上所有结点的值均大于它的根结点的值;它的左、右子树也分别为二叉排序树。
平衡二叉搜索树又被称为AVL(Adelson-Velsky and Landis)树,且具有以下性质:它是一棵空树或它的左右两个子树的高度差的绝对值不超过1,并且左右两个子树都是一棵平衡二叉树。
🌲的存储方式:
二叉树可以链式存储,也可以顺序存储。链式存储用指针,顺序存储用数组。顾名思义就是顺序存储的元素在内存是连续分布的,而链式存储则是通过指针把分布在散落在各个地址的节点串联一起。
struct TreeNode {
int val;
TreeNode *left;
TreeNode *right;
TreeNode(int x) : val(x), left(NULL), right(NULL) {}
};
如果父节点的数组下表是i,那么它的左孩子就是i * 2 + 1,右孩子就是 i * 2 + 2。
🌲的遍历方式:
深度优先遍历:前序遍历,中序遍历,后序遍历(递归法和迭代法)
广度优先遍历:层序遍历(迭代法)
具体见二叉树的七种遍历。
🌲的构造:
前序和中序可以唯一确定一颗二叉树,后序和中序可以唯一确定一颗二叉树,而前序和后序不能唯一确定一颗二叉树!因为没有中序遍历无法确定左右部分,也就是无法分割。
————————————————————————
下面重点说下二叉搜索树是一个有序树:
若它的左子树不空,则左子树上所有结点的值均小于它的根结点的值;
若它的右子树不空,则右子树上所有结点的值均大于它的根结点的值;
它的左、右子树也分别为二叉搜索树
对于二叉搜索树比如求最值,求差值等,要利用好其特性——大多是二叉搜索树的题目,其实都离不开中序遍历,因为这样就是有序的。
- 回溯
result = []
def backtrack(路径, 选择列表):
if 满足结束条件:
result.add(路径)
return
for 选择 in 选择列表:
做选择
backtrack(路径, 选择列表)
撤销选择