如今不论是校招还是社招,大多数公司都会有笔试+面试的算法题,以此来考察候选人的数据结构和算法能力,因此我们面试前最好复习下算法,简单来说就是刷题呗!
以下是本人社招时在Leetcode和牛客网上的大厂的高频题,大概二三百道,此系列只列出最热门的一百来道,代码都是Leetcode上的,可以正常运行。大家可以根据下面推荐的题目来有选择的刷题,最好是进入Leetcode或牛客来刷,里面有许多优秀解法可以参考!
常见算法有背包、DFS、BFS、动态规划、数组、状态压缩、图优化、数学推导、字符串、链表二叉树、邻接表、图优化等等。
下面是正常的题目,大家可以参考一下:
//2021.04.28
1、全排列II----有重复数字的全排列数组
class Solution {
private:
vector<vector<int>> result;
vector<int> path;
void backtracking (vector<int>& nums, vector<bool>& used) {
// 此时说明找到了一组
if (path.size() == nums.size()) {
result.push_back(path);
return;
}
for (int i = 0; i < nums.size(); i++) {
// used[i - 1] == true,说明同一树支nums[i - 1]使用过
// used[i - 1] == false,说明同一树层nums[i - 1]使用过
// 如果同一树层nums[i - 1]使用过则直接跳过
if (i > 0 && nums[i] == nums[i - 1] && used[i - 1] == false) {
continue;
}
if (used[i] == false) {
used[i] = true;
path.push_back(nums[i]);
backtracking(nums, used);
path.pop_back();
used[i] = false;
}
}
}
public:
vector<vector<int>> permuteUnique(vector<int>& nums) {
result.clear();
path.clear();
sort(nums.begin(), nums.end()); // 排序
vector<bool> used(nums.size(), false);
backtracking(nums, used);
return result;
}
};
作者:carlsun-2
链接:https://leetcode-cn.com/problems/permutations-ii/solution/47-quan-pai-lie-iiche-di-li-jie-pai-lie-zhong-de-q/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
2、滑动窗口最大值
class Solution {
public:
vector<int> maxSlidingWindow(vector<int>& nums, int k) {
int n = nums.size();
deque<int> q;
for (int i = 0; i < k; ++i) {
while (!q.empty() && nums[i] >= nums[q.back()]) {
q.pop_back();
}
q.push_back(i);
}
vector<int> ans = {nums[q.front()]};
for (int i = k; i < n; ++i) {
while (!q.empty() && nums[i] >= nums[q.back()]) {
q.pop_back();
}
q.push_back(i);
while (q.front() <= i - k) {
q.pop_front();
}
ans.push_back(nums[q.front()]);
}
return ans;
}
};
作者:LeetCode-Solution
链接:https://leetcode-cn.com/problems/sliding-window-maximum/solution/hua-dong-chuang-kou-zui-da-zhi-by-leetco-ki6m/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
3、加起来和为目标值的组合——多种都可以
class Solution {
public:
vector<vector<int> > combinationSum2(vector<int> &num, int target) {
vector<vector<int> > res;
vector<int> tmp;
if (num.empty()) return res;
sort(num.begin(), num.end());
dfs(num, target, res, tmp, 0);
return res;
}
void dfs(vector<int> &num, int target, vector<vector<int> > &res, vector<int> &tmp, int start) {
if (target == 0) {
res.push_back(tmp);
return;
}
if (start >= num.size()) return;
for (int i = start; i < num.size(); ++i) {
if (i > start && num[i] == num[i-1]) continue; // 去重
if (num[i] <= target) { // 剪枝
tmp.push_back(num[i]);
dfs(num, target - num[i], res, tmp, i + 1);
tmp.pop_back();
}
}
}
};
https://www.nowcoder.com/practice/75e6cd5b85ab41c6a7c43359a74e869a?tpId=117&tqId=37742&rp=1&ru=%2Fta%2Fjob-code-high&qru=%2Fta%2Fjob-code-high%2Fquestion-ranking&tab=answerKey
4、最长有效括号
class Solution {
public:
int longestValidParentheses(string s) {
int left = 0, right = 0, maxlength = 0;
for (int i = 0; i < s.length(); i++) {
if (s[i] == '(') {
left++;
} else {
right++;
}
if (left == right) {
maxlength = max(maxlength, 2 * right);
} else if (right > left) {
left = right = 0;
}
}
left = right = 0;
for (int i = (int)s.length() - 1; i >= 0; i--) {
if (s[i] == '(') {
left++;
} else {
right++;
}
if (left == right) {
maxlength = max(maxlength, 2 * left);
} else if (left > right) {
left = right = 0;
}
}
return maxlength;
}
};
作者:LeetCode-Solution
链接:https://leetcode-cn.com/problems/longest-valid-parentheses/solution/zui-chang-you-xiao-gua-hao-by-leetcode-solution/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
//2021.05.07
5、丢棋子问题
https://www.nowcoder.com/practice/d1418aaa147a4cb394c3c3efc4302266?tpId=117&tqId=37844&rp=1&ru=%2Fta%2Fjob-code-high&qru=%2Fta%2Fjob-code-high%2Fquestion-ranking&tab=answerKey
https://www.cnblogs.com/willwuss/p/12256475.html
/*
设P(N,K)的返回值时N层楼时有K个棋子在最差的情况下仍的最少次数。
(1)如果N==0,棋子在第0层肯定不会碎,所以P(0, K) = 0;
(2)如果K==1,楼层有N层,只有1个棋子,故只能从第一次开始尝试,P(N,1)=N;
(3)对于N>0且K>1, 我们需考虑第1个棋子是从那层开始仍的。如果第1个棋子从第i层开始仍,那么有以下两种情况:
1) 碎了。没必要尝试第i层以上的楼层了,接下来的问题就变成了剩下i-1层楼和K-1个棋子,所以总步数为 1+P(i-1, K-1);
2)没碎。 那么可以知道没必要尝试第i层及其以下的楼层了,接下来的问题就变成了还剩下N-i层和K个棋子,所以总步数为 1+P(N-i, K).
(4)根据题意应该选取(1)和(2)中较差的那种情况,即 max{ P(i-1, K-1), P(N-i, K)}+1。 由于i的取值范围是 1到N,
那么步数最少的情况为, P(N, K)=min{ max{P(i-1, K-1), P(N-i, K)}(1<=i<=N) }+1。
通过研究以上递归函数发现, P(N, K)过程依赖于P(0...N-1, K-1) 和 P(0...N-1, K)。所以,若把所有的递归的返回值看作是一个二维数组,可以用动态规划的方法优化递归过程。从而减少计算量。
dp[0][K] = 0, dp[N][1] = N, dp[N][K] = min( max(dp[i-1][K-1], dp[N-i][K])) + 1。
*/
(1)时间复杂度 O(N^2 * K)
public int solutionTwo(int N, int K){
if ( N<1 || K<1 )
return 0;
if ( K == 1 ) return N;
int[][] dp = new int[N+1][K+1];
for(int i=1; i<dp.length; ++i) {
dp[i][1] = i;
}
for(int i=1; i<dp.length; ++i) {
for(int j=2; j<=K; ++j) {
int min = Integer.MAX_VALUE;
for(int k=1; k<i+1; ++k) {
min = Math.min(min, Math.max(dp[k-1][j-1], dp[i-k][j]));
}
dp[i][j] = min + 1;
}
}
return dp[N][K];
}
(2)map[i][j]的意义为i个棋子仍j次最多搞定的楼层数
/*
通过研究map表发现,第一排的值从左到有一次为1,2,3...,第一纵列都为0, 初次之外的其他位置(i, j),都有 map[i][j] == map[i][j-1] + map[i-1][j-1] + 1.
将设i个棋子仍j次最多搞定m层楼,“搞定最多”说明每次仍的位置都是最优的且在棋子肯定够用的情况下,若第1个棋子仍在a层楼是最优的。
1. 如果第1个棋子以碎,那么就向下,看i-1个棋子扔j-1次最多搞定多少层楼;
2. 如果第1个棋子没碎,那么就向上,看i个棋子扔j-1次最多搞定多少层楼;
3. a层楼本身也是被搞定的1层;
1、2、3的总楼数就是i个棋子扔j次最多搞定的楼数,map表的生成过程极为简单,同时数值增长的极快。原始问题可以通过map表得到很好的解决。
*/
6、最长连续序列
class Solution {
public:
int longestConsecutive(vector<int>& nums) {
unordered_set<int> num_set;
for (const int& num : nums) {
num_set.insert(num);
}
int longestStreak = 0;
for (const int& num : num_set) {
if (!num_set.count(num - 1)) {
int currentNum = num;
int currentStreak = 1;
while (num_set.count(currentNum + 1)) {
currentNum += 1;
currentStreak += 1;
}
longestStreak = max(longestStreak, currentStreak);
}
}
return longestStreak;
}
};
//2021.05.10
7、二叉树的直径
class Solution {
public:
int res=0;//答案
int dfs(TreeNode* p,int d){
if(!p) return 0;//结束递归的条件
int dl=dfs(p->left,d+1);//该节点左子树高度
int dr=dfs(p->right,d+1);//该节点右子树高度
res=max(res,dr+dl);//此次递归该做的事
return max(dl,dr)+1;//此次递归返回的该节点高度
}
int diameterOfBinaryTree(TreeNode* root) {
dfs(root,0);
return res;
}
}
8、最大数(一个数组中的数字进行拼接,返回一个数值最大的字符串)
class Solution {
public:
string largestNumber(vector<int>& nums) {
vector<string> str;
for(auto i : nums) {
str.push_back(to_string(i));
}
// 使用 lambda 比较 elements.
auto cmp = [](string left, string right) {
return left + right > right + left;
};
sort(str.begin(),str.end(), cmp);
stringstream ss;
for(auto c : str) {
ss << c;
}
string ans = ss.str();
if(ans[0] == '0'){
return "0";
}
return ans;
}
};
作者:AC_OIer
链接:https://leetcode-cn.com/problems/largest-number/solution/gong-shui-san-xie-noxiang-xin-ke-xue-xi-vn86e/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
9、大数乘法——返回字符串
https://www.nowcoder.com/practice/c4c488d4d40d4c4e9824c3650f7d5571?tpId=117&tqId=37843&rp=1&ru=%2Fta%2Fjob-code-high&qru=%2Fta%2Fjob-code-high%2Fquestion-ranking&tab=answerKey
string solve(string s, string t) {
// write code here
int n = s.size(), m = t.size();
vector<int> res(n + m, 0);
for (int i = 0; i < n; i ++)
for (int j = 0; j < m; j ++)
res[i + j + 1] += (s[i] - '0') * (t[j] - '0');
for (int i = res.size() - 1; i > 0; i --)
{
if (res[i] >= 10)
{
res[i - 1] += res[i] / 10;
res[i] %= 10;
}
}
string ss = "";
for (int i = 0; i < res.size(); i ++)
{
if (i == 0 && res[i] == 0) continue;
ss += to_string(res[i]);
}
return ss;
}