面试算法题
目录
- 简单
- 53. 最大子数组和 - 力扣(LeetCode)
- 415. 字符串相加 - 力扣(LeetCode)
- 206. 反转链表 - 力扣(LeetCode)
- 1. 两数之和 - 力扣(LeetCode)
- 572. 另一棵树的子树 - 力扣(LeetCode)
- 1410. HTML 实体解析器 - 力扣(LeetCode)
- 69. x 的平方根 - 力扣(LeetCode)
- 26. 删除有序数组中的重复项 - 力扣(LeetCode)
- 141. 环形链表 - 力扣(LeetCode)
- LCR 140. 训练计划 II - 力扣(LeetCode)
- 338. 比特位计数 - 力扣(LeetCode)
- 101. 对称二叉树 - 力扣(LeetCode)
- 110. 平衡二叉树 - 力扣(LeetCode)
- 155. 最小栈 - 力扣(LeetCode)
- 27. 移除元素 - 力扣(LeetCode)
- 中等
- 3. 无重复字符的最长子串 - 力扣(LeetCode)
- 146. LRU 缓存 - 力扣(LeetCode)
- 236. 二叉树的最近公共祖先 - 力扣(LeetCode)
- 72. 编辑距离 - 力扣(LeetCode)
- 15. 三数之和 - 力扣(LeetCode)
- 1143. 最长公共子序列 - 力扣(LeetCode)
- LCR 143. 子结构判断 - 力扣(LeetCode)
- 54. 螺旋矩阵 - 力扣(LeetCode)
- 56. 合并区间 - 力扣(LeetCode)
- 46. 全排列 - 力扣(LeetCode)
- 105. 从前序与中序遍历序列构造二叉树 - 力扣(LeetCode)
- 103. 二叉树的锯齿形层序遍历 - 力扣(LeetCode)
- 279. 完全平方数 - 力扣(LeetCode)
- 240. 搜索二维矩阵 II - 力扣(LeetCode)
- 牛牛的字符串解码问题_牛客题霸_牛客网 (nowcoder.com)
- 牛奶供应问题_牛客题霸_牛客网 (nowcoder.com)
简单
53. 最大子数组和 - 力扣(LeetCode)
动态规划思想
class Solution {
public:
int maxSubArray(vector<int>& nums) {
int maxSum = nums[0]; // 初始化最大子数组和为数组的第一个元素
int currentSum = nums[0]; // 初始化当前子数组和为数组的第一个元素
for (int i = 1; i < nums.size(); i++) {
// 如果currentSum加上当前元素比当前元素还小,就重新开始找新的子数组
currentSum = max(currentSum + nums[i], nums[i]);
// 更新全局最大子数组和
maxSum = max(maxSum, currentSum);
}
return maxSum;
}
};
415. 字符串相加 - 力扣(LeetCode)
class Solution {
public:
string addStrings(string num1, string num2) {
int i = num1.size() - 1;
int j = num2.size() - 1;
int carry = 0;
string result = "";
while (i >= 0 || j >= 0 || carry) {
int sum = carry;
if (i >= 0) {
sum += num1[i] - '0'; // 将字符转换为整数
i--;
}
if (j >= 0) {
sum += num2[j] - '0'; // 将字符转换为整数
j--;
}
result = char(sum % 10 + '0') + result; // 计算当前位的结果并添加到结果字符串前
carry = sum / 10; // 计算进位
}
return result;
}
};
206. 反转链表 - 力扣(LeetCode)
双指针解法
class Solution {
public:
ListNode* reverseList(ListNode* head) {
ListNode* cur = NULL, *pre = head;
while (pre != NULL) {
ListNode* t = pre->next;
pre->next = cur;
cur = pre;
pre = t;
}
return cur;
}
};
1. 两数之和 - 力扣(LeetCode)
双指针进行维护
class Solution {
public:
vector<int> twoSum(vector<int>& nums, int target) {
int idx = nums.size();
vector<pair<int, int>> v;
for (int i = 0; i < idx; i++) { // 修正为包含全部元素
v.push_back({nums[i], i});
}
sort(v.begin(), v.end());
int i = 0, j = idx - 1; // 初始化 j
while (i < j) { // 确保 i 和 j 不相遇
int sum = v[i].first + v[j].first;
if (sum == target) {
return {v[i].second, v[j].second}; // 找到合适的对,返回其索引
} else if (sum > target) {
j--;
} else {
i++;
}
}
return {}; // 如果没有找到,返回空数组
}
};
572. 另一棵树的子树 - 力扣(LeetCode)
//isSubtree()函数用与不断向下搜索(左下和右下)
//judge()函数用于在搜索的时候进行判断
class Solution {
public:
bool judge(TreeNode* root, TreeNode* subRoot)
{
if(subRoot==NULL&&root==NULL) return true;
if(!root&&subRoot||root&&!subRoot||root->val!=subRoot->val) return false;
return judge(root->left,subRoot->left)&&judge(root->right,subRoot->right);
}
bool isSubtree(TreeNode* root, TreeNode* subRoot) {
if(root==NULL) return false;//只对root进行扩展,所以只判断root
return judge(root,subRoot) || isSubtree(root->left,subRoot) ||isSubtree(root->right,subRoot);
}
};
1410. HTML 实体解析器 - 力扣(LeetCode)
class Solution {
public:
string entityParser(string text) {
int l1=0, l2=0, l3=0, l4=0, l5=0, l6=0;
// Replace " with "
while (text.find(""", l1) != string::npos) {
int idx = text.find(""", l1);
text.replace(idx, 6, "\"");
l1 = idx + 1;
}
// Replace ' with '
while (text.find("'", l2) != string::npos) {
int idx = text.find("'", l2);
text.replace(idx, 6, "'");
l2 = idx + 1;
}
// Replace > with >
while (text.find(">", l4) != string::npos) {
int idx = text.find(">", l4);
text.replace(idx, 4, ">");
l4 = idx + 1;
}
// Replace < with <
while (text.find("<", l5) != string::npos) {
int idx = text.find("<", l5);
text.replace(idx, 4, "<");
l5 = idx + 1;
}
// Replace ⁄ with /
while (text.find("⁄", l6) != string::npos) {
int idx = text.find("⁄", l6);
text.replace(idx, 7, "/");
l6 = idx + 1;
}
// Finally, replace & with & to avoid interference with other entities
while (text.find("&", l3) != string::npos) {
int idx = text.find("&", l3);
text.replace(idx, 5, "&");
l3 = idx + 1;
}
return text;
}
};
69. x 的平方根 - 力扣(LeetCode)
二分秒了
class Solution {
public:
bool check(long long mid,long long x)
{
if(mid*mid<=x) return true;
return false;
}
long long search(long long l,long long r,long long x)
{
while(l<r)
{
long long mid=(l+r+1)>>1;
if(check(mid,x)) l=mid;
else r=mid-1;
}
return l;
}
long long mySqrt(long long x) {
return search(0,x,x);
}
};
26. 删除有序数组中的重复项 - 力扣(LeetCode)
非常基础的一道题
class Solution {
public:
int removeDuplicates(vector<int>& nums) {
unordered_map<int,int> mp;
vector<int> v;
for(auto &ele : nums)
{
if(mp[ele]) continue;
else
{
mp[ele]++;
v.push_back(ele);
}
}
nums=v;
return v.size();
}
};
141. 环形链表 - 力扣(LeetCode)
非常简单的map映射就可以解决
class Solution {
public:
bool hasCycle(ListNode *head) {
unordered_map<ListNode*,int> mp;
while(head!=NULL)
{
mp[head]++;
if(mp[head]>1) return true;
head=head->next;
}
return false;
}
};
LCR 140. 训练计划 II - 力扣(LeetCode)
class Solution {
public:
ListNode* trainingPlan(ListNode* head, int cnt) {
ListNode* p=head;
if(p->next==NULL) return p;
vector<int> v;
while(p!=NULL)
{
v.push_back(p->val);
p=p->next;
}
ListNode* l=new ListNode(v[v.size()-cnt]);
ListNode* current=l;//目的是使l一直为头节点
for(int i=v.size()-cnt+1;i<v.size();i++)
{
current->next=new ListNode(v[i]);
current=current->next;
}
return l;
}
};
338. 比特位计数 - 力扣(LeetCode)
class Solution {
public:
int lowbit(int x)
{
return x & -x;
}
vector<int> countBits(int n) {
vector<int> v;
for(int i=0;i<=n;i++)
{
int temp=i;
int res=0;
while(temp)
{
temp-=lowbit(temp);
res++;
}
v.push_back(res);
}
return v;
}
};
101. 对称二叉树 - 力扣(LeetCode)
很简单的一个递归用法,虽然我没想到
class Solution {
public:
bool check(TreeNode* l,TreeNode* r)
{
if(!l&&!r) return true;
if(!l||!r) return false;
return l->val==r->val && check(l->left,r->right) && check(l->right,r->left);
}
bool isSymmetric(TreeNode* root) {
return check(root,root);
}
};
110. 平衡二叉树 - 力扣(LeetCode)
平衡二叉树也叫AVL树,它或者是一颗空树,或者具有以下性质的二叉排序树:它的左子树和左子树的高度之差(平衡因子)的绝对值不超过1,且它的左子树和右子树都是一颗平衡二叉树。
涉及到求解二叉树深度
int depth(TreeNode* node)
{
if(!node) return true;
return max(depth(node->left),depth(node->right))+1;
}
下面为递归代码,不过我觉得这个代码有点冗余了,每个子节点都要判断一遍
class Solution {
public:
int depth(TreeNode* node)
{
if(!node) return true;
return max(depth(node->left),depth(node->right))+1;
}
bool isBalanced(TreeNode* root) {
if(!root) return true;
int d=abs(depth(root->left)-depth(root->right));
return (d<=1) && (isBalanced(root->left)) && (isBalanced(root->right));
}
};
155. 最小栈 - 力扣(LeetCode)
class MinStack {
public:
stack<long long> st;
MinStack() {
}
void push(int val) {
st.push(val);
}
void pop() {
st.pop();
}
int top() {
return st.top();
}
int getMin() {
long long mn=1e10;
stack<long long> ts;
ts=st;
while(!ts.empty())
{
long long tp=ts.top();
mn=min(mn,tp);
ts.pop();
}
return mn;
}
};
27. 移除元素 - 力扣(LeetCode)
增序放置不等于val的元素,k即作为增量下标又作为数量统计
class Solution {
public:
int removeElement(vector<int>& nums, int val) {
int k = 0;
for (int i = 0; i < nums.size(); i++) {
if(nums[i]!=val)
{
nums[k]=nums[i];
k++;
}
}
return k;
}
};
中等
3. 无重复字符的最长子串 - 力扣(LeetCode)
本来想用双指针的,结果双指针都不需要,挺简单的
class Solution {
public:
int lengthOfLongestSubstring(string s)
{
if(!s.size()) return 0;
unordered_map<char,int> mp;
unordered_map<char,int> idx;
int ans=0;
int mx=-1e7;
for(int i=0;i<s.size();i++)
{
mp[s[i]]++;
if(mp[s[i]]==1)
{
idx[s[i]]=i;
ans++;
mx=max(mx,ans);
}
else
{
i=idx[s[i]];
ans=0;
unordered_map<char,int>().swap(mp);
unordered_map<char,int>().swap(idx);
}
}
return mx;
}
};
146. LRU 缓存 - 力扣(LeetCode)
很不错的一道题,让我学会了以下用法:
unordered_map<int,list<pair<int,int>>::iterator> cache
item.splice(item.begin(),item,cache[key]);//(移动位置,容器,移动的元素)
item.emplace_front(key,value);//链表头插
class LRUCache {
private:
int cap;
list<pair<int,int>> item;
unordered_map<int,list<pair<int,int>>::iterator> cache;//cache始终指向list尾部
public:
LRUCache(int capacity) {
cap=capacity;
}
int get(int key) {
if(cache.find(key)==cache.end()) return -1;
item.splice(item.begin(),item,cache[key]);
return cache[key]->second;//把链表返回过去
}
void put(int key, int value) {
if(cache.find(key)!=cache.end())
{
cache[key]->second=value;
item.splice(item.begin(),item,cache[key]);
return;
}
if(item.size()==cap)
{
auto last=item.back();
cache.erase(last.first);
item.pop_back();
}
item.emplace_front(key,value);
cache[key]=item.begin();
}
};
/**
* Your LRUCache object will be instantiated and called as such:
* LRUCache* obj = new LRUCache(capacity);
* int param_1 = obj->get(key);
* obj->put(key,value);
*/
236. 二叉树的最近公共祖先 - 力扣(LeetCode)
非常巧妙的一道题
class Solution {
public:
TreeNode* lowestCommonAncestor(TreeNode* root, TreeNode* p, TreeNode* q) {
if(root==NULL || root==p || root==q) return root;
TreeNode* left=lowestCommonAncestor(root->left,p,q);
TreeNode* right=lowestCommonAncestor(root->right,p,q);
if(left==NULL) return right;
if(right==NULL) return left;
return root;
}
};
72. 编辑距离 - 力扣(LeetCode)
很奇妙的一道动态规划的题
dp[i][j] 表示以下标i-1为结尾的字符串word1,和以下标j-1为结尾的字符串word2,最近编辑距离为dp[i][j]。
状态转移方程为:dp[i][j]=min({dp[i-1][j],dp[i][j-1],dp[i-1][j-1]})+1;
class Solution {
public:
int minDistance(string word1, string word2) {
vector<vector<int>> dp(word1.size()+1,vector<int>(word2.size()+1,0));
for(int i=0;i<=word1.size();i++) dp[i][0]=i;
for(int j=0;j<=word2.size();j++) dp[0][j]=j;
for(int i=1;i<=word1.size();i++)
{
for(int j=1;j<=word2.size();j++)
{
if(word1[i-1]==word2[j-1]) dp[i][j]=dp[i-1][j-1];
else
{
dp[i][j]=min({dp[i-1][j],dp[i][j-1],dp[i-1][j-1]})+1;
}
}
}
return dp[word1.size()][word2.size()];
}
};
15. 三数之和 - 力扣(LeetCode)
双指针+转化
nums[i] + nums[j] + nums[k] == 0 可以转化为 nums[j] + nums[k]==-nums[i]
因为下标i,j,k互补相等且i<j<k,及在下标(i-idx]范围内查找。
及for循环遍历i,维护j、k两个指针
set<vector<int>> st;
st.insert({nums[i],nums[l],nums[r]});
vector<vector<int>> ans(st.begin(),st.end());
class Solution {
public:
vector<vector<int>> threeSum(vector<int>& nums) {
set<vector<int>> st;
sort(nums.begin(),nums.end());
int idx=nums.size();
for(int i=0;i<idx;i++)
{
if(i&&nums[i]==nums[i-1]) continue;
int l=i+1,r=idx-1;
while(l<r)
{
if(nums[l]+nums[r]>-nums[i]) r--;
else if(nums[l]+nums[r]<-nums[i]) l++;
else
{
st.insert({nums[i],nums[l],nums[r]});
l++,r--;//要注意插入后要更新状态
}
}
}
vector<vector<int>> ans(st.begin(),st.end());
return ans;
}
};
1143. 最长公共子序列 - 力扣(LeetCode)
动态规划经典题
数组范围是0~n-1;
动态规划存储上一步的状态所以是1~n;
因为比较的是上一步的状态所以是i-1和j-1;
状态转移方程1:dp[i][j] = dp[i-1][j-1] + 1;
状态转移方程2:dp[i][j] = max(dp[i-1][j], dp[i][j-1]);
class Solution {
public:
int longestCommonSubsequence(string text1, string text2) {
int dp[1010][1010]; // 初始化全部元素为0
for(int i = 1; i <= text1.size(); i++) {
for(int j = 1; j <= text2.size(); j++) {
if( text1[i-1] == text2[j-1] ) {
dp[i][j] = dp[i-1][j-1] + 1; // 如果当前字符匹配,则增加长度
} else {
dp[i][j] = max(dp[i-1][j], dp[i][j-1]); // 否则,取最大值
}
}
}
return dp[text1.size()][text2.size()]; // 返回最终结果
}
};
LCR 143. 子结构判断 - 力扣(LeetCode)
class Solution {
public:
bool judge(TreeNode* A, TreeNode* B)
{
if(B==NULL) return true;
if((A==NULL&&B!=NULL)||A->val!=B->val) return false;
return judge(A->left,B->left)&&judge(A->right,B->right);//本身||左字数查找||右子树查找
}
bool isSubStructure(TreeNode* A, TreeNode* B) {
if(A==NULL||B==NULL) return false;
return judge(A,B) || isSubStructure(A->left,B) || isSubStructure(A->right,B);//当前节点||同时左查||同时右查
}
};
54. 螺旋矩阵 - 力扣(LeetCode)
class Solution {
private:
int st[20][20];
public:
vector<int> spiralOrder(vector<vector<int>>& matrix) {
if (matrix.empty()) return {}; // 处理空矩阵的情况
vector<int> v;
int ti=matrix.size();
int tj=matrix[0].size();
int x=0,y=0;
st[0][0]=1;
v.push_back(matrix[0][0]);
while(v.size()<ti*tj)
{
while(y!=tj-1&&!st[x][y+1])
{
y++;
v.push_back(matrix[x][y]);
st[x][y]=1;
}
while(x!=ti-1&&!st[x+1][y])
{
x++;
v.push_back(matrix[x][y]);
st[x][y]=1;
}
while(y!=0&&!st[x][y-1])
{
y--;
v.push_back(matrix[x][y]);
st[x][y]=1;
}
while(x!=0&&!st[x-1][y])//x!=0的判断是为了防止数组越界
{
x--;
v.push_back(matrix[x][y]);
st[x][y]=1;
}
}
return v;
}
};
56. 合并区间 - 力扣(LeetCode)
class Solution {
public:
vector<vector<int>> merge(vector<vector<int>>& intervals) {
sort(intervals.begin(),intervals.end());
int st=-2e9,ed=-2e9;
vector<vector<int>> res;
for(auto &ele : intervals)
{
if(ed<ele[0])
{
if(st!=-2e9) res.push_back({st,ed});
st=ele[0],ed=ele[1];
}
else ed=max(ed,ele[1]);
}
if(st!=-2e9) res.push_back({st,ed});
return res;
}
};
46. 全排列 - 力扣(LeetCode)
class Solution {
private:
vector<vector<int>> v;
vector<int> num;
int vis[10];
int a[10];
int r;
int n;
public:
void dfs(int x)
{
if(x==r+1)
{
vector<int> nt;
for(int i=1;i<=r;i++) nt.push_back(num[a[i]-1]);
v.push_back(nt);
return;
}
for(int i=1;i<=n;i++)
{
if(!vis[i])
{
a[x]=i;
vis[i]=1;
dfs(x+1);
vis[i]=0;
}
}
}
vector<vector<int>> permute(vector<int>& nums) {
num=nums;
r=n=nums.size();
a[0]=0;
dfs(1);
return v;
}
};
105. 从前序与中序遍历序列构造二叉树 - 力扣(LeetCode)
class Solution {
private:
vector<int> preorder;
unordered_map<int,int> mp;
TreeNode* check(int root,int left,int right)
{
if(left>right) return NULL;
TreeNode* node=new TreeNode(preorder[root]);
int i=mp[preorder[root]];//对前序遍历数组进行操作
node->left = check(root+1,left,i-1);
node->right=check(root+i+1-left,i+1,right);
return node;
}
public:
TreeNode* buildTree(vector<int>& preorder, vector<int>& inorder) {
this->preorder=preorder;
for(int i=0;i<inorder.size();i++)
{
mp[inorder[i]]=i;//记录下中序遍历数组中元素的坐标
}
return check(0,0,inorder.size()-1);
}
};
103. 二叉树的锯齿形层序遍历 - 力扣(LeetCode)
class Solution {
public:
vector<vector<int>> zigzagLevelOrder(TreeNode* root) {
if(!root) return {};
vector<vector<int>> ans;
queue<TreeNode*> que;
que.push(root);
while(!que.empty())
{
vector<int> v;
for(int i=que.size();i>=1;i--)
{
auto top=que.front();
que.pop();
v.push_back(top->val);//存储该节点的值,每次循环只存储当前节点的值
if(top->left) que.push(top->left);
if(top->right) que.push(top->right);
}
if(ans.size()%2) reverse(v.begin(),v.end());
ans.push_back(v);
}
return ans;
}
};
279. 完全平方数 - 力扣(LeetCode)
记模板就行
状态转移方程:mx = min(mx, f[i - j * j]);
class Solution {
public:
int numSquares(int n) {
vector<int> f(n + 1);
for (int i = 1; i <= n; i++) {
int mn = INT_MAX;
for (int j = 1; j * j <= i; j++) {
mn = min(mn, f[i - j * j]);
}
f[i] = mn + 1;
}
return f[n];
}
};
240. 搜索二维矩阵 II - 力扣(LeetCode)
从右上角搜最合适
class Solution {
public:
bool searchMatrix(vector<vector<int>>& matrix, int target)
{
if (matrix.empty() || matrix[0].empty()) return false;
int m = matrix.size(), n = matrix[0].size();
int row = 0, col = 0;
while (row < m && col >= 0)
{
if (matrix[row][col] == target) return true;
else if (matrix[row][col] > target) col--;
else row++;
}
return false;
}
};
牛牛的字符串解码问题_牛客题霸_牛客网 (nowcoder.com)
用栈解决嵌套问题
class Solution {
public:
string decodeString(string s) {
int num;
stack<string> st;
stack<int> nums;
string ts;
for(int i=0;i<s.size();i++)
{
if(s[i]>='1'&&s[i]<='9')
{
num=s[i]-'0';
}
else if(s[i]=='[')
{
st.push(ts);
nums.push(num);
ts="";
num=0;
}
else if(s[i]==']')
{
string tp=ts;//保存中间编码部分
int ans=nums.top();
nums.pop();
ts=st.top();
st.pop();
for(int i=1;i<=ans;i++) ts+=tp;
}
else
{
ts+=s[i];
}
}
return ts;
}
};
牛奶供应问题_牛客题霸_牛客网 (nowcoder.com)
重复:不断取出最小的,加上下一元素后再压入
最后取出最大的
#include <functional>
#include <vector>
class Solution {
public:
int animalTaskScheduler(vector<int>& taskDurations, int capacity) {
priority_queue<int,vector<int>,greater<int>> que;
for(auto &ele : taskDurations)
{
if(que.size()<capacity)
{
que.push(ele);
}
else
{
int num=que.top();
que.pop();
num+=ele;
que.push(num);
}
}
int res;
while(!que.empty())
{
res=que.top();
que.pop();
}
return res;
}
};