左神算法中级班第七课[C++代码]
声明:博客根据左神在毕站的讲课视频整理,代码是根据左神的java代码版本改写为c++版本
代码为自己动手改写,未经应用不能转载
github代码链接:
https://github.com/hjfenghj/zuoshen_middlecalss
第一题:纯code问题
技巧:goto语句的使用,注意continue,break和goto的区别
代码
- code
#include<iostream>
#include<vector>
#include<string>
using namespace std;
class PROBLEM01
{
public:
//求得数字的长度
vector<string> S{ "W","Q","B","S","L" };
vector<string> S_19 = { "One ", "Two ", "Three ", "Four ", "Five ", "Six ",
"Seven ", "Eight ", "Nine ", "Ten ", "Eleven ", "Twelve ",
"Thirteen ", "Fourteen ", "Fifteen ", "Sixteen ", "Seventeen ",
"Eighteen ", "Nineteen " };
vector<string> S_0 = { "Twenty ", "Thirty ", "Forty ", "Fifty ",
"Sixty ", "Seventy ", "Eighty ", "Ninety " };
string str_res = "";
int pown(int N, int L)
{
int res = 1;
int temp = N;
while (L != 0)
{
if (L & 1 == 1)
{
res *= temp;
temp = temp * temp;
}
else
temp = temp * temp;
L = L >> 1;
}
return res;
}
//求出每位的数字,并添加单位变成字符串
string get_string(int N)
{
if (N <= 19)
return S_19[N-1];
else if (N / 10 / 10 == 0)
return S_0[N / 10 % 10 - 2];
//其他情况
int idx = 4;
int L = 0;//数字的长度
while (N / pown(10, idx) == 0)
{
idx--;
}
L = idx;
while (L >= 0)
{
int cur = N / pown(10, L);
char flag = 'L';
if (cur == 0 && str_res[str_res.size() - 1] == flag)
goto FFF;
else if (cur == N)
str_res += to_string(cur);
else if(cur==0)
str_res += S[4];
else
str_res += to_string(cur) + S[4 - L];
FFF:
N = N % pown(10,L);
L = L - 1;
}
return str_res;
}
};
int main()
{
int N = 19120;
PROBLEM01 P;
string str = P.get_string(N);
string new_str(str.begin(), str.end() - 1);
cout << ((str[str.size()-1] == '0') ? new_str : str )<< endl;;
return 0;
}
第二题:找到数组中没出现的数字
- 思路
- 哈希表解法,时间复杂度和空间复杂度都为O(n)
- 时间复杂度O(n),空间复杂度O(1)
遍历数组元素,将元素i放在第i-1个位置;之后没有地方放法元素就是结果
代码
- code
class PROBLEM02
{
public:
void get_res(vector<int>& Arr)
{
for (int i = 0; i < Arr.size(); i++)
{
process(Arr[i], Arr);
}
for (int i = 0; i < Arr.size(); i++)
{
if (Arr[i] != i + 1)
cout << i+1 << " ";
}
return;
}
void process(int val, vector<int>& Arr)
{
while (Arr[val - 1] != val)
{
int temp = Arr[val - 1];
Arr[val - 1] = val;
val = temp;
}
}
};
第三题:3的倍数的另一种思路
技巧:一个整数每个位置上的数相加如果是3的倍数,那么这个整数就是3的倍数;
1+12+137=150是3的倍数,所以112137也是3的倍数,137112和137121也是3的倍数;
第四题:大哥打赏问题
递归边界条件的寻找
平凡解作为终止条件
题目添加一个条件:start和end都是偶数=
- 思路
递归解题的时候,需要一个终止条件来终止递归的套娃;就是为了防止无限套用,比如说下图:
所以需要选择完备的终止条件; - 思路
使用平凡解作为终止条件
平凡终止条件1:全部使用点赞的方式到达目标积分需要花费的C币
平凡终止条件2:当前的积分不可能是目标积分的两倍
注意:这里的终止条件都是通过业务分析得到到,而且远不止一个,但是只要终止条件选的对,只需要一个就可以得到结果;终止条件越多剪枝越充分,跳出循环越快,可以节省无效的递归操作
代码
- code
#include<iostream>
#include<vector>
using namespace std;
class PROBLEM04
{
public:
// pre 表示已经花费的C币 可变
// end 表示目的积分
// add 表示点赞一次花费的C币
// mul 表示送礼一次花费的C币
// del 表示私聊一次花费的C币
// cur 表示目前到达的积分 可变
// coinlimit 表示花费不会超过coinlimit个C币
// endlimit 表示积分不会超过endlimit个积分
int process1(int pre, int end, int add, int mul, int del, int cur, int coinlimit, int endlimit)
{
//无效解
if (cur >= endlimit || cur<0)
return INT_MAX;
if (pre >= coinlimit)
return INT_MAX;
if (cur == end)
return pre;
//三种情况
int ans = INT_MAX;
int p1 = process1(pre + add, end, add, mul, del, cur + 2, coinlimit, endlimit);
if (p1 != INT_MAX)
ans = min(ans, p1);
int p2 = process1(pre + mul, end, add, mul, del, cur * 2, coinlimit, endlimit);
if (p2 != INT_MAX)
ans = min(ans, p2);
int p3 = process1(pre + del, end, add, mul, del, cur - 2, coinlimit, endlimit);
if (p3 != INT_MAX)
ans = min(ans, p3);
return ans;
}
//动态规划解
int proecess2()
{
}
};
int main()
{
int pre = 0;
int end = 100;
int add = 1;
int mul = 2;
int del = 6;
int cur = 4;
int coinlimit = (end - cur) / 2;
int endlimit = 2 * end;
PROBLEM04 P;
int ans = P.process1(pre, end, add, mul, del, cur, coinlimit, endlimit);
cout << ans << endl;
return 0;
}
第五题:求完全二叉树的节点个数
技巧:完全二叉树某节点A如果A的右节点的最大深度达到了树A的最大深度,那么A的左子树是满树;图A的右节点的最大深度没有达到最大深度,那么A的右节点的右节点是满树
-
思路1
遍历二叉树,计算节点数 -
思路2
完全二叉树某节点A如果A的右节点的最大深度达到了树A的最大深度,那么A的左子树是满树;图A的右节点的最大深度没有达到最大深度,那么A的右节点的右节点是满树;代码
-
code
#include<iostream>
#include<vector>
using namespace std;
class TreeNode
{
public:
int val;
TreeNode* left;
TreeNode* right;
TreeNode(int v)
{
val = v;
}
};
class PROBLEM05
{
public:
int get_depth(TreeNode* node){
int depth = 0;
while (node){
depth++ ;
node = node->left;
}
return depth;
}
//N的L次方
int pown(int N, int L){
int res = 1;
int temp = N;
while (L != 0){
if (L & 1 == 1){
res *= temp;
temp = temp * temp;
}
else
temp = temp * temp;
L = L >> 1;
}
return res;
}
int process(TreeNode* node, int depth){
int ans = 0;
if (node->left == nullptr && node->right==nullptr)
return 1;
//右数的最大深度到达最大深度
if (get_depth(node->right) == depth - 1){
node = node->right;
ans = pown(2, depth - 1) + process(node,depth-1);
}
//右树没有到达最大高度,那么这是右树的右树满树
else if (get_depth(node->right) < depth - 1){
int L = get_depth(node->right);
node = node->left;
ans = pown(2, L) + process(node, depth - 1);
}
return ans;
}
int get_res(TreeNode* node){
int d = get_depth(node);//最大深度,全文贯穿
cout << d << endl;
int ans = process(node, d);
return ans;
}
};
int main()
{
TreeNode* root = new TreeNode(1);
root->left = new TreeNode(2);
root->right = new TreeNode(3);
root->left->left = new TreeNode(4);
root->left->right = new TreeNode(5);
root->right->left = new TreeNode(6);
//root->right->right = new TreeNode(7);
PROBLEM05 P;
int ans = P.get_res(root);
cout << ans << endl;
return 0;
}
第六题:图的问题大魔王问题,很难
堆的应用,图的数据结构;使用map存储节点的父节点
- 思路:
1) 根据给出的矩阵,得到一个map映射,记录节点与节点的父节点,得到图中的出度为1的一个节点
2)建立一个新的map映射,<int,map<int,int>>,记录第i个节点到到达最后一个节点的信息,表示从第i个任务开始,到最后一个任务花费的时间和获得的钱数,因为第i个节点出度可能大于1,所以节点i到最后一个节点的路径也可能大于1,所以使用map记录路径上所有节点信息之和;3)map中存的数据进行清洗,存在一个优先队列中,同样的时间只保留得到回报多的路径;然后按时间建立小根堆
4)将第三步第一次清洗后的小根堆中的数据,整合到一个map;只保留时间增加,积分也增加的;
5) 在大map中找到第一个小于等于 规定时间内得数据;
代码
- code
#include<iostream>
#include<vector>
#include<map>
#include<set>
#include<queue>
using namespace std;
class PROBLEM06
{
static bool cmp(pair<int const, int> P1, pair<int const, int> P2)
{
//参数1是堆顶到堆底降序(因为参数1是负数),参数2堆顶到堆降序
return P1.first != P2.first ? P2.first > P1.first : P2.second > P1.second;
}
public:
//All_Times 表示时间限制
//used_times 表示每个任务需要消耗的天数
//get_moneys 表示每个任务得到的回报
//D_arr 表示两项任务之间的依赖关系,假如D_arr[0][3] = 1表示任务0是任务3的先修任务
void get_res(int All_Times, vector<int> use_times, vector<int> get_money, vector<vector<int>> D_arr)
{
int L = use_times.size();
int end=0;//最后一个节点
//使用map储存任务之间的依赖关系
map<int, vector<int>> parents;//使用map,方便查找
for (int i = 0; i < D_arr.size(); i++)
{
bool flag = true;
for (int j = 0; j < D_arr.size(); j++)
{
if (D_arr[i][j] == 1)
{
parents[j].push_back(i);
flag = false;
}
}
if (flag)
end = i;
}
//储存每个节点到任务结束需要的一系列(时间,回报)对
map<int,map<int, int>> t_m_vec;
queue<int> Q;//遍历所有的节点
t_m_vec[end].emplace(-use_times[end], get_money[end]);//使用-use_times是为了下文使用lower_bound函数
Q.push(end);
while (!Q.empty())
{
int cur = Q.front();
Q.pop();
for (int c : parents[cur])//表里cur的所有父亲节点,更新父亲节点id对应的t_m_vec
{
for (auto t_m : t_m_vec.find(cur)->second)
{
int newtime = use_times[c] + abs(t_m.first);
int new_money = get_money[c] + t_m.second;
t_m_vec[c].emplace(-newtime, new_money);
}
Q.push(c);
}
}
priority_queue<pair<int,int>,vector<pair<int,int>>,decltype(&cmp)> clean_SET(cmp);//第一次数据清洗,将所有的map中所有pair数据根据cmp规则排序
for (auto tm : t_m_vec)
{
for (auto m : tm.second)
{
clean_SET.push(make_pair(m.first, m.second));
}
}
map<int,int> All_Map; //第二次数据清洗,保留时间多,钱数也多的数据;
int allsize = 1;
auto cur = clean_SET.top();
clean_SET.pop();
All_Map.emplace(cur.first, cur.second);
while (!clean_SET.empty())
{
if (cur.first != clean_SET.top().first && cur.second < clean_SET.top().second)
{
allsize++;
cur = clean_SET.top();
All_Map.emplace(cur.first, cur.second);
}
clean_SET.pop();
}
int ans = INT_MIN;
int T = -All_Times;
int old_ans;
for(int K = All_Times; K > 0; K--)
{
if (All_Map.lower_bound(-K) != All_Map.end())
{
ans = max(ans, All_Map.lower_bound(-K)->second);
T = max(T, All_Map.lower_bound(-K)->first);
break;
}
}
cout << ans << " " << -T << endl;
return;
}
};
int main()
{
int allTime = 10;
vector<int> revenue = { 2000, 4000, 2500, 1600, 3800, 2600, 4000, 3500 };
vector<int> times = { 3, 3, 2, 1, 4, 2, 4, 3 };
vector<vector<int>> dependents = {
{ 0, 1, 1, 0, 0, 0, 0, 0 },
{ 0, 0, 0, 1, 1, 0, 0, 0 },
{ 0, 0, 0, 1, 0, 0, 0, 0 },
{ 0, 0, 0, 0, 1, 1, 1, 0 },
{ 0, 0, 0, 0, 0, 0, 0, 1 },
{ 0, 0, 0, 0, 0, 0, 0, 1 },
{ 0, 0, 0, 0, 0, 0, 0, 1 },
{ 0, 0, 0, 0, 0, 0, 0, 0 } };
PROBLEM06 P;
P.get_res(allTime, times, revenue, dependents);
return 0;
}