左神算法中级班第七课[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;
}

第二题:找到数组中没出现的数字

在这里插入图片描述

  • 思路
  1. 哈希表解法,时间复杂度和空间复杂度都为O(n)
  2. 时间复杂度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;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

星光技术人

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值