头条面试题

待处理
- 极大似然的原理
- SVM 原理
- L1和L2的区别,以及各自的使用场景
- 为什么梯度是函数变化最快的方向 https://zhuanlan.zhihu.com/p/24913912
去除字符串连续字符
递归的去除字符串中连续的 ac 和 b
#include <iostream>
#include <vector>
#include <stack>
using namespace std;
int main() {
string s {"aaccaptgbcaca"};
if (s.empty()) {
return 0;
}
stack<char> st;
int cursor = 0;
while(cursor < s.size()) {
char ch = s[cursor++];
if (st.empty()) {
st.push(ch);
} else {
if ( ch == 'b') {
continue;
} else if (st.top() == 'a' && ch == 'c') {
st.pop();
} else {
st.push(ch);
}
}
}
vector<char> ch(st.size());
for (int i = st.size()-1; i >= 0; i--) {
ch[i] = st.top();
st.pop();
}
for (auto& e : ch) {
cout << e << " ";
}
return 0;
}
将一个字符串变成数字
#include <iostream>
#include <sstream>
using namespace std;
int main() {
string s = " 1234js9";
stringstream ss {s};
int num;
ss >> num;
cout << num << endl;
return 0;
}
链表加法
leetcode 2
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode(int x) : val(x), next(NULL) {}
* };
*/
class Solution {
public:
ListNode* addTwoNumbers(ListNode* l1, ListNode* l2) {
ListNode dummyHead(-1);
// 凡是这种要构建新链表的, 貌似都要搞一个 dummyHead 比较简单
ListNode *p = &dummyHead;
int partSum = 0;
while (l1 != nullptr || l2 != nullptr) {
if (l1) {
partSum += l1->val;
l1 = l1->next;
}
if (l2) {
partSum += l2->val;
l2 = l2->next;
}
p->next = new ListNode(partSum%10);
p = p->next;
partSum /= 10;
}
if (partSum != 0) p->next = new ListNode(partSum);
return dummyHead.next;
}
};
判断二叉树是不是二叉搜索树
leetcode98. Validate Binary Search Tree
方法 1
中序遍历树, 查看遍历过程中是否是升序
/**
* Definition for a binary tree node.
* struct TreeNode {
* int val;
* TreeNode *left;
* TreeNode *right;
* TreeNode(int x) : val(x), left(NULL), right(NULL) {}
* };
*/
class Solution {
public:
bool isValidBST(TreeNode* root) {
TreeNode* p = root;
if (!root) return true;
stack<TreeNode*> st;
long pre = LONG_MIN;
long cur = LONG_MIN;
bool isValid = true;
while (p || !st.empty()) {
if (p) {
st.push(p);
p = p->left;
} else {
cur = st.top()->val;
if (cur <= pre) {
isValid = false;
break;
}
pre = cur;
p = st.top()->right;
st.pop();
}
}
return isValid;
}
};
方法 2
根节点的值大于左子树的最大值, 小于右子树的最小值,
作为叶子节点来说, 以左叶子节点来说, 左叶子节点要
- 满足父节点的约束;
- 小于其父节点的值;
class Solution {
public:
bool isValidBST(TreeNode* root) {
return isValidBST(root, LONG_MIN, LONG_MAX);
}
bool isValidBST(TreeNode* root, long min_val, long max_val) {
if (!root) return true;
return (root->val > min_val) && (root->val < max_val) &&
(isValidBST(root->left, min_val, root->val)) &&
(isValidBST(root->right, root->val, max_val));
}
};
将二叉搜索树转换成双向链表
递归解法
解法很经典, 好好揣摩.
/*
struct TreeNode {
int val;
struct TreeNode *left;
struct TreeNode *right;
TreeNode(int x) :
val(x), left(NULL), right(NULL) {
}
};*/
#include<iostream>
#include<stack>
using namespace std;
class Solution {
public:
TreeNode* res;
TreeNode* Convert(TreeNode* pRootOfTree)
{
if(pRootOfTree == nullptr) return nullptr;
TreeNode* pre = nullptr;
convertHelper(pRootOfTree, pre);
return res;
}
// *& 很重要, 必须使用引用, 否则当前 pre 并没有更新到最新位置
void convertHelper(TreeNode* cur, TreeNode*& pre)
{
if(cur == nullptr) return;
convertHelper(cur->left, pre);
cur->left = pre;
if(pre) {
pre->right = cur; // 用于处理链表的起始位置
} else {
res = cur;
}
pre = cur;
convertHelper(cur->right, pre);
}
};
非递归解法
/*
struct TreeNode {
int val;
struct TreeNode *left;
struct TreeNode *right;
TreeNode(int x) :
val(x), left(NULL), right(NULL) {
}
};*/
#include<iostream>
#include<stack>
using namespace std;
class Solution {
public:
TreeNode* Convert(TreeNode* pRootOfTree) {
if (!pRootOfTree) {
return nullptr;
}
TreeNode DummyHead(-1);
TreeNode *dp = &DummyHead;
TreeNode *p = pRootOfTree;
stack<TreeNode*> st;
while (p || !st.empty()) {
if (p) {
st.push(p);
p = p->left;
} else {
p = st.top();
st.pop();
dp->right = p;
p->left = dp;
dp = p;
p = p->right;
}
}
DummyHead.right->left = nullptr;
return DummyHead.right;
}
};
他人秋招三面通过面经
用了同学的白金内推码,所以直接进入了面试,全程都在写题!机器学习的问题非常少!
一面:
1、介绍项目
2、强化学习PG的推导
3、强化学习DQN,DDQN,AC,DDPG的区别
4、n个[0,n)的数,求每个数的出现次数(不能开辟额外空间)
5、K个有序数组,找一个长度最小的区间,在这个区间里至少包含每个数组各一个数。
分析:初始化带下为K的最小堆,K个数字是每个数组中的最小值,设置变量max记录k个数字中的最大值,删除堆顶元素,将原堆顶元素对应的数组中下一个元素加入到堆中,调整堆,并且记录当前区间范围为(max-min),重复执行直到某个数组所有值都被删除。
二面
1、介绍DQN的项目
2、数组的全排列(空间复杂度O(1))
数组中有重复元素,所以我们需要一个Set保存已经出现过的排列。因此我先写了一个回溯的方法,可是空间复杂度比较高,面试官说能不能用O(1)的空间复杂度,全排列直接print出来就行。即我们不需要保存已经出现过什么排列。这需要对数组先进性排序。
3、两堆钞票,尽可能均分(利用背包问题的思想)
想了半天,写出来一个深度优先搜索的算法。面试官提示我可以考虑从背包问题的角度出发,但最后也没想出来。
三面:
1、无向无环图中,最短路径的最大值(O(n^3)的解法)
这里考察的其实就是Floyd算法。哎,只可惜自己当时没有复习图的相关算法,完全不会写呀。
算法思想原理:Floyd算法是一个经典的动态规划算法。用通俗的语言来描述的话,首先我们的目标是寻找从点i到点j的最短路径。
从任意节点i到任意节点j的最短路径不外乎2种可能,1是直接从i到j,2是从i经过若干个节点k到j。所以,我们假设Dis(i,j)为节点u到节点v的最短路径的距离,对于每一个节点k,我们检查Dis(i,k) + Dis(k,j) < Dis(i,j)是否成立,如果成立,证明从i到k再到j的路径比i直接到j的路径短,我们便设置Dis(i,j) = Dis(i,k) + Dis(k,j),这样一来,当我们遍历完所有节点k,Dis(i,j)中记录的便是i到j的最短路径的距离。
2、LSTM的公式
3、RNN为什么出现梯度消失
4、BPTT的推导。