有营养的算法笔记(一)

时间:2022-10-07 09:58:31

调整二叉树

1.题目描述

给定一棵多叉树的头节点head,每个节点只有黑白两色所有黑节点都保留,所有从头节点到黑节点路径上的点都保留返回处理后树的头节点

有营养的算法笔记(一)
意思就差不多是上面这个图一样,保留黑色节点并将沿途路径上的节点都要保留。

2.解题思路
这个题了直接干就是了深度优先遍历,先遍历其孩子节点将其孩子节点处理完毕之后用一个容器保存好如果一个节点孩子节点被全部删除了,并且它自己也不是黑色节点那么这个节点给上层返回nullptr,否则这个更新这个节点的孩子节点并返回

3.对应代码

#include<iostream>
#include<vector>
using namespace std;
//给定一棵多叉树的头节点head,每个节点只有黑白两色
//所有黑节点都保留,所有从头节点到黑节点路径上的点都保留
//返回处理后树的头节点
struct TreeNode
{
	bool retain;//每个节点是否需要被保留也就是是黑色节点还是白色节点
	vector<TreeNode*>nexts;//邻居节点
	int val;
	TreeNode(int v,int r)
		:val(v)
		,retain(r)
	{}

};
typedef TreeNode Node;
Node* Reatin(Node* head)
{
	//没有子节点可以看自己是否是黑色节点
	if (head->nexts.empty()) {
		return head->retain ? head : nullptr;
	}
	vector<Node*>newNext;
	//遍历其孩子节点
	for (auto next : head->nexts) {
		Node* ret = Reatin(next);
		if (ret)
		{
			newNext.push_back(ret);
		}
	}
	//如果他的孩子节点调整之后还有,或者当前节点需要保留
	if (!newNext.empty() || head->retain) {
		head->nexts = newNext;
		return head;
	}
	//否则直接返回空
	return nullptr;
}

int main()
{
	return 0;
}

猜数字大小

1.对应OJ链接

猜数字大小

2.题目描述
有营养的算法笔记(一)
3.解题思路

很明显这是一个范围上的尝试模型。我们可以设计一个这样的尝试,我们已经确定了答案就在这个1到n当中即为L到R当中。所以我门就可以设计这样的一个尝试。现在我已经确定了答案就在L…R当中请返回我最倒霉的情况下需要的最小金币数。下面我们来分析可能性:
可能性1:我就猜开头位置也就是L那么我最倒霉那么也就是我没有猜对承担了一个L的代价,那么在[L+1…R]范围上继续搞
可能性2:我就猜结尾位置也就是R位子,就算我最倒霉没猜中承担了一个R的代价,继续在[L…R-1]范围内继续搞
可能性3:这个可能性可就多了[L+1…R-1]范围内的数字我全试抓一个最好的答案更新最好的答案。

4.对应代码

class Solution {
public:
    vector<vector<int>>dp;
    int getMoneyAmount(int n) {
        dp.resize(n+1,vector<int>(n+1,-1));
         return process(1,n);
    }
    //现在我已经锁定了答案一定就在[L....R]范围内返回我最倒霉的情况下需要的最小现金数
    int process(int L,int R)
    {
        //只有一个数
        if(L==R){
            return 0;
        }
        //两个数我们肯定猜L就算我最倒霉那么我承担的代价也就算L
        if(L==R-1){
            return L;
        }
        if(dp[L][R]!=-1){
            return dp[L][R];
        }
        int p1=process(L+1,R)+L;//我就猜L
        int p2=process(L,R-1)+R;//就猜R
        //(L.....R)位置我全试
        int ans=min(p1,p2);
        for(int i=L+1;i<R;i++){
            int left=process(L,i-1);
            int right=process(i+1,R);
            int cur=max(left,right)+i;//我最倒霉
            ans=min(ans,cur);
        }
        dp[L][R]=ans;
        return ans;
    }
};

二进制中1的个数为target的数量

1.题目描述

限制:0 <= start <= end,0 <= target <= 64[start,end]范围上的数字,有多少数字二进制中1的个数等于target。

2.解题思路

最容易想到的就是遍历这个范围内的每一个数字,求每个数字当中二进制为1的数量如果数量等于target我们就累加答案。思路非常的简单,对于暴力解来说。下面我们先来看看暴力解是怎么写的

3.对应代码:

typedef long long ll;
//限制:0 <= start <= end,0 <= target <= 64
//[start, end]范围上的数字,有多少数字二进制中1的个数等于target
int BitOne(ll num)
{
	//获取某一个数字当中二进制当中1的个数

	int ans = 0;
	while (num)
	{
		if (num % 2 == 1) {
			ans++;
		}
		num /= 2;
	}
	return ans;
}
ll GetNums1(ll start, ll end, int target)
{
	ll ans = 0;
	//一个一个开始尝试
	for (ll i = start; i <= end; i++) {
		if (BitOne(i) == target) {
			ans++;
		}
	}
	return ans;
}

下面我们来看看比较优良的方法,这种方法真的不是人能想的。首先我们想一个问题

1.如果我们能够求出[0…start]范围内二进制位当中个数等于target的数量有几个假设为a,我们也能求出[0…end]范围内二进制位当中个数等于target的数量假设为b,那么答案不就是b-a个吗
2.那么现在的关键就在于如何求[0…a]范围内二进制当中等于target的数量,现在我们可以这样进行尝试我们先把a二进制当中最高位为1的数量拿到。从这个比特位开始进行尝试,每个比特位的位置可以填1或者0但是所做的选择必须对应的数值必须小于a。于是我们就可以进行尝试:
num[h…index+1]决定完了,需要保证之前做的决定不能比num要大
可能性:
(1)之前做的决定已经小于num对应的状态了
(2)之前做的决定已经等于num对应的状态了index…去做决定吧
less1说明做的决定小于num所对应的状态,less0说明之前做的决定等于num所对应的前缀状态rest代表还剩下几个1,[0…index].[index…]有多少个决定并将不能超过num

下面让我们来看看代码:

#include<iostream>
using namespace std;
typedef long long ll;
//限制:0 <= start <= end,0 <= target <= 64
//[start, end]范围上的数字,有多少数字二进制中1的个数等于target
int BitOne(ll num)
{
	//获取某一个数字当中二进制当中1的个数

	int ans = 0;
	while (num)
	{
		if (num % 2 == 1) {
			ans++;
		}
		num /= 2;
	}
	return ans;
}
ll GetNums1(ll start, ll end, int target)
{
	ll ans = 0;
	//一个一个开始尝试
	for (ll i = start; i <= end; i++) {
		if (BitOne(i) == target) {
			ans++;
		}
	}
	return ans;
}
//num[h...........index+1]决定完了,需要保证之前做的决定不能比num要大
/*可能性:(1)之前做的决定已经小于num对应的状态了
          (2)之前做的决定已经等于num对应的状态了
		  index.............去做决定吧
		  less==1说明做的决定小于num所对应的状态
		  less==0说明之前做的决定等于num所对应的前缀状态
		  rest代表还剩下几个1
		  [0.........index]
		  [index.........]有多少个决定并将不能超过num
*/
ll process(int index, int rest, int less, int num)
{
	if (rest > index + 1) {
		return 0;
	}
	//搞定了找到了一种方案
	if (rest == 0) {
		return 1;
	}
	//小于num
	if (less == 1)
	{
		if (index + 1 == rest) {
			return 1;
		}
		else
		{
			return process(index - 1, rest, less, num) + process(index - 1, rest - 1, less, num);
		}
	}

	else
	{
		if (index + 1 == rest) {

			return (num & (1 << index)) == 1 ? process(index - 1, rest - 1, less, num) : 0;
		}

		if ((num & (1 << index)) == 0) {
			return process(index - 1, rest, 0, num);
		}
		else//index位置为1
		{
			return process(index - 1, rest - 1, 0, num) + process(index - 1, rest, 1, num);
		}
	}

}


ll GetNums2(ll start, ll end, int target)
{
	if (start < 0 || end<0 || start>end || target < 0) {
		return 0;
	}
	if (start == 0 && end == 0) {
		return target == 0 ? 1 : 0;
	}
	int ehigh = 62;
	//寻找最高位为1的状态
	while ((end & (1LL << ehigh)) == 0) {
		ehigh--;
	}
	if (start == 0) {
		return process(ehigh, target, t, end);
	}
	else {
		start--;//为了方便做差
		
		int shigh = 62;
		while (shigh>=0&&(start&(1LL<<shigh))==0)
		{
			shigh--;
		}

	
		return process(ehigh, target,0, end)
			- process(shigh, target,0, start);
	}
}


int main()
{
	srand(time(0));
	int times = 1000;
	for (int i = 0; i < times; i++)
	{
		int start = rand() % 100 + 1;
		int end = rand() % 1000 + 1;
		int target = rand() % 100 + 1;
		if (start <= end)
		{
			int ans1 = GetNums1(start, end, target);
			int ans2 = GetNums2(start, end, target);
			if (ans1 != ans2) {
				cout << "错了" << endl;
				return 0;
			}
			
		}
	}
	cout << "对了" << endl;
}