C++常见算法
DFS(深度优先遍历)
适用于:
一般用于排列,例如从几个数里面抽取几个数把所有结果都排列出来
模板:
bool book[128] = { 0 }; //很重要
vector<int> vecOut;
void DFS(参数一般包括输出(可以用成员变量代替),输入, 当前个数(初始化时是0),长度)
{
if(nCur == nLen)
{
vecAll.emplace_back(vecOut);
return; //注意要加上
}
for (int i = 0; i < nLen; ++i)
{
if (!book[i])
{
book[i] = true;
vecOut.emplace_back(nums[i]);
DFS(vecAll, nums, nCur + 1, nLen); //注意nCur要+1
book[i] = false;
vecOut.pop_back();
}
}
}
例1:力扣46
个人解:
bool book[128] = { 0 };
vector<int> vecOut;
class Solution {
public:
void DFS(vector<vector<int>>& vecAll, vector<int>& nums, int nCur, int nLen)
{
if (nCur == nLen)
{
vecAll.emplace_back(vecOut);
return;
}
for (int i = 0; i < nLen; ++i)
{
if (!book[i])
{
book[i] = true;
vecOut.emplace_back(nums[i]);
DFS(vecAll, nums, nCur + 1, nLen);
book[i] = false;
vecOut.pop_back();
}
}
}
vector<vector<int>> permute(vector<int>& nums) {
vector<vector<int>> vecAll;
DFS(vecAll, nums, 0, nums.size());
return vecAll;
}
};
BFS(广度优先遍历)
适用于:
一般用于图,二维数组这些找最小或最大或最短路径这些。
模板:
int dir[4][2] = { {-1, 0}, {1, 0}, {0, -1}, {0, 1} }; //上下左右四个方向(顺序没影响)
int m = grid.size(); //得到行;
int n = grid[0].size(); //得到列;
queue<pair<int, int>> q;
q.emplace(i, j); //通过遍历把符合条件的坐标入队
while (!q.empty())
{
int x = q.front().first; //得到当前x的坐标
int y = q.front().second;
q.pop(); //出队
for (int i = 0; i < 4; ++i) //遍历当前的坐标的上下左右四个方向
{
int ni = x + dir[i][1];
int nj = y + dir[i][0];
if (ni >= 0 && ni < m && nj >= 0 && nj < n && 写一个条件//grid[ni][nj] == 1)
{
//grid[ni][nj] = 2; //有可能需要改变状态
//dis[tx][ty] = dis[ni][nj] + 1; //如果是同时发生的话,可以用一个二维数组来存储每一个位置的数,然后累加
q.emplace(ni, nj); //符合条件继续入队
}
}
}
例1:力扣994
个人解:
//代码有点问题
#include <queue>
class Solution {
public:
int orangesRotting(vector<vector<int>>& grid) {
int m = grid.size();
int n = grid[0].size();
int sum = 0;
int nMin = 0;
int nMin[128][128] = { 0 };
int dir[4][2] = { {-1, 0}, {1, 0}, {0, -1}, {0, 1} };
queue<pair<int, int>> q;
queue<pair<int, int>> Q;
for (int i = 0; i < m; ++i)
{
for (int j = 0; j < n; ++j)
{
if (grid[i][j] == 1)
{
bBook[i][j] = 1;
sum++;
}
else if (grid[i][j] == 2)
{
q.emplace(i, j);
nMin[i][j] = 0;
}
}
}
while (!q.empty())
{
vector<vector<int>>vecTmp;
while (!q.empty())
{
int x = q.front().first;
int y = q.front().second;
q.pop();
}
bool bFlag = false;
for (int i = 0; i < 4; ++i)
{
int ni = x + dir[i][1];
int nj = y + dir[i][0];
if (ni >= 0 && ni < m && nj >= 0 && nj < n && grid[ni][nj] == 1)
{
grid[ni][nj] = 2;
q.emplace(ni, nj);
bFlag = true;
sum--;
nMin[ni][nj]
}
}
if (sum == 0)
{
break;
}
}
if (sum != 0)
{
return -1;
}
return nMin;
}
};
双指针
例1:力扣15
class Solution {
public:
vector<vector<int>> threeSum(vector<int>& nums)
{
int size = nums.size();
if (size < 3) return {}; // 特判
vector<vector<int> >res; // 保存结果(所有不重复的三元组)
std::sort(nums.begin(), nums.end());// 排序(默认递增)
for (int i = 0; i < size; i++) // 固定第一个数,转化为求两数之和
{
if (nums[i] > 0) return res; // 第一个数大于 0,后面都是递增正数,不可能相加为零了
// 去重:如果此数已经选取过,跳过
if (i > 0 && nums[i] == nums[i-1]) continue;
// 双指针在nums[i]后面的区间中寻找和为0-nums[i]的另外两个数
int left = i + 1;
int right = size - 1;
while (left < right)
{
if (nums[left] + nums[right] > -nums[i])
right--; // 两数之和太大,右指针左移
else if (nums[left] + nums[right] < -nums[i])
left++; // 两数之和太小,左指针右移
else
{
// 找到一个和为零的三元组,添加到结果中,左右指针内缩,继续寻找
res.push_back(vector<int>{nums[i], nums[left], nums[right]});
left++;
right--;
// 去重:第二个数和第三个数也不重复选取
// 例如:[-4,1,1,1,2,3,3,3], i=0, left=1, right=5
while (left < right && nums[left] == nums[left-1]) left++;
while (left < right && nums[right] == nums[right+1]) right--;
}
}
}
return res;
}
};
例2:力扣455
class Solution {
public:
int findContentChildren(vector<int>& g, vector<int>& s) {
sort(g.begin(), g.end());
sort(s.begin(), s.end());
int m = g.size(), n = s.size();
int count = 0;
for (int i = 0, j = 0; i < m && j < n; i++, j++) {
while (j < n && g[i] > s[j]) { //如果g[i] > s[j],就让j++直到找到大于等于为止
j++;
}
if (j < n) {
count++;
}
}
return count;
}
};
01背包问题(每个物品仅能使用一次)
int main()
{
int w[5] = { 0 , 2 , 3 , 4 , 5 }; //商品的体积2、3、4、5
int v[5] = { 0 , 3 , 4 , 5 , 6 }; //商品的价值3、4、5、6
int bagV = 8; //背包大小
int dp[5][9] = { { 0 } }; //动态规划表
for (int i = 1; i <= 4; i++) {
for (int j = 1; j <= bagV; j++) {
if (j < w[i]) //如果此物品的容量大于当前的总容量(即装不下)
dp[i][j] = dp[i - 1][j];
else
dp[i][j] = max(dp[i - 1][j], dp[i - 1][j - w[i]] + v[i]); //在装与不装之间选择最优的一个
}
}
}
dp[i][j] = max(dp[i - 1][j], dp[i - 1][j - w[i]] + v[i]);详解:
dp[i - 1][j]: 不装时的价值
dp[i - 1][j - w[i]]:假设装进背包,当前剩余容量的价值
v[i]:此时物品的价值
两个代码其实只有一句不同(注意下标)
dp[i][j] = max(dp[i - 1][j], dp[i - 1][j - w[i]] + v[i]);//01背包
dp[i][j] = max(dp[i - 1][j], dp[i][j - w[i]] + v[i]);//完全背包问题
栈
适用于对称匹配
例1:力扣394
#include <stack>
class Solution {
public:
string decodeString(string s) {
string res = "";
stack <int> nums;
stack <string> strs;
int num = 0;
int len = s.size();
for (int i = 0; i < len; ++i)
{
if (s[i] >= '0' && s[i] <= '9') //可以isdigit替代数字判断
{
num = num * 10 + s[i] - '0';
}
else if ((s[i] >= 'a' && s[i] <= 'z') || (s[i] >= 'A' && s[i] <= 'Z')) //可用isalpha替代字母判断
{
res = res + s[i];
}
else if (s[i] == '[') //将‘[’前的数字压入nums栈内, 字母字符串压入strs栈内
{
nums.push(num);
num = 0;
strs.push(res);
res = "";
}
else //遇到‘]’时,操作与之相配的‘[’之间的字符,使用分配律
{
int times = nums.top();
nums.pop();
for (int j = 0; j < times; ++j)
strs.top() += res;
res = strs.top(); //之后若还是字母,就会直接加到res之后,因为它们是同一级的运算
//若是左括号,res会被压入strs栈,作为上一层的运算
strs.pop();
}
}
return res;
}
};