模板整理
整理遇到的几个比较难记的算法的模板。
KMP算法
KMP算法是字符串匹配算法,通常是在长字符串中对短字符串(模式串)进行匹配。
使用next数组对模式串的前缀表进行记录。
前缀表:当匹配失败时,将根据这个前缀表决定指针的位置。前缀表的目的是找到模式串中相等的前缀和后缀。
例如aabaaf,对于i=1, 模式串为aa,前缀和后缀都是a,因此next中存储的是1。
这个过程类似自己跟自己匹配。 前缀部分的next是我们先得到的。因此在用后缀跟前缀匹配的时候,一旦匹配失败,我们可以得出,当前后缀的前缀跟前缀的前缀是相同的。
比如 abadxxxxabab,next=0 0 1 0 xxxx 1 2 3,当我们计算最后一个b的时候匹配失败了。
此时我们要找到能够匹配aba的后缀的整个字符串的前缀。我们已经知道aba后缀对应相等的前缀了。其实就是next[i-1],也就是当前的j=3.
那么这个值除了已经匹配失败的j=3,还有前缀aba对应的next,也满足这个要求。通过不断的往前进行搜索,得到结果。可以把这个过程看成一棵树。
在haystack中搜索模式串的原理和上面是差不多的。
class Solution {
void getNext(int *next,string needle){
int n=needle.size();
int j=0;
next[0] = j;
for(int i=1;i<n;++i){
while(j>0&&needle[j]!=needle[i]){
j=next[j-1];
}
if(needle[j]==needle[i]){
j++;
}
next[i]=j;
}
}
public:
int strStr(string haystack, string needle) {
int n=haystack.size(), m=needle.size();
int next[m];
getNext(next, needle);
int i=0,j=0;
for(int i=0;i<n;++i){
while(j>0&&needle[j]!=haystack[i]){
j=next[j-1];
}
if(haystack[i]==needle[j]){
j++;
}
if(m==j){
return (i - m + 1);
}
}
return -1;
}
};
在next查找的时候,我们需要使next[j-1]来查找对应的前缀。因此可以把next数组统一减一,相当于把数组右移了,因此访问的时候可以直接用next[j].但是很多地方就要改成j+1;
写法:
class Solution {
void getNext(int *next,string needle){
int n=needle.size();
int j=-1;
next[0] = j;
for(int i=1;i<n;++i){
while(j>=0&&needle[j+1]!=needle[i]){
j=next[j];
}
if(needle[j+1]==needle[i]){
j++;
}
next[i]=j;
}
}
public:
int strStr(string haystack, string needle) {
int n=haystack.size(), m=needle.size();
int next[m];
getNext(next, needle);
int i=0,j=-1;
for(int i=0;i<n;++i){
while(j>=0&&needle[j+1]!=haystack[i]){
j=next[j];
}
if(haystack[i]==needle[j+1]){
j++;
}
if(m==j+1){
return (i - m + 1);
}
}
return -1;
}
};
二叉树
二叉树的数据结构
struct TreeNode {
int val;
TreeNode *left;
TreeNode *right;
TreeNode() : val(0), left(nullptr), right(nullptr) {}
TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {}
};
二叉树常见的遍历:前序遍历(中左右)、中序遍历(左中右)、后序遍历(左右中)、层序遍历
递归遍历
用递归的方法遍历二叉树
前序遍历
class Solution {
public:
void traversal(TreeNode* root, vector<int>& res){
if(root==nullptr)return;
res.push_back(root->val);
traversal(root->left, res);
traversal(root->right, res);
}
vector<int> preorderTraversal(TreeNode* root) {
vector<int> res;
traversal(root, res);
return res;
}
};
中序遍历
class Solution {
public:
void traversal(TreeNode* root, vector<int>& res){
if(root==nullptr)return;
traversal(root->left, res);
res.push_back(root->val);
traversal(root->right, res);
}
vector<int> inorderTraversal(TreeNode* root) {
vector<int>res;
traversal(root, res);
return res;
}
};
后序遍历
class Solution {
void traversal(TreeNode* root, vector<int>& res){
if(root==nullptr)return;
traversal(root->left, res);
traversal(root->right, res);
res.push_back(root->val);
}
public:
vector<int> postorderTraversal(TreeNode* root) {
vector<int>res;
traversal(root, res);
return res;
}
};
迭代遍历
需要借助栈来保存。
前序遍历
使用栈来模拟整个过程.先把右指针进栈再进栈左指针,在出栈时取值
class Solution {
public:
vector<int> preorderTraversal(TreeNode* root) {
if(root==nullptr)return {};
vector<int> res;
stack<TreeNode*>nodes;
nodes.push(root);
while(!nodes.empty()){
TreeNode* cur=nodes.top();
nodes.pop();
res.push_back(cur->val);
if(cur->right != nullptr)nodes.push(cur->right);
if(cur->left != nullptr)nodes.push(cur->left);
}
return res;
}
};
或者,按照其一直往左走的特点实现在进栈时取值
class Solution {
public:
vector<int> preorderTraversal(TreeNode* root) {
stack<TreeNode*> st;
TreeNode* cur = root;
vector<int> v;
while(cur || !st.empty())
{
//开始访问一棵树
//1,左路节点
//2,左路节点的右子树
while(cur)
{
v.push_back(cur->val);
st.push(cur);
cur=cur->left;
}
//开始访问右子树
TreeNode* top = st.top();
st.pop();
//子问题访问右子树
cur = top->right;
}
return v;
}
};
中序遍历
只有节点的左子树都被访问过后,才将节点的值输出。
class Solution {
public:
vector<int> inorderTraversal(TreeNode* root) {
stack<TreeNode*>st;
vector<int>res;
TreeNode* cur=root;
while(cur||!st.empty()){
while(cur){
st.push(cur);
cur=cur->left;
}
TreeNode* node = st.top();
st.pop();
res.push_back(node->val);
cur = node->right;
}
return res;
}
};
后序遍历
左右中反过来就是中右左,因此把前序遍历的访问顺序翻转,再反过来就行了
class Solution {
public:
vector<int> postorderTraversal(TreeNode* root) {
stack<TreeNode*> st;
vector<int> result;
if (root == NULL) return result;
st.push(root);
while (!st.empty()) {
TreeNode* node = st.top();
st.pop();
result.push_back(node->val);
if (node->left) st.push(node->left); // 相对于前序遍历,这更改一下入栈顺序 (空节点不入栈)
if (node->right) st.push(node->right); // 空节点不入栈
}
reverse(result.begin(), result.end()); // 将结果反转之后就是左右中的顺序了
return result;
}
};
统一的迭代法
在栈中加入NULL节点,表示上一个入栈的节点的左右节点是被访问过的。通过这种方法,可以把三种遍历统一起来。依旧是出栈的时候, 如果是NULL加入答案
前序遍历
class Solution {
public:
vector<int> preorderTraversal(TreeNode* root) {
stack<TreeNode*> st;
TreeNode* cur = root;
if(root!=nullptr)st.push(root);
vector<int> res;
while(!st.empty()){
cur = st.top();
st.pop();
if(cur!=nullptr){
if(cur->right)st.push(cur->right); //记得判断,否则会跟空节点混淆
if(cur->left)st.push(cur->left);
st.push(cur);
st.push(nullptr);
}else{
cur = st.top();
st.pop();
res.push_back(cur->val);
}
}
return res;
}
};
中序遍历
class Solution {
public:
vector<int> inorderTraversal(TreeNode* root) {
stack<TreeNode*> st;
TreeNode* cur = root;
if(root!=nullptr)st.push(root);
vector<int> res;
while(!st.empty()){
cur = st.top();
st.pop();
if(cur!=nullptr){
if(cur->right)st.push(cur->right);
st.push(cur);
st.push(nullptr);
if(cur->left)st.push(cur->left);
}else{
cur = st.top();
st.pop();
res.push_back(cur->val);
}
}
return res;
}
};
后序遍历
class Solution {
public:
vector<int> postorderTraversal(TreeNode* root) {
stack<TreeNode*> st;
TreeNode* cur = root;
if(root!=nullptr)st.push(root);
vector<int> res;
while(!st.empty()){
cur = st.top();
st.pop();
if(cur!=nullptr){
st.push(cur);
st.push(nullptr);
if(cur->right)st.push(cur->right);
if(cur->left)st.push(cur->left);
}else{
cur = st.top();
st.pop();
res.push_back(cur->val);
}
}
return res;
}
};
层序遍历
迭代方法
class Solution {
public:
vector<vector<int>> levelOrder(TreeNode* root) {
queue<TreeNode*>q;
if(root)q.push(root);
vector<vector<int>> res;
while(!q.empty()){
int n=q.size();
vector<int> layer;
while(n--){
TreeNode* tmp = q.front();
q.pop();
if(tmp->left) q.push(tmp->left);
if(tmp->right) q.push(tmp->right);
layer.push_back(tmp->val);
}
res.push_back(layer);
}
return res;
}
};
递归方法
传入的res必须使用引用
class Solution {
public:
void order(TreeNode* root, vector<vector<int>>& res, int depth){
if(root == nullptr)return;
if(depth+1>res.size()){
res.push_back({});
}
res[depth].push_back(root->val);
order(root->left, res, depth+1);
order(root->right, res, depth+1);
}
vector<vector<int>> levelOrder(TreeNode* root) {
vector<vector<int>> res;
order(root, res, 0);
return res;
}
};
计算模板
有时候会遇到一些计算题。记录一些公式
等差数列 an+1 = an + d, an=a1+(n-1)d, S(n)=na1+n(n-1)d/2=(a1+an)n/2
等比数列 an = a1q^(n-1), S(n) = a1(1-q^n)/(1-q)=(a1-anq)/(1-q);特殊情况:二叉树中,一般q=2, Sn=a1(q^n-1);