力扣最热一百题——验证二叉搜索树

目录

题目链接:98. 验证二叉搜索树 - 力扣(LeetCode)

题目描述

示例

提示:

二叉搜索树的要求

解法一:采用中序遍历

中序遍历的定义

为什么二叉搜索树的中序遍历是严格递增的

二叉搜索树(BST)的性质:

中序遍历的结果:

举例:

思路解析

Java写法:

C++写法:

运行时间

时间复杂度和空间复杂度

时间复杂度:

空间复杂度:

解法二:递归遍历

Java写法:

所以正确的Java写法如下

C++写法:

运行时间

时间复杂度和空间复杂度

1. 时间复杂度(Time Complexity)

2. 空间复杂度分析(Space Complexity)

1. 递归栈空间:

2. 二叉树的高度:

3. 所以:

总结

解决方案:

关键点:


题目链接:98. 验证二叉搜索树 - 力扣(LeetCode)

注:下述题目描述和示例均来自力扣

题目描述

给你一个二叉树的根节点 root ,判断其是否是一个有效的二叉搜索树。

有效 二叉搜索树定义如下:

  • 节点的左

    子树

    只包含 小于 当前节点的数。
  • 节点的右子树只包含 大于 当前节点的数。
  • 所有左子树和右子树自身必须也是二叉搜索树。

示例

示例 1:

输入:root = [2,1,3]
输出:true

示例 2:

输入:root = [5,1,4,null,null,3,6]
输出:false
解释:根节点的值是 5 ,但是右子节点的值是 4 。

提示:

  • 树中节点数目范围在[1, 104] 内
  • -2^{31} <= Node.val <= 2^{31} - 1


二叉搜索树的要求

在二叉搜索树中,任意节点的值都必须满足以下条件:

  • 左子树的所有节点值都小于当前节点的值。
  • 右子树的所有节点值都大于当前节点的值。
  • 左右子树也必须分别满足相同的条件。

解法一:采用中序遍历

中序遍历的定义

中序遍历(Inorder Traversal) 是指按照以下顺序访问二叉树的每个节点:

  • 左子树(递归)
  • 根节点
  • 右子树(递归)

        也就是,对于树的每个节点,首先访问其左子树,然后再访问根节点,然后再访问其右子树。

为什么二叉搜索树的中序遍历是严格递增的

二叉搜索树(BST)的性质:
  • 左子树的所有节点值都小于根节点的值
  • 右子树的所有节点值都大于根节点的值
  • 对于每个节点,其左子树的所有节点值都比该节点小其右子树的所有节点值都比该节点大

这意味着:

  • 如果你从根节点出发,中序遍历时,会先遍历左子树,再访问当前节点,最后访问右子树。
  • 在访问当前节点时,它的值必须大于其左子树的最大值(因为左子树的节点值都小于当前节点),并且它的值必须小于其右子树的最小值(因为右子树的节点值都大于当前节点)。
中序遍历的结果:
  • 如果我们对一棵二叉搜索树进行中序遍历,遍历顺序是 左子树 -> 根节点 -> 右子树,这会确保遍历的顺序是递增的。
  • 具体来说:
    1. 左子树的节点值小于当前节点的值
    2. 当前节点的值 小于右子树的节点值
    所以,在中序遍历过程中,访问到的每个节点的值都会是比前一个节点值更大的,这也就是 严格递增
举例:

假设有一棵简单的二叉搜索树,如下所示:

      4
     /  \
     2    6
     /  \  / \
     1   3 5   7

这棵树的中序遍历顺序如下:

  1. 先遍历左子树 (1, 2, 3)
    • 访问节点 1 -> 访问节点 2 -> 访问节点 3
  2. 访问根节点 4
  3. 然后遍历右子树 (5, 6, 7)
    • 访问节点 5 -> 访问节点 6 -> 访问节点 7

所以中序遍历得到的节点值是 1, 2, 3, 4, 5, 6, 7,这些值是 严格递增 的。

思路解析

  1. 二叉搜索树(BST)的性质:

    • 对于一个二叉搜索树,中序遍历 得到的节点值应该是 严格递增 的。
    • 也就是说,若中序遍历得到的节点值 v1, v2, v3, ..., vn 满足 v1 < v2 < v3 < ... < vn,那么该树是一个有效的二叉搜索树。
  2. 中序遍历的迭代实现:

    • 使用一个栈来模拟中序遍历的过程。
    • 栈用于保存当前节点的左子树路径,直到节点的左子树为空时,才会处理当前节点并遍历右子树。
  3. 检查 BST 的有效性:

    • 通过一个变量 inorder 来记录上一个访问的节点值(也就是上一个中序遍历的值)。
    • 如果当前节点的值小于或等于 inorder,则说明树不是一个有效的二叉搜索树,因为 BST 中序遍历的值必须是严格递增的。
    • 如果当前节点值大于 inorder,则更新 inorder 为当前节点的值,并继续遍历右子树。

  long inorder = -Long.MAX_VALUE;:用 inorder 变量来记录上一个节点的值,初始化为一个极小值。其实这第一种解法是我重新写的,因为每次的二叉树我都是先递归研究的所以,这里相同的问题我就不再赘述了,可以看下面解法二中的问题解决所提到的方法,为什么使用long。

Java写法:

/**
 * Definition for a binary tree node.
 * public class TreeNode {
 *     int val;
 *     TreeNode left;
 *     TreeNode right;
 *     TreeNode() {}
 *     TreeNode(int val) { this.val = val; }
 *     TreeNode(int val, TreeNode left, TreeNode right) {
 *         this.val = val;
 *         this.left = left;
 *         this.right = right;
 *     }
 * }
 */
class Solution {
    public boolean isValidBST(TreeNode root) {
        // 使用栈来模拟中序遍历
        Deque<TreeNode> stack = new LinkedList<TreeNode>();
        
        // 用来记录上一个访问的节点的值,初始化为负无穷
        long checkValue = -Long.MAX_VALUE;

        // 遍历树,栈不为空或者当前节点不为null时继续处理
        while (!stack.isEmpty() || root != null) {
            // 先遍历左子树,压入栈中
            while (root != null) {
                stack.push(root);
                root = root.left;
            }

            // 弹出栈顶节点
            root = stack.pop();

            // 如果当前节点值小于等于上一个节点的值,说明不是二叉搜索树
            if (root.val <= checkValue) {
                return false;
            }

            // 更新当前节点的值
            checkValue = root.val;

            // 处理右子树
            root = root.right;
        }
        // 如果遍历完树没有发现问题,说明是有效的二叉搜索树
        return true;
    }
}


C++写法:

#include <stack>
#include <climits>  // 用于INT_MIN和INT_MAX

using namespace std;

struct TreeNode {
    int val;
    TreeNode *left;
    TreeNode *right;
    TreeNode() : val(0), left(nullptr), right(nullptr) {}
    TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
    TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {}
};

class Solution {
public:
    bool isValidBST(TreeNode* root) {
        // 用栈模拟中序遍历
        stack<TreeNode*> stack;
        
        // 用一个变量记录上一个节点的值,初始化为负无穷
        long inorder = LONG_MIN;

        // 遍历树,栈不为空或者当前节点不为null时继续
        while (!stack.empty() || root != nullptr) {
            // 先遍历左子树,压入栈
            while (root != nullptr) {
                stack.push(root);
                root = root->left;
            }

            // 弹出栈顶节点
            root = stack.top();
            stack.pop();

            // 如果当前节点的值小于等于前一个节点的值,则不是二叉搜索树
            if (root->val <= inorder) {
                return false;
            }

            // 更新前一个节点的值
            inorder = root->val;

            // 处理右子树
            root = root->right;
        }

        // 如果整个遍历过程中没有发现问题,返回 true
        return true;
    }
};

运行时间

时间复杂度和空间复杂度

时间复杂度:

  • 时间复杂度:O(N),其中 N 是树中的节点数。
    • 每个节点会被访问一次,栈的操作(压入和弹出)是常数时间 O(1)。
    • 所以整体的时间复杂度是树的节点数 N。

空间复杂度:

  • 空间复杂度:O(H),其中 H 是树的高度。
    • 在最坏的情况下(例如树是链式结构),栈会存储所有的节点,栈的大小为 O(N)。
    • 在最好的情况下(树是平衡的),栈的最大大小为树的高度 O(log N)。
    • 因此,空间复杂度是 O(H),其中 H 是树的高度。



解法二:递归遍历

  • 递归遍历: 通过递归的方式遍历二叉树的每一个节点,判断它是否符合二叉搜索树的要求。每个节点需要检查是否符合范围条件,这个范围是通过父节点传递下来的。

  • 递归函数 checkIsBSTree

    • 输入参数: 每次递归调用时,node表示当前节点,smallValue表示当前节点值的下限,largeValue表示当前节点值的上限。对于每个节点,我们都需要检查它的值是否在smallValuelargeValue之间。
    • 返回值: 如果当前节点的值满足条件,并且它的左子树和右子树也满足条件,返回true;否则返回false
  • 判断条件:

    • 空节点: 如果当前节点为空(即叶子节点的子节点),直接返回true,表示空节点符合任何二叉搜索树的要求。
    • 值的有效性: 如果当前节点的值不在smallValuelargeValue之间,则返回false,说明当前节点不符合BST的要求。
  • 递归左右子树:

    • 对于左子树,我们传递更新后的上限node.val(即当前节点值)作为右边界,因为左子树的值必须小于当前节点的值。
    • 对于右子树,我们传递更新后的下限node.val作为左边界,因为右子树的值必须大于当前节点的值。
  • 递归的终止条件:

    • 当递归到达叶子节点时,返回true,表示叶子节点本身符合BST的要求。
    • 如果发现某个节点值不在合法范围内,立即返回false,停止递归。
  • 上下界的合理性:

    • 由于我们在递归过程中会传递上下限,这样我们就可以确保每个节点都严格遵守其父节点的限制(左子树的节点值小于父节点,右子树的节点值大于父节点)。
    • 使用Long.MIN_VALUELong.MAX_VALUE作为初始范围,保证可以处理任何int范围内的数字。

假设我们有一棵二叉树:

       5
       /  \
       1   7
            / \
             6   8
 

  1. 首先调用checkIsBSTree(root, Long.MIN_VALUE, Long.MAX_VALUE),即检查根节点5。
  2. 对于节点5,判断它是否在范围[Long.MIN_VALUE, Long.MAX_VALUE]内,显然是有效的。
  3. 然后递归检查它的左子树(节点1),checkIsBSTree(node.left, Long.MIN_VALUE, 5),检查1是否在范围[Long.MIN_VALUE, 5]内,符合条件。
  4. 再递归检查右子树(节点7),checkIsBSTree(node.right, 5, Long.MAX_VALUE),检查7是否在范围[5, Long.MAX_VALUE]内,符合条件。
  5. 继续递归检查节点7的左右子树,分别检查节点6和节点8,最终返回true,表示这棵树符合二叉搜索树的规则。

Java写法:

        这是我一开始的写法,就会有一个问题。

/**
 * Definition for a binary tree node.
 * public class TreeNode {
 *     int val;
 *     TreeNode left;
 *     TreeNode right;
 *     TreeNode() {}
 *     TreeNode(int val) { this.val = val; }
 *     TreeNode(int val, TreeNode left, TreeNode right) {
 *         this.val = val;
 *         this.left = left;
 *         this.right = right;
 *     }
 * }
 */
class Solution {
    public boolean isValidBST(TreeNode root) {

        return checkIsBSTree(root, Integer.MIN_VALUE, Integer.MAX_VALUE);
    }

    /**
        用于判断是否在范围内
        左子树的值要在(smallValue, node.val)
        右子树的值要在(node.val, largeValue)
     */
    public boolean checkIsBSTree(TreeNode node, int smallValue, int largeValue){
        // 到了叶子节点还没发现不符合要求的则返回true
        if(node == null){
            return true;
        }

        // 判断节点值的合理性
        if(node.val <= smallValue || node.val >= largeValue){
            return false;
        }

        boolean left = checkIsBSTree(node.left, smallValue, node.val);
        boolean right = checkIsBSTree(node.right, node.val, largeValue);

        return left && right;

    }
}

        在执行到如下的示例的时候会毫不吝啬的错了。

        然后一开始我没反应过来,就直接做了个单节点判断,防止它继续报错。就在主方法加了如下的代码:

        if(root.left == null && root.right == null){
            return true;
        }

        就是校验了一下单节点,好了,刚才的用例就那么完美的过去了,然后我继续运行重新发现还有更加恐怖的事情发生了,这个用例又报错了。

        

        直到现在我才反应过来这个数字,这个数字就是在int类型下的最大的和最小的值,我们可以验证一下。

        验证代码

public class test {
    public static void main(String[] args) {
        System.out.println( "integer下的最大值" + Integer.MAX_VALUE);
        System.out.println( "integer下的最小值" + Integer.MIN_VALUE);
        System.out.println( "long下的最大值" + Long.MIN_VALUE);
        System.out.println( "long下的最小值" + Long.MIN_VALUE);
    }
}

        结果

integer下的最大值  2147483647    2^{31}
integer下的最小值 -2147483648    2^{31} - 1
long下的最大值   9223372036854775807  2^{63}
long下的最小值  -9223372036854775808  2^{63} - 1

 

        然后我就水灵灵了重新注意到了题目的取值范围如下。

        那么它就刚好可以取到2147483647这个值,而在我的判断false的逻辑中,写的是如下的代码。

        // 判断节点值的合理性
        if(node.val <= smallValue || node.val >= largeValue){
            return false;
        }

        这里就直接出事了,因为这里的node.val和largeValue是一样的,直接误判了,原因如下。

        Java中的Integer.MIN_VALUE-2147483648Integer.MAX_VALUE2147483647,但是在判断树是否为二叉搜索树(BST)时,算法会将Integer.MAX_VALUE作为有效的右子树上界。因此,当node.val的值为2147483647时,node.val >= Integer.MAX_VALUE会成立,导致算法错误地认为这是一个无效的节点。

        呃这,那就直接采用long吧,其实这是一个好习惯对于算法比赛来说,因为真的很容易忽略这一点,但就是这一点就容易导致问题出现,我们追求的AC题目而不是让我们的代码在最快的执行速度下拥有最小的内存。

所以正确的Java写法如下

/**
 * Definition for a binary tree node.
 * public class TreeNode {
 *     int val;
 *     TreeNode left;
 *     TreeNode right;
 *     TreeNode() {}
 *     TreeNode(int val) { this.val = val; }
 *     TreeNode(int val, TreeNode left, TreeNode right) {
 *         this.val = val;
 *         this.left = left;
 *         this.right = right;
 *     }
 * }
 */
class Solution {
    public boolean isValidBST(TreeNode root) {
        return checkIsBSTree(root, Long.MIN_VALUE, Long.MAX_VALUE);
    }

    /**
        用于判断是否在范围内
        左子树的值要在(smallValue, node.val)
        右子树的值要在(node.val, largeValue)
     */
    public boolean checkIsBSTree(TreeNode node, long smallValue, long largeValue){
        // 到了叶子节点还没发现不符合要求的则返回true
        if(node == null){
            return true;
        }

        // 判断节点值的合理性
        if(node.val <= smallValue || node.val >= largeValue){
            return false;
        }

        boolean left = checkIsBSTree(node.left, smallValue, node.val);
        boolean right = checkIsBSTree(node.right, node.val, largeValue);

        return left && right;

    }
}

C++写法:

#include <climits>  // 引入 LONG_MIN 和 LONG_MAX

// 定义二叉树节点结构
class TreeNode {
public:
    int val;        // 节点值
    TreeNode* left; // 左子树
    TreeNode* right; // 右子树
    
    // 构造函数
    TreeNode() : val(0), left(nullptr), right(nullptr) {}
    TreeNode(int val) : val(val), left(nullptr), right(nullptr) {}
    TreeNode(int val, TreeNode* left, TreeNode* right) : val(val), left(left), right(right) {}
};

class Solution {
public:
    // 判断是否是有效的二叉搜索树
    bool isValidBST(TreeNode* root) {
        return checkIsBSTree(root, LONG_MIN, LONG_MAX);
    }

private:
    // 辅助递归函数,检查子树是否符合二叉搜索树的条件
    bool checkIsBSTree(TreeNode* node, long long smallValue, long long largeValue) {
        // 如果当前节点为空,返回 true,表示空树是有效的
        if (node == nullptr) {
            return true;
        }

        // 判断当前节点的值是否在有效的范围内
        if (node->val <= smallValue || node->val >= largeValue) {
            return false;  // 如果当前节点值不在范围内,返回 false
        }

        // 递归检查左子树,范围是 (smallValue, node->val)
        bool left = checkIsBSTree(node->left, smallValue, node->val);
        // 递归检查右子树,范围是 (node->val, largeValue)
        bool right = checkIsBSTree(node->right, node->val, largeValue);

        // 左右子树都符合条件时,返回 true,否则返回 false
        return left && right;
    }
};

运行时间

时间复杂度和空间复杂度

1. 时间复杂度(Time Complexity)

时间复杂度取决于二叉树的节点数目和我们访问每个节点的时间。

  • 对于每个节点,我们都要进行常数时间的操作:检查节点值是否在合法范围内、递归检查左子树和右子树。
  • 由于我们是递归遍历整棵二叉树,且每个节点都只被访问一次,因此时间复杂度为 O(N),其中 N 是二叉树中的节点数目。

2. 空间复杂度分析(Space Complexity)

        在递归算法中,空间复杂度的关键因素是 递归栈 的深度,因为每一次递归调用都会将一个栈帧压入栈中。我们需要根据二叉树的结构来评估最大可能的递归栈深度。

1. 递归栈空间:
  • 每次递归调用都会为当前节点分配一个栈帧。
  • 因此,空间复杂度不仅取决于输入数据,还取决于递归的深度,即二叉树的高度。
2. 二叉树的高度:
  • 最坏情况下,二叉树呈现 链状结构,即每个节点只有一个子节点(左或右),树的高度为 n(树的节点数)。这种情况下,递归栈深度也为 n,所以空间复杂度为 O(n)
  • 最好情况下,二叉树是 完全平衡 的,树的高度为 log(n),这时递归栈深度为 log(n),空间复杂度为 O(log n)
3. 所以
  • 在递归过程中,由于每一层的递归调用会占用栈空间,空间复杂度取决于递归的深度。
  • 最坏情况下:当二叉树呈链状时,递归的深度为 n,因此空间复杂度为 O(n)
  • 最好情况下:当二叉树是完全平衡的时,递归的深度为 log n,因此空间复杂度为 O(log n)

总结

二叉搜索树的特点是:

  • 对于每个节点,左子树的所有节点值小于该节点,右子树的所有节点值大于该节点。
  • 中序遍历结果应该是严格递增的。

解决方案:

  1. 递归方式:通过递归地检查每个节点是否满足左子树小于该节点,右子树大于该节点,利用递归函数传递上下界来判断。

    • 时间复杂度:O(N),遍历每个节点一次。
    • 空间复杂度:O(H),H 是树的高度,最坏情况下递归栈的深度为树的高度。
  2. 迭代方式:使用栈模拟中序遍历,同时检查每次访问的节点值是否符合严格递增的要求。

    • 时间复杂度:O(N),每个节点访问一次。
    • 空间复杂度:O(H),栈的最大深度为树的高度,最坏情况下为 O(N)。

关键点:

  • 通过中序遍历检查树的节点是否严格递增。
  • 迭代方式通过显式栈模拟递归,避免了递归栈溢出的风险,但在极端不平衡树上可能需要更多空间。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/909183.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

golang 实现 bitcoin内核:编码实现椭圆曲线上的点

比特币在很大程度上依赖于一个称为椭圆曲线的数学对象&#xff0c;如果没有这个数学结构&#xff0c;比特币就像海滩上的城堡&#xff0c;随时可能崩溃。什么是椭圆曲线&#xff0c;它的方程是这样的&#xff1a;y^2 x^3 ax b&#xff0c;它的形状就像下面这样&#xff1a; …

[免费]基于Python的Django+Vue3在线考试系统【论文+源码+SQL脚本】

大家好&#xff0c;我是java1234_小锋老师&#xff0c;看到一个不错的基于Python的DjangoVue3在线考试系统&#xff0c;分享下哈。 项目视频演示 【免费】基于Python的DjangoVue3在线考试系统 Python毕业设计_哔哩哔哩_bilibili 项目介绍 本论文提出并实现了一种基于Python…

API网关 - JWT认证 ; 原理概述与具体实践样例

API网关主要提供的能力&#xff0c;就是协议转换&#xff0c;安全&#xff0c;限流等能力。 本文主要是分享 如何基于API网关实现 JWT 认证 。 包含了JWT认证的流程&#xff0c;原理&#xff0c;与具体的配置样例 API网关认证的重要性 在现代Web应用和微服务架构中&#x…

【保姆级教程】实操 Linux 磁盘管理:硬盘选型 分区挂载

最近&#xff0c;Linux 服务器自带的固态硬盘&#xff0c;空间告警&#xff0c;急需加上一块新的硬盘来救急。 今日分享&#xff0c;系统梳理下 Linux 下挂载磁盘的详细步骤和注意事项&#xff0c;方便日后翻阅&#xff0c;也给有类似需求的小伙伴一点帮助。 1. SSD&#xff…

平衡二叉树(递归)

给定一个二叉树&#xff0c;判断它是否是 平衡二叉树.平衡二叉树 是指该树所有节点的左右子树的深度相差不超过 1。 示例 1&#xff1a; 输入&#xff1a;root [3,9,20,null,null,15,7] 输出&#xff1a;true示例 2&#xff1a; 输入&#xff1a;root [1,2,2,3,3,null,null,4…

雷池社区版新版本功能防绕过人机验证解析

前两天&#xff0c;2024.10.31&#xff0c;雷池社区版更新7.1版本&#xff0c;其中有一个功能&#xff0c;新增请求防重放 更新记录&#xff1a;hhttps://docs.waf-ce.chaitin.cn/zh/%E7%89%88%E6%9C%AC%E6%9B%B4%E6%96%B0%E8%AE%B0%E5%BD%95 仔细研究了这个需求&#xff0c;…

黑龙江某涝区泵闸站自动化、信息化改造项目案例

项目背景 黑龙江某地区紧邻松花江&#xff0c;雨季时降雨量增大&#xff0c;排水渠水位上涨&#xff0c;如果出现排涝不及时&#xff0c;水位过高时会漫入周边农田&#xff0c;引发洪涝灾害&#xff0c;对作物生长造成重大损害。为应对这一问题&#xff0c;自今年起&#xff0c…

Java 多线程(八)—— 锁策略,synchronized 的优化,JVM 与编译器的锁优化,ReentrantLock,CAS

前言 本文为 Java 面试小八股&#xff0c;一句话&#xff0c;理解性记忆&#xff0c;不能理解就死背吧。 锁策略 悲观锁与乐观锁 悲观锁和乐观锁是锁的特性&#xff0c;并不是特指某个具体的锁。 我们知道在多线程中&#xff0c;锁是会被竞争的&#xff0c;悲观锁就是指锁…

【Spring IOC】实现一个简单的 Spring 容器

1. 理解知识 Spring 容器的作用 Spring 容器是Spring的核心&#xff0c;用来管理Bean对象的。容器将创建对象&#xff0c;把它们连接在一起&#xff0c;配置它们&#xff0c;并管理他们的整个生命周期从创建到销毁。 Bean 对象的管理 当一个 Bean 对象交给 Spring 容器管理…

非线性数据结构之图

一、有向图&#xff08;Directed Graph&#xff09; 1. 定义 有向图是一个由顶点&#xff08;节点&#xff09;和有方向的边&#xff08;弧&#xff09;组成的图。在有向图中&#xff0c;每条边都有一个起点和一个终点&#xff0c;表示从一个顶点到另一个顶点的关系。 2. 特…

【算法】——滑动窗口专题

阿华代码&#xff0c;不是逆风&#xff0c;就是我疯 你们的点赞收藏是我前进最大的动力&#xff01;&#xff01; 希望本文内容能够帮助到你&#xff01;&#xff01; 目录 一&#xff1a;长度最小的子数组 二&#xff1a;无重复字符的最长子串 三&#xff1a;最大连续1的个…

目前主流的人工智能学习框架有哪些?

随着人工智能&#xff08;AI&#xff09;技术的蓬勃发展&#xff0c;越来越多的AI学习框架相继推出&#xff0c;成为开发者、研究人员和企业构建机器学习&#xff08;ML&#xff09;和深度学习&#xff08;DL&#xff09;模型的首选工具。AI学习框架不仅提供了丰富的工具库和函…

揭开自然语言处理(NLP)的神秘面纱

时间&#xff1a;2024年 11月 05日 作者&#xff1a;小蒋聊技术 邮箱&#xff1a;wei_wei10163.com 微信&#xff1a;wei_wei10 音频&#xff1a;喜马拉雅 大家好&#xff0c;欢迎来到“小蒋聊技术” &#xff0c;我是小蒋&#xff01;。小蒋最近在学习清华大模型课程&…

C#:强大而优雅的编程语言

在当今的软件开发领域&#xff0c;C#作为一种广泛应用的编程语言&#xff0c;以其强大的功能、优雅的语法和丰富的生态系统&#xff0c;受到了众多开发者的喜爱。本文将深入探讨 C#的各个方面&#xff0c;展示它的魅力和优势。 一、C#的历史与发展 C#是由微软公司开发的一种面…

SQL CASE表达式与窗口函数

CASE 表达式是一种通用的条件表达式&#xff0c;类似于其他编程语言中的if/else语句。 窗口函数类似于group by&#xff0c;但是不会改变记录行数&#xff0c;能扫描所有行&#xff0c;能对每一行执行聚合计算或其他复杂计算&#xff0c;并把结果填到每一行中。 1 CASE 表达式…

C++之位算法

位算法 常见位运算总结 位1的个数 给定一个正整数 n&#xff0c;编写一个函数&#xff0c;获取一个正整数的二进制形式并返回其二进制表达式中 设置位 的个数&#xff08;也被称为汉明重量&#xff09;。 示例 1&#xff1a; 输入&#xff1a;n 11 输出&#xff1a;3 解释…

【OJ题解】C++实现字符串大数相乘:无BigInteger库的字符串乘积解决方案

&#x1f984;个人主页: 起名字真南 &#x1f984;个人专栏:【数据结构初阶】 【C语言】 【C】 【OJ题解】 目录 1. 引言2. 题目分析示例&#xff1a; 3. 解题思路4. C代码实现5. 代码详解6. 时间和空间复杂度分析7. 边界情况分析8. 总结 1. 引言 在开发中&#xff0c;有时我们…

用Python将PDF表格提取到文本、CSV和Excel文件中

从PDF文档中提取表格并将其转换为更易于处理的格式&#xff08;如文本、CSV和Excel文件&#xff09;&#xff0c;是数据分析和信息管理中的常见需求。此过程可显著简化表格数据的处理&#xff0c;使数据的操作、分析和与其他数据集的集成更加便捷。无论是财务报表、研究论文&am…

如何在 IntelliJ IDEA 中调整 `Ctrl+/` 快捷键生成注释的位置

前言 在使用 IntelliJ IDEA 编写代码时&#xff0c;注释是代码可读性和维护性的重要组成部分。IDEA 提供了快捷键 Ctrl/ 用于快速生成单行注释。然而&#xff0c;默认情况下&#xff0c;使用此快捷键生成的注释会出现在行首&#xff0c;导致注释与代码之间存在较大的空格&…

深入理解对象池 sync.Pool

文章目录 前言应用使用源码走读数据结构Get获取对象Put归还对象poolDeque分析GC时 总结 前言 当多个 goroutine 都需要创建同⼀种对象的时候&#xff0c;如果 goroutine 数量过多&#xff0c;导致对象的创建剧增&#xff0c;进⽽导致 GC 压⼒增大。形成下面的恶性循环&#xf…