5.26


第一性原理——财富 - mdnice 墨滴

财富

财富的第一性原理是:将“未来”进行定价,并转化为可流通的“当前选择权”。

本质是“时间的交换与支配”。

人类最稀缺的资源不是黄金或石油,而是“时间”。每个人的时间都是有限的,且单向流逝。财富系统,是人类迄今为止发明的最伟大的工具,它为我们储存时间、交换时间、购买时间,甚至在某种意义上,创造时间。

剥离现象看本质

  • 金钱(货币):金钱本身毫无价值。它是一张“信任券”。你之所以接受它,是因为你相信在未来可以用它兑换到另一个人或组织的时间与劳动成果(商品或服务)。你的薪水,本质上就是你过去一个月时间的凝结物,你用它来购买他人未来的时间为你服务(例如,厨师为你做饭、司机为你开车)。

  • 储蓄:储蓄的本质不是在储存金钱,而是在储存“未来的选择权”。你牺牲当下的消费(放弃购买他人当下的时间),是为了在未来某个不确定的时刻,拥有支配他人时间的权力,以应对风险或抓住机遇。

  • 投资(股票/债券/房产):投资是对“未来时间”更高阶的支配。你购买一家公司的股票,本质上是在购买这家公司管理层和所有员工“未来一段时间”的集体智慧与劳动,并赌他们的未来能创造出比现在更多的价值。你是在用你储存的“过去的时间”(资本),去预订一个更高效、更庞大的“未来的时间”。

  • 债务(杠杆):债务是“透支未来的时间”。你向银行贷款,等于银行提前将一张基于社会总信用的“时间兑换券”给了你。你获得了“当下的选择权”,但代价是你必须用自己“未来的时间”(未来的收入)去偿还。国家发行国债,也是在将整个国家“未来的生产时间”作为抵押,换取当下的发展能力。

  • 知识/技能/品牌:这是无形的财富。它们之所以是财富,因为它们能极大地提高你“单位时间的价值”,或者能让你在未来以极低的成本,自动地“支配”他人的时间。一个知名作家(品牌)写一本书,可以在未来数十年里,持续地将读者的“时间”转化为自己的财富。

  • 通货膨胀:通胀的本质是“时间兑换券”的贬值。它意味着,你过去储存的“时间”,在未来能兑换到的“他人时间”变少了。这是对储蓄者的一种无形剥削。

所以,人类一切的经济活动,从生产、消费到投资、信贷,都是一场围绕“时间”展开的宏大游戏。财富,就是你在这场游戏中,能够支配他人“未来时间”的总量。


226. 翻转二叉树 - 力扣(LeetCode)

class Solution {
public:
    TreeNode* invertTree(TreeNode* root) {
        auto dfs = [&](this auto&& dfs , TreeNode* node)->void{
            if(node == nullptr)  return;
            swap(node->left , node->right);//直接交换即可
            dfs(node->left);
            dfs(node->right);
        };       
        dfs(root);
        return root;
    }
};

101. 对称二叉树 - 力扣(LeetCode)​ 🤡

不能在check里直接取p->left->val,会取空。

class Solution {
public:
    bool isSymmetric(TreeNode* root) {
       auto check = [&](this auto&& check , TreeNode* p , TreeNode* q)->bool{
          if(!p || !q)  return p == q;
          return (p->val == q->val) && check(p->left , q->right) && check(p->right , q->left);
       };

       return check(root->left , root->right);
    }
};

543. 二叉树的直径 - 力扣(LeetCode)🤡

🤡错误解:

class Solution {
public:
    int diameterOfBinaryTree(TreeNode* root) {
      int res = 0;  
      auto dfs = [&](this auto&& dfs , TreeNode* node)->int{
            if(node == nullptr)  return -1;

            res = max(res , dfs(node->left) + dfs(node->right) + 2);
            return max(dfs(node->left) , dfs(node->right));//这里返回的是当前子树的最长链,需要+1,改为 return max(dfs(node->left) + 1 , dfs(node->right) + 1);
        };
        dfs(root);
        return res;
    }
};

记住本题型的两个关键概念:

  • :从子树中的叶子节点到当前节点的路径。把最长链的长度,作为 dfs 的返回值。根据这一定义,空节点的链长是 −1,叶子节点的链长是 0。

  • 直径:等价于由两条(或者一条)链拼成的路径。我们枚举每个 node,假设直径在这里「拐弯」,也就是计算由左右两条从下面的叶子节点到 node 的链的节点值之和,去更新答案的最大值。

⚠注意:dfs 返回的是链的长度,不是直径的长度。

⚠注意:dfs 返回的是当前子树的最大链长(也可以理解为子树的高度),不包含当前节点和其父节点的这条边。

image-20250526143427572

class Solution {
public:
    int diameterOfBinaryTree(TreeNode* root) {
      int res = 0;  
      auto dfs = [&](this auto&& dfs , TreeNode* node)->int{
            if(node == nullptr)  return -1;

            int l_len = dfs(node->left) + 1;// 左子树最大链长+1
            int r_len = dfs(node->right) + 1;// 右子树最大链长+1

            res = max(res , l_len + r_len);// 两条链拼成路径
            return max(l_len , r_len);// 当前子树最大链长
        };
        dfs(root);
        return res;
    }
};

124. 二叉树中的最大路径和 - 力扣(LeetCode)

  • 链:从下面的某个节点(不一定是叶子,因为要保证路径和最大)到当前节点的路径。把这条链的节点值之和,作为dfs的返回值。如果节点值之和是负数,则返回0。

     return max(max(l_val , r_val) + node->val , 0);
    /*其实等价于
    return max({dfs(node->left) + node->val , dfs(node->right) + node->val , 0};
    与上题很相似,上题是 dfs结果  + 1,本题是  +node->val
    只不过因为直径拼接的时候如果写l_val = dfs(node->left) + node->val会导致重复算一次node->val
    
  • 直径:等价于由两条(或者一条)链拼成的路径。我们枚举每个node,假设直径在这里「拐弯」,也就是计算由左右两条从下面的某个节点(不一定是叶子)到node的链的节点值之和,去更新答案的最大值。

     ans = max(ans , l_val + r_val + node->val);
    

⚠注意:dfs返回的是链的节点值之和,不是直径的节点值之和

class Solution {
public:
    int maxPathSum(TreeNode* root) {
      int res = INT_MIN;  
      auto dfs = [&](this auto&& dfs , TreeNode* node)->int{
            if(node == nullptr)  return 0;
            //假设在node处拐点
            int l_len = dfs(node->left) ;// 左子树最大路径和
            int r_len = dfs(node->right) ;// 右子树最大路径和

            res = max(res , l_len + r_len + node->val);// 两条链拼成路径
            return max(max(l_len , r_len)  + node->val , 0);// 当前子树最大链长
        };
        dfs(root);
        return res;
    }
};

与上题统一的写法:

class Solution {
public:
    int maxPathSum(TreeNode* root) {
      int res = INT_MIN;  
      auto dfs = [&](this auto&& dfs , TreeNode* node)->int{
            if(node == nullptr)  return 0;
            //假设在node处拐点
            int l_len = dfs(node->left)  + node->val;// 左链最大路径和
            int r_len = dfs(node->right)  + node->val;// 右链最大路径和

            res = max(res , l_len + r_len - node->val);// 两条链拼成路径,node->val重复算了一次
            return max({l_len , r_len, 0});// 当前子树最大链长
        };
        dfs(root);
        return res;
    }
};

108. 将有序数组转换为二叉搜索树 - 力扣(LeetCode)

BST 的中序遍历是升序的,因此本题等同于根据中序遍历的序列恢复二叉搜索树。因此我们可以以升序序列中的任一个元素作为根节点,以该元素左边的升序序列构建左子树,以该元素右边的升序序列构建右子树,这样得到的树就是一棵二叉搜索树, 又因为本题要求高度平衡,因此我们每次选择升序序列的中间元素作为根节点。是一个递归的过程。

法一:左闭右闭区间,返回条件是 l>r

class Solution {
public:
    TreeNode* sortedArrayToBST(vector<int>& nums) {
        auto dfs = [&](this auto&& dfs , int l , int r)->TreeNode*{
            if(l > r)  return nullptr;

            int mid = (l + r) / 2;
            TreeNode* root = new TreeNode(nums[mid]);
            root->left = dfs(l ,mid - 1);
            root->right = dfs(mid + 1 , r);
            return root;
        };

        return dfs(0 , nums.size() - 1);
    }
};

法二:分治法(左闭右开区间,返回条件是 l == r)

示例 1 nums=[−10,−3,0,5,9],我们从数组正中间的数 nums[2]=0 开始,把数组一分为二,得到两个小数组:

  • 左:[−10,−3]。
  • 右:[5,9]。

答案由三部分组成:

  • 根节点:节点值为 nums[2]=0。
  • 把 nums[2] 左边的 [−10,−3] 转换成一棵平衡二叉搜索树,作为答案的左儿子。这是一个和原问题相似的子问题,可以递归解决。
  • 把 nums[2] 右边的 [5,9] 转换成一棵平衡二叉搜索树,作为答案的右儿子。这是一个和原问题相似的子问题,可以递归解决。

递归边界:如果数组长度等于 0,返回空节点

注意:答案可能不是唯一的。如果 n 是偶数,我们可以取数组正中间左边那个数作为根节点的值,也可以取数组正中间右边那个数作为根节点的值。下面代码取的是正中间右边那个数,即下标为 2n 的数(当 n 是偶数时)。

class Solution {
  //// 把 nums[left] 到 nums[right-1] 转成平衡二叉搜索树
public:
    TreeNode* sortedArrayToBST(vector<int>& nums) {
        auto dfs = [&](this auto&& dfs , int l , int r)->TreeNode*{
            if(l == r)  return nullptr;
            int m = l + (r - l) / 2;
            return new TreeNode(nums[m] , dfs(l , m) , dfs(m + 1 , r));//左子树是[l , m - 1],右子树[m + 1 , r - 1] 与上一题区间范围是一样的
        };

        return dfs(0 , nums.size());//注意右边开区间即取到nums.size()-1
    }
};
posted @ 2025-05-26 23:43  七龙猪  阅读(1)  评论(0)    收藏  举报
-->