柠檬微趣C++笔试复盘

本文回顾了一次C++笔试,重点讨论了如何找到二叉树最浅的叶子结点以及实现正则表达式的匹配算法。对于二叉树问题,采用宽度优先搜索策略获取最浅叶子结点;在正则表达式匹配中,通过状态转移方程处理'*'和'?'两种特殊字符的情况,实现了匹配算法。

柠檬微趣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];
    }
};

### 柠檬社招笔试题目相关信息 柠檬是一家专注于游戏开发的公司,其社会招聘笔试题目通常涉及算法设计、数据结构应用以及编程语言的实际操作能力。以下是对可能涉及的 `unordered_map` 相关问题的详细分析: #### 1. `unordered_map` 的基本使用与性能优化 在实际笔试中,可能会考察对 `unordered_map` 的理解和优化能力。例如,如何通过调整负载因子和预分配桶数量来提高性能。 ```cpp std::unordered_map<int, std::string> data; data.reserve(1000); // 预分配空间以减少重新哈希的次数[^1] data.max_load_factor(0.5); // 设置较低的负载因子以减少冲突 ``` #### 2. 自定义键类型的哈希函数实现 笔试中也可能要求为自定义类型实现哈希函数。以下是一个示例,展示如何为一个简单的结构体定义哈希函数。 ```cpp struct Point { int x, y; bool operator==(const Point& p) const { return x == p.x && y == p.y; } }; namespace std { template<> struct hash<Point> { size_t operator()(const Point& p) const { return hash<int>()(p.x) ^ (hash<int>()(p.y) << 1); } }; } int main() { std::unordered_map<Point, std::string> pointMap; pointMap[{1, 2}] = "A"; pointMap[{3, 4}] = "B"; } ``` #### 3. 处理哈希冲突的策略 笔试题可能涉及对哈希冲突处理的理解。`unordered_map` 默认使用链地址法解决冲突,但考生需要了解其工作原理以及潜在的性能瓶颈。 - 当哈希表中的元素过多时,冲突会导致查找时间退化为 $O(n)$。 - 可以通过调整 `max_load_factor` 和调用 `rehash` 来优化性能[^2]。 #### 4. 实际应用场景模拟 笔试中可能会给出一个具体的应用场景,要求使用 `unordered_map` 设计解决方案。例如,统计一组字符串中每个单词出现的频率。 ```cpp #include <iostream> #include <unordered_map> #include <vector> #include <sstream> std::unordered_map<std::string, int> countWords(const std::vector<std::string>& words) { std::unordered_map<std::string, int> wordCount; for (const auto& word : words) { wordCount[word]++; } return wordCount; } int main() { std::vector<std::string> words = {"apple", "banana", "apple", "orange", "banana", "apple"}; auto result = countWords(words); for (const auto& [word, count] : result) { std::cout << word << ": " << count << "\n"; } } ``` #### 5. 哈希表与其他数据结构的对比 笔试中可能还会考察对不同数据结构的选择依据。例如,`unordered_map` 和 `map` 的区别在于底层实现和性能特点[^3]。 | 特性 | `unordered_map` | `map` | |--------------|--------------------------|------------------------| | **底层结构** | 哈希表 | 红黑树 | | **查找速度** | 平均 $O(1)$,最坏 $O(n)$ | 稳定 $O(\log n)$ | | **内存占用** | 较高(桶数组+链表) | 较低 | | **元素顺序** | 无序 | 按键排序(升序) | #### 6. 常见面试题解析 以下是几个可能出现在柠檬社招笔试中的题目及其解答: ##### 题目 1:如何判断两个 `unordered_map` 是否相等? ```cpp bool areMapsEqual(const std::unordered_map<int, int>& m1, const std::unordered_map<int, int>& m2) { if (m1.size() != m2.size()) return false; for (const auto& [key, value] : m1) { if (m2.find(key) == m2.end() || m2.at(key) != value) return false; } return true; } ``` ##### 题目 2:如何高效地合并两个 `unordered_map`? ```cpp void mergeMaps(std::unordered_map<int, int>& m1, const std::unordered_map<int, int>& m2) { for (const auto& [key, value] : m2) { m1[key] += value; } } ``` ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值