【2022/12/17】每日两题+每日effective c++

每日两题

每天坚持,加油!责任感来自于对自我规则以及高要求!

1、贪心算法:我要监控二叉树!

给定一个二叉树,我们在树的节点上安装摄像头。

节点上的每个摄影头都可以监视其父对象、自身及其直接子对象。

计算监控树的所有节点所需的最小摄像头数量。

示例 1:

在这里插入图片描述
输入:[0,0,null,0,0]
输出:1
解释:如图所示,一台摄像头足以监控所有节点。

这道题在leetcode里面是hard级别的题目。是贪心算法的题目,也就是局部最优等于全局最优。

要局部最优,那么叶子节点不能是摄像头,因为叶子节点的父节点是摄像头就可以了,这样一定可以减少摄像头的个数,而且保证叶子节点也可以被覆盖到,所以一定从叶子节点开始遍历。所以本题可以选择后序遍历

结点可以分为未覆盖结点,已覆盖结点以及摄像头结点。

其次就是对于空结点的处理,因为空结点不需要被覆盖,也不起摄像头作用,所以设为已覆盖结点即可。

然后就是遍历的返回。如果两个子节点都是已覆盖结点,那么其父节点为未覆盖结点就可以了,交给父节点上面的结点负责。如果父节点为根节点,那么只能设为摄像头了。

如果两个结点有任何一个是未覆盖结点,那么其父节点一定是摄像头结点。

排除掉前面两种情况,如果两个结点有任何一个是摄像头结点,那么父节点就是已覆盖结点。

按照这个逻辑遍历一遍,就可以求得最少摄像头数量,代码如下:

class Solution { 
private:
    int result = 0;
    // 0 未覆盖 1 有摄像头 2 已覆盖
    int traversal(TreeNode *cur){
        if(cur == nullptr) return 2;
        // 后序遍历
        int left = traversal(cur->left);
        int right = traversal(cur->right);
        // 分三种情况
        if(left == 2 && right == 2) return 0;
        if(left == 0 || right == 0) {
            result++;
            return 1;
        }
        if(left == 1 || right == 1) return 2;
        return -1;
    }

public:
    int minCameraCover(TreeNode* root) {
        if(traversal(root) == 0) result++; //一种特殊情况.头结点的子节点都已被覆盖
        return result;
    } };

2、斐波那契数

动态规划五大步:
1、给出dp[i]以及i的定义。
2、给出状态转移公式。
3、给出初始值。
4、给出遍历顺序,从前到后还是从后到前。
5、自己推一遍看行不行。

这道题简单直接给代码:

class Solution {
public:
    int fib(int n) {
        if (n <= 1) return n;
        int dp[2] = {0, 1};
        for(int i = 0; i < (n-1); i++){
            int tmp = dp[1];
            dp[1] = dp[0] + dp[1];
            dp[0] = tmp;
        }
        return dp[1];
    }
};

每日effective c++

14 在管理资源类中小心copy行为

当使用RAII的时候,如果管理的对象不是单纯地new和delete堆内存,那么就要考虑自己想实现的功能。

如果是考虑禁止复制,可以考虑uncopyable类或者使用unique_ptr(使用unique_ptr还可以实现转移底部资源的拥有权),如果考虑引用计数法,可以考虑使用shared_ptr。unique_ptr和shared_ptr都可以传递独特的删除器,只要传函数指针进去即可。

比如想管理互斥锁的上锁和解锁,就可以,:

class Lock {
public:
	explict Lock(Mutex* pm) : mutexPtr(pm, unlock){
		lock(mutexPtr.get());
	}
private:
	shared_ptr<Mutex> mutexPtr;
};

这样就可以随便复制,不用担心析构问题。

这里解释一下为什么要用explict,考虑如下代码:

class Test1
{
public:
	Test1(int n) { num = n; };   //普通构造函数
private:
	int num;
};

class Test2
{
public:
	explicit Test2(int n) { num = n; }   //explicit(显示)构造函数
private:
	int num;
};

int main()
{
	Test1 t1 = 12;      //隐式调用其构造函数,成功
	Test2 t2 = 12;      //编译错误,不能隐式调用其构造函数
	Test2 t3(12);       //显示调用成功
	return 0;
}

转载自https://blog.csdn.net/qq_45662588/article/details/121328778
那么当Test2 t2 = 12;的时候,其实编译器是希望可以将12这个int类型转化成Test2类型的,然后通过拷贝赋值的方式赋给t2。但是如果我们不想这样,就要使用explict。当构造函数为单参数的时候就要额外注意了!!其实在函数调用的时候,就经常会发生拷贝赋值的情况,所以要额外注意了哈!比如如下:

class Sales_item  
{  
public:  
    std::istream& input(std::istream& in);  
    std::ostream& output(std::ostream& out);  
    inline double avg_price() const;  
    bool same_isbn(const Sales_item &rbs) const  
    {  
        return isbn == rbs.isbn;  
    }  
    Sales_item add(Sales_item& other);  
    Sales_item(const std::string &book = "7115145547"):isbn(book),units_sold(0),revenue(0.0){}  
private:  
    std::string isbn;  
    unsigned units_sold;  
    double revenue;  
};  

转载自https://www.cnblogs.com/hong2016/p/6730613.html
就容易出现可以直接把string给add函数使用的情况,

Sales_item trans1;  
string null_book = "9-999-99999-9";  
trans1.same_isbn(null_book);  

而这不是我们想要的结果。

15 在资源管理类中提供对原始资源的访问

要访问智能指针的原始资源,可以使用.get(),*ptr,ptr->mem的形式,一般的RAII资源也应该提供相应的函数。

当RAII资源提供相应函数的时候,就要思考,如何提供,如果使用显式的转换,用户调用起来会麻烦,但是隐式转换又会不安全,所以还是推荐按显式转换。

显式转换就是直接在RAII类中(比如f)定义一个函数(比如get()),return资源就可以了,就是用户使用起来会麻烦(要f.get()),觉得为啥我不直接调你这个资源,于是直接避开RAII类,就会出现问题(一般也不会怎么傻哈)。

隐式转换就是直接用operator 资源名() const返回资源,那么你就直接拿f使用就完事了,但问题就是,你可以直接把底层资源拷贝赋值给别人,即使你不想这样,如果底层资源不小心因为这种原因被删了,或者原RAII类对象被删了,那么就会造成资源管理失败问题。

有效的C++ STL是指标准模板库(Standard Template Library),它是C++编程中广泛使用的一组模板类和函数的集合,提供了一些常用的数据结构和算法实现,通过使用STL,程序员可以更加高效地开发出稳定、可读性强且可重用的代码。 STL包括了三个主要组件:容器(Container)、算法(Algorithm)和迭代器(Iterator)。 容器是STL的基础,它提供了一系列的数据结构,如vector、list、map等,可以用来储存和操作各种类型的数据。 算法提供了一组通用算法,如排序、查找、拷贝、删除等等,可以直接应用于各种容器中的数据,使得程序员不需要自己实现这些基本算法,从而节省了开发时间。 迭代器是STL的一个重要概念,它可以将容器和算法进行有效地连接,通过迭代器,程序员可以以一种统一的方式来访问和操作容器中的元素,无需关心容器内部的具体实现。 使用STL,程序员可以极大地提高编程效率和代码质量。STL的模板类和函数经过了大量的测试和调试,保证了它们的稳定性和可靠性。此外,STL还具有高度的可重用性,程序员可以在不同的项目中重复使用同样的代码,从而减少了代码的重复编写和维护成本。 总之,STL是一种高效的C++编程工具,它通过提供一组模板类和函数,为程序员提供了各种常用的数据结构和算法实现,使得程序员能够更加高效地开发出稳定、可读性强且可重用的代码。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值