柠檬微趣C++笔试复盘
有些题目是经典题目改版后的题,没有原题了,就只回忆记录下当时的做法。
1.二叉树的最浅叶子结点
- 使用宽度优先搜索来进行遍历,宽搜到的叶子结点一定是距离最浅的叶子结点。然后把这一层的叶子结点全部取出来就得到了所有最浅的叶子结点的和。
利用两个指针front和last来维护每一层的所有节点。
#include <stdio.h>
#include <string.h>
#include <assert.h>
#include <malloc.h>
#define maxlen 30
typedef struct Node
{
struct Node * leftChild;
struct Node * rightChild;
int key;
}Node;
Node *initTree()
{
int n = 0;
scanf("%d",&n);
if(0 == n){
return NULL;
}
Node *p = (Node *)malloc(sizeof(Node));
p->key = n;
p->leftChild = initTree();
p->rightChild = initTree();
return p;
}
void preOrderRecursive(Node *root)
{
if(root){
printf("%d\n",root->key);
preOrderRecursive(root->leftChild);
preOrderRecursive(root->rightChild);
}
}
int getResult(Node *pTree)
{
Node *queue[maxlen];
int front = 0;
int rear = 0;
int count = 0;
queue[(++rear)%maxlen] = pTree;
int last = 1;
while(front != rear){
Node *tmp = queue[(++front)%maxlen];
if(tmp->leftChild == NULL && tmp->rightChild == NULL){
count += tmp->key;
while(front != last){
tmp = queue[(++front)%maxlen];
if(tmp->leftChild == NULL && tmp->rightChild == NULL){
count += tmp->key;
}
}
return count;
}
if(tmp->leftChild){
queue[(++rear)%maxlen] = tmp->leftChild;
}
if(tmp->rightChild){
queue[(++rear)%maxlen] = tmp->rightChild;
}
if(front == last){
last = rear;
}
}
return count;
}
/*
输入的先序遍历序列为:
1 5 22 0 0 14 0 31 0 0 9 8 0 98 0 0 7 0 0
*/
int main()
{
Node *root = initTree();
printf("先序遍历结果:\n");
preOrderRecursive(root);
printf("result = %d\n",getResult(root));
}
题目中给出了基本的输入输出和数据结构,自己只需要补足一个getReuslt()函数就可以。
2.实现简单的正则表达式
-这个力扣上有一个原题,不过这个题是 相对于力扣上的题多了一个条件:
'?‘表示可以匹配前一个字符1到n次,和’*‘不同的是,’*'表示可以匹配前一个字符0到n次。
状态转移方程如下:
如果p[j]!='*'&&p[j]!='?'
f[i][j]=f[i-1][j-1]&&(s[i]==p[j]||p[j]=='.')
如果p[j]=='*'
先放结论:
f[i][j]=f[i][j-2]||i&&f[i-1][j]&&(s[i]==p[j-1]||p[j-1]=='.'
这里前面的比较容易理解,当'*'直接表示0个字符时,f[i][j]=f[i][j-2]
后面的这里不是特别容易理解,很多题解省略了一步推导
当'*'表示一个字符时, f[i][j]=f[i-1][j-2]&&s[i]==p[j-1]
当'*'表示两个字符时, f[i][j]=f[i-2][j-2]&&s[i-1]=s[i]=p[j-1]
当'*'表示三个字符时,f[i][j]=f[i-3][j-2]&&s[i-2]=s[i-1]=s[i]=p[j-2]
...
依次类推,我们是要枚举'*'可以表示几个状态吗?
这样的话,时间复杂度是o(n^3)的
我们不妨写出f[i-1][j]来观察规律:
当'*'表示0个字符的时候,f[i-1][j]=f[i-1][j-2]
当'*'表示一个字符时,f[i-1][j]=f[i-2][j-2]&&s[i-1]=p[j-1]
当'*'表示两个字符时,f[i-1][j]=f[i-3][j-2]&&s[i-2]=s[i-1]=p[j-1]
观察上述式子,可以看到f[i][j]可以由f[i-1][j]巧妙的转换
由此得出 f[i][j]=f[i][j-2]||i&&f[i-1][j]&&(s[i]==p[j-1]||p[j-1]=='.'
如果p[j]=='?'
'?'的意思与'*'的不同是'*'能表示0个字符,而'?'至少表示1个字符
按照上面的思路可以写出以下:
当'?'表示一个字符时, f[i][j]=f[i-1][j-2]&&s[i]==p[j-1]
当'?'表示两个字符时, f[i][j]=f[i-2][j-2]&&s[i-1]=s[i]=p[j-1]
当'?'表示三个字符时,f[i][j]=f[i-3][j-2]&&s[i-2]=s[i-1]=s[i]=p[j-2]
我们不妨写出f[i-1][j]来观察规律:
当'?'表示一个字符时,f[i-1][j]=f[i-2][j-2]&&s[i-1]=p[j-1]
当'?'表示两个字符时,f[i-1][j]=f[i-3][j-2]&&s[i-2]=s[i-1]=p[j-1]
当'?'表示三个字符时,f[i-1][j]=f[i-4][j-2]&&s[i-3]=s[i-2]=s[i-2]=p[j-1]
由此得出结论,用f[i-1][j]代替后面的一堆东西得到:
f[i][j]=f[i-1][j]||(i&&f[i-1][j-2]&&(s[i]==p[j-1]||p[j-1]=='.'));
总的代码如下:
class Solution {
public:
bool isMatch(string s, string p) {
int n = s.size(), m = p.size();
s = ' ' + s, p = ' ' + p;
vector<vector<bool>> f(n + 1, vector<bool>(m + 1));
f[0][0] = true;
for (int i = 0; i <= n; i ++ )
for (int j = 1; j <= m; j ++ ) {
if (j + 1 <= m && p[j + 1] == '*') continue;
if (i && p[j] != '*'&&p[j]!='?') {
f[i][j] = f[i - 1][j - 1] && (s[i] == p[j] || p[j] == '.');
} else if (p[j] == '*') {
f[i][j] = f[i][j - 2] || i && f[i - 1][j] && (s[i] == p[j - 1] || p[j - 1] == '.');
}else if(p[j]=='?')
{
f[i][j]=f[i-1][j]||(i&&f[i-1][j-2]&&(s[i]==p[j-1]||p[j-1]=='.'));
}
}
return f[n][m];
}
};