当前位置: 首页 > news >正文

代码随想录——二叉树(二)

文章目录

      • 前言
      • 二叉树最大深度
      • 二叉树的最小深度
      • 翻转二叉树
      • 对称二叉树
      • 完全二叉树的节点个数
      • 平衡二叉树
      • 二叉树的所有路径
      • 左叶子之和
      • 找左下角的值
      • 路径总和
      • 从中序与后序序列构造二叉树
      • 最大二叉树
      • 合并二叉树
      • 二叉搜索树中的搜索
      • 验证二叉搜索树
      • 二叉搜索树的最小绝对差
      • 二叉树中的众数
      • 二叉树的最近公共祖先
      • 二叉搜索树的最近公共祖先
      • 二叉搜索树中的插入操作
      • 删除二叉搜索树中的节点
      • 修剪二叉搜索树
      • 将有序数组转换为二叉搜索树
      • 把二叉搜索树转换为累加树

前言

代码随想录系列也写了好几期了,我写代码随想录的题目主要是为了准备机试,这也是为什么我只用c语言的原因。而我更新文章是为了之后复习以及督促自己(看到有人点赞我真的很开心)。
然后是博客质量的问题,首先这个系列主要是为了日后复习的,所以很多是我了我自己看懂就行了,并没有很深刻地解释一些问题。还有一部分原因是我每天还要学习毕设的一些知识,我选了一个对我来说很有挑战的题目,因此要分配一些精力去学习,导致我没有很多的时间打磨的题解。最后谢谢大家的关注与点赞,希望可以帮助大家!

二叉树最大深度

104. 二叉树的最大深度

思路:DFS,递归

/*** Definition for a binary tree node.* struct TreeNode {*     int val;*     struct TreeNode *left;*     struct TreeNode *right;* };*/
int Max(int a,int b){return a>b?a:b;
}
int maxDepth(struct TreeNode* root) {if(root==NULL)return 0;return Max(maxDepth(root->left),maxDepth(root->right))+1;
}

二叉树的最小深度

111. 二叉树的最小深度

思路:DFS,递归

/*** Definition for a binary tree node.* struct TreeNode {*     int val;*     struct TreeNode *left;*     struct TreeNode *right;* };*/
int Min(int a,int b){return a<b?a:b;
}
int minDepth(struct TreeNode* root) {if(root==NULL)return 0;else if(root->left==NULL)return minDepth(root->right)+1;else if(root->right==NULL)return minDepth(root->left)+1; else return Min(minDepth(root->left),minDepth(root->right))+1;
}

翻转二叉树

226. 翻转二叉树

思路:将每个节点的左右孩子交换,按照任意一种遍历均可

下面是先序遍历代码

/*** Definition for a binary tree node.* struct TreeNode {*     int val;*     struct TreeNode *left;*     struct TreeNode *right;* };*/
struct TreeNode* invertTree(struct TreeNode* root) {//对于每个节点,交换左右孩子节点if(root==NULL)return root;else{struct TreeNode *temp=root->left;root->left=root->right;root->right=temp;invertTree(root->left);invertTree(root->right);return root;}
}

对称二叉树

101. 对称二叉树

思路:本来想着先用上一题的代码把二叉树翻转,然后通过比较翻转之后的树与原来的树是否相同判断原二叉树是否对称,但是我上面的代码直接改变了原二叉树的结构(不过思路应该是可行的)。然后发现直接比较根节点的左右子树是否对称就行了,逻辑和上面比较两个树是否相同差不多。

/*** Definition for a binary tree node.* struct TreeNode {*     int val;*     struct TreeNode *left;*     struct TreeNode *right;* };*/bool ismirror(struct TreeNode* a,struct TreeNode* b){if(a==NULL&&b==NULL)return true;else if(a==NULL)return false;else if(b==NULL)return false;else{if(a->val!=b->val)return false;else{return (ismirror(a->left,b->right)&&ismirror(a->right,b->left));}}
}
bool isSymmetric(struct TreeNode* root) {return ismirror(root->left,root->right);
}

完全二叉树的节点个数

222. 完全二叉树的节点个数

思路一:遍历一遍二叉树并计数(不过这样就浪费了完全二叉树的条件)

/*** Definition for a binary tree node.* struct TreeNode {*     int val;*     struct TreeNode *left;*     struct TreeNode *right;* };*/
void trval(struct TreeNode* root,int *num){if(root==NULL)return;*num+=1;trval(root->left,num);trval(root->right,num);
}
int countNodes(struct TreeNode* root) {int ans=0;trval(root,&ans);return ans;
}

思路二:下面介绍另一种方法。首先判断该完全二叉树是否为满二叉树:

  • 若为满二叉树,那么节点的个数为2^n-1,其中n为二叉树高度。
  • 若不为满二叉树,那么分别计算左右子树的节点数,然后相加。

那么,如何判断二叉树呢?

只需要判断根-左孩子-左孩子-左孩子…与跟-右孩子-右孩子-右孩子…这两条路径的长度是否相同即可。因为题目已知这是一颗完全二叉树,只要两边的节点存在,中间的节点一定存在。具体见代码

/*** Definition for a binary tree node.* struct TreeNode {*     int val;*     struct TreeNode *left;*     struct TreeNode *right;* };*/int countNodes(struct TreeNode* root) {if(root==NULL)return 0;//判断是否为满二叉树:是——>节点个数为2^n-1,n为深度//                  否——>分别计算左右子树的节点数,并相加struct TreeNode* Left=root->left;struct TreeNode* Right=root->right;int leftdepth=0,rightdepth=0;while(Left){leftdepth++;Left=Left->left;}while(Right){rightdepth++;Right=Right->right;}if(leftdepth==rightdepth)return (2<<leftdepth)-1;return countNodes(root->left)+countNodes(root->right)+1;
}

平衡二叉树

110. 平衡二叉树

思路:用定义,计算每个节点的平衡因子

/*** Definition for a binary tree node.* struct TreeNode {*     int val;*     struct TreeNode *left;*     struct TreeNode *right;* };*/
int depth(struct TreeNode* root){if(root==NULL)return 0;return fmax(depth(root->left),depth(root->right))+1;
}
bool isBalanced(struct TreeNode* root) {if(root==NULL)return true;if(fabs(depth(root->left)-depth(root->right))<=1){return isBalanced(root->left)&&isBalanced(root->right);}else{return false;}
}

二叉树的所有路径

257. 二叉树的所有路径

思路:按照一种遍历顺序(我使用的是DFS)遍历到叶子节点,存储路径上节点的值。思路很简单,实现有点麻烦。

/*** Definition for a binary tree node.* struct TreeNode {*     int val;*     struct TreeNode *left;*     struct TreeNode *right;* };*/
/*** Note: The returned array must be malloced, assume caller calls free().*/
void getpaths(struct TreeNode* root,char** s,int* stack,int top,int *returnSize){if(root==NULL)return;if(root->left!=NULL||root->right!=NULL){//当前节点非叶子节点stack[top++]=root->val;getpaths(root->left,s,stack,top,returnSize);getpaths(root->right,s,stack,top,returnSize);}else{char* t=malloc(sizeof(char)*100);int len=0;for(int i=0;i<top;i++){len+=sprintf(t+len,"%d->",stack[i]);}sprintf(t+len,"%d",root->val);s[(*returnSize)++]=t;}
}
char** binaryTreePaths(struct TreeNode* root, int* returnSize) {char **ans=malloc(sizeof(char*)*100);*returnSize=0;int stack[100+5];getpaths(root,ans,stack,0,returnSize);return ans;
}

左叶子之和

404. 左叶子之和

思路:遍历,找到所有左叶子,然后相加。

/*** Definition for a binary tree node.* struct TreeNode {*     int val;*     struct TreeNode *left;*     struct TreeNode *right;* };*/
//判断是否为叶子节点
bool isleaf(struct TreeNode* root){if(root==NULL)return false;if(root->left==NULL&&root->right==NULL)return true;return false;
}
int sumOfLeftLeaves(struct TreeNode* root) {if(root==NULL)return 0;int sum=0;if(isleaf(root->left)){//左孩子是叶子sum=sum+root->left->val+sumOfLeftLeaves(root->right);}else{sum=sum+sumOfLeftLeaves(root->left)+sumOfLeftLeaves(root->right);}return sum;
}

找左下角的值

513. 找树左下角的值

思路一:DFS,找到每个子树左下角的值,并计算子树的高度,若左子树更高,取左子树左下角的值;若右子树更高,取右子树左下角的值。

/*** Definition for a binary tree node.* struct TreeNode {*     int val;*     struct TreeNode *left;*     struct TreeNode *right;* };*/
bool isleaf(struct TreeNode* root){if(root==NULL)return false;if(root->left==NULL&&root->right==NULL)return true;return false;
}
int find(struct TreeNode* root,int* depth){//返回左下的值,并计算深度if(root==NULL){return 0;//这里随便返回一个值,因为如果传来一个空指针,计算的深度为0,不会选用}(*depth)++;if(isleaf(root)){return root->val;}int leftdepth=0;//左子树高度int rightdepth=0;//右子树高度int findleft=find(root->left,&leftdepth);//左子树的左下角int findright=find(root->right,&rightdepth);//左子树的左下角if(leftdepth>=rightdepth){(*depth)+=leftdepth;return findleft;}else {(*depth)+=rightdepth;return findright;} }
int findBottomLeftValue(struct TreeNode* root) {if(isleaf(root)){       return root->val;}int leftdepth=0;//左子树高度int rightdepth=0;//右子树高度int findleft=find(root->left,&leftdepth);//左子树的左下角int findright=find(root->right,&rightdepth);//左子树的左下角if(leftdepth>=rightdepth){return findleft;}else return findright; 
}

思路二:既然要取最后一层最左边的值,那么层序遍历也是可以的。这里有个技巧处理,先将左孩子入队,再将左孩子入队,这样最后一个入队的元素就是左下角元素。

/*** Definition for a binary tree node.* struct TreeNode {*     int val;*     struct TreeNode *left;*     struct TreeNode *right;* };*/int findBottomLeftValue(struct TreeNode* root) {int ans=0;struct TreeNode* queue[10000];int front=0;int tail=0;if(root==NULL)return 0;queue[front++]=root;while(front>tail){int t=front;while(tail<t){struct TreeNode* node=queue[tail++];if(node->right){queue[front++]=node->right;}if(node->left){queue[front++]=node->left;}ans=queue[front-1]->val;}}return ans;
}

路径总和

112. 路径总和

思路:递归,回溯

image.png

/*** Definition for a binary tree node.* struct TreeNode {*     int val;*     struct TreeNode *left;*     struct TreeNode *right;* };*/bool hasPathSum(struct TreeNode* root, int targetSum) {if(root==NULL)return false;if(root->left==NULL&&root->right==NULL){//如果是叶子节点return root->val==targetSum;}return hasPathSum(root->left,targetSum-root->val)||hasPathSum(root->right,targetSum-root->val);
}

从中序与后序序列构造二叉树

思路:递归

1.由后序遍历的最后一个元素得到根节点
2.再根据中序遍历序列和根节点,将中序遍历序列分为左右子树的中序遍历序列
3.左右子树的个数,将后序遍历序列分为左右子树的后序遍历序列

/*** Definition for a binary tree node.* struct TreeNode {*     int val;*     struct TreeNode *left;*     struct TreeNode *right;* };*/
//1.由后序遍历的最后一个元素得到根节点
//2.再根据中序遍历序列和根节点,将中序遍历序列分为左右子树的中序遍历序列
//3.左右子树的个数,将后序遍历序列分为左右子树的后序遍历序列struct TreeNode* buildTree(int* inorder, int inorderSize, int* postorder, int postorderSize) {if (inorderSize == 0 || postorderSize == 0) return NULL;// 创建根节点,值为后序遍历的最后一个元素struct TreeNode* root = malloc(sizeof(struct TreeNode));root->val = postorder[postorderSize - 1];root->left = NULL;root->right = NULL;// 在中序遍历中找到根节点的位置int index = 0;while (index < inorderSize && inorder[index] != root->val) {index++;}// 递归构建左子树root->left = buildTree(inorder, index, postorder, index);// 递归构建右子树root->right = buildTree(inorder + index + 1, inorderSize - index - 1, postorder + index, postorderSize - index - 1);return root;
}

最大二叉树

654. 最大二叉树

思路:递归。

和上一题思路差不多,将原序列从中间(本题为最大值)分为三部分,分别作为左孩子,根,右孩子,然后对左右孩子进行相同的操作。

/*** Definition for a binary tree node.* struct TreeNode {*     int val;*     struct TreeNode *left;*     struct TreeNode *right;* };*/
struct TreeNode* constructMaximumBinaryTree(int* nums, int numsSize) {if(numsSize==0)return NULL;int maxval=nums[0],maxindex=0;for(int i=0;i<numsSize;i++){//找到最大值,作为根节点if(nums[i]>maxval){maxval=nums[i];maxindex=i;}}struct TreeNode *root=malloc(sizeof(struct TreeNode));root->val=maxval;root->left=constructMaximumBinaryTree(nums,maxindex);root->right=constructMaximumBinaryTree(nums+maxindex+1,numsSize-maxindex-1);return root;
}

合并二叉树

617. 合并二叉树

思路:见代码注释。递归。

/*** Definition for a binary tree node.* struct TreeNode {*     int val;*     struct TreeNode *left;*     struct TreeNode *right;* };*/
struct TreeNode* mergeTrees(struct TreeNode* root1, struct TreeNode* root2) {//1.若都是NULL,返回NULL(可省略)//2.只有一个为NULL,直接返回另一个不为空的树//3.两个都不为空时,左子树与左子树,右子树与右子树合并,根的值相加//1.若都是NULL,返回NULL(可省略)if(root1==NULL&&root2==NULL)return NULL; //2.只有一个为NULL,直接返回另一个不为空的树if(root1==NULL)return root2;if(root2==NULL)return root1;//3.两个都不为空时,左子树与左子树,右子树与右子树合并,根的值相加struct TreeNode* mergeroot=malloc(sizeof(struct TreeNode));mergeroot->val=root1->val+root2->val;mergeroot->left=mergeTrees(root1->left,root2->left);mergeroot->right=mergeTrees(root1->right,root2->right);return mergeroot;
}

二叉搜索树中的搜索

700. 二叉搜索树中的搜索

/*** Definition for a binary tree node.* struct TreeNode {*     int val;*     struct TreeNode *left;*     struct TreeNode *right;* };*///BST中:左<根<右//按照二叉搜索树的规则搜索就好了
struct TreeNode* searchBST(struct TreeNode* root, int val) {if(root==NULL)return NULL;if(root->val==val)return root;else if(root->val<val){return  searchBST(root->right,val);}else return searchBST(root->left,val);
}

验证二叉搜索树

98. 验证二叉搜索树

思路:某年的408真题,当时我还写错了。我当时将问题转化为判断是否每个节点都满足:左<根<右,但其实这样是错误的。

正确做法是利用二叉搜索树的一个特性:中序遍历序列是递增的,我们验证这个就好了。

我是直接把中序遍历序列放入一个数组中,再验证该数组是否严格递增。(这样效率不高,但实现简单)

/*** Definition for a binary tree node.* struct TreeNode {*     int val;*     struct TreeNode *left;*     struct TreeNode *right;* };*/
void inorder(struct TreeNode*root,int *nums,int *len){if(root==NULL)return;inorder(root->left,nums,len);nums[(*len)++]=root->val;inorder(root->right,nums,len);
}
bool isValidBST(struct TreeNode* root) {int *len=malloc(sizeof(int));*len=0;int *nums=malloc(sizeof(int)*10005);inorder(root,nums,len);for(int i=0;i<*len-1;i++){if(nums[i]>=nums[i+1])return false;}return true;
}

还有一种方法是:保存上一次访问的值,当访问某个节点时,可以与上一次访问的值比较,若不大于上一次访问的值,直接访问false;这样在时间与空间效率都更高。

/*** Definition for a binary tree node.* struct TreeNode {*     int val;*     struct TreeNode *left;*     struct TreeNode *right;* };*/
bool inorder(struct TreeNode*root,long long *pre){if(root==NULL)return true;bool res=inorder(root->left,pre);if(root->val<=*pre){return false;}*pre=root->val;return inorder(root->right,pre)&&res;
}
bool isValidBST(struct TreeNode* root) {long long pre=LONG_MIN;return inorder(root,&pre);
}

二叉搜索树的最小绝对差

530. 二叉搜索树的最小绝对差

思路:中序遍历,用min记录结果,用pre记录上一次访问的值,计算本次访问节点的值与pre的差,比较与min之间的大小关系,并更新min

/*** Definition for a binary tree node.* struct TreeNode {*     int val;*     struct TreeNode *left;*     struct TreeNode *right;* };*/
long long Abs(long long a){if(a>0)return a;return 0-a; 
}
void inorder(struct TreeNode* root,long long *pre,long long* min){if(root==NULL)return;inorder(root->left,pre,min);if(Abs(root->val-*pre)<*min)*min=Abs(root->val-*pre);*pre=root->val;inorder(root->right,pre,min);
}
int getMinimumDifference(struct TreeNode* root) {long long *pre=malloc(sizeof(long long));*pre=INT_MIN;long long *min=malloc(sizeof(long long));*min=LONG_MAX;inorder(root,pre,min);return *min;
}

二叉树中的众数

501. 二叉搜索树中的众数

思路一:哈希表,最简单的思路。遍历一遍二叉树,用哈希表统计

每个数出现的次数,遍历哈希表,找到出现次数最大的数

/*** Definition for a binary tree node.* struct TreeNode {*     int val;*     struct TreeNode *left;*     struct TreeNode *right;* };*/
/*** Note: The returned array must be malloced, assume caller calls free().*/
void inorder(struct TreeNode* root,int *hash){if(root==NULL)return;inorder(root->left,hash);hash[root->val+100000]++;inorder(root->right,hash);
}
int* findMode(struct TreeNode* root, int* returnSize) {int hash[200005];memset(hash,0,sizeof(hash));int Maxnum=0;inorder(root,hash);for(int i=0;i<=200000;i++){if(hash[i]>Maxnum)Maxnum=hash[i];}int *ans=malloc(sizeof(int)*10000);*returnSize=0;for(int i=0;i<=200000;i++){if(hash[i]==Maxnum)ans[(*returnSize)++]=i-100000;}return ans;
}

思路二:

  1. 利用中序遍历

    二叉搜索树(BST)的中序遍历结果是一个有序数组,因此可以通过中序遍历统计节点值的频率。

  2. 统计频率

    使用一个变量Curnum记录当前节点值的连续出现次数。

    使用另一个变量Maxnum记录当前的最大频率。

  3. 更新众数

    如果Curnum > Maxnum,说明当前节点值的频率更高,更新Maxnum,并重置众数数组。

    如果Curnum == Maxnum,说明当前节点值也是众数,将其加入众数数组。

  4. 两次遍历

    第一次遍历:确定众数的最大频率(Maxnum)和众数的数量(returnSize)。

    第二次遍历:根据MaxnumreturnSize,将众数存入分配好的数组中。

/*** Definition for a binary tree node.* struct TreeNode {*     int val;*     struct TreeNode *left;*     struct TreeNode *right;* };*/
/*** Note: The returned array must be malloced, assume caller calls free().*/// 中序遍历函数,用于统计节点值的频率并找到众数
void inorder(struct TreeNode* root, struct TreeNode **pre, int *Maxnum, int *Curnum, int* ans, int* returnSize) {if (root == NULL) return; // 如果当前节点为空,直接返回// 递归遍历左子树inorder(root->left, pre, Maxnum, Curnum, ans, returnSize);// 处理当前节点if (*pre && (*pre)->val == root->val) {(*Curnum)++; // 如果当前节点值与前一个节点值相同,增加当前频率} else {*Curnum = 1; // 否则,重置当前频率为1}// 更新最大频率和众数数组if (*Curnum > *Maxnum) {*Maxnum = *Curnum; // 如果当前频率大于最大频率,更新最大频率*returnSize = 1;   // 重置众数数组大小为1} else if (*Curnum == *Maxnum) {if (ans) {ans[*returnSize] = root->val; // 如果当前频率等于最大频率,将当前值存入众数数组}(*returnSize)++; // 增加众数数组大小}*pre = root; // 更新前一个节点为当前节点// 递归遍历右子树inorder(root->right, pre, Maxnum, Curnum, ans, returnSize);
}// 查找众数的主函数
int* findMode(struct TreeNode* root, int* returnSize) {int Maxnum = 0; // 最大频率int Curnum = 0; // 当前频率*returnSize = 0; // 众数数组大小struct TreeNode* pre = NULL; // 前一个节点指针// 第一次中序遍历:确定最大频率和众数数量inorder(root, &pre, &Maxnum, &Curnum, NULL, returnSize);// 分配众数数组内存int* ans = malloc(sizeof(int) * (*returnSize));if (ans == NULL) {*returnSize = 0; // 如果内存分配失败,返回空数组return NULL;}// 重置变量Curnum = 0;*returnSize = 0;pre = NULL;// 第二次中序遍历:填充众数数组inorder(root, &pre, &Maxnum, &Curnum, ans, returnSize);return ans; // 返回众数数组
}

二叉树的最近公共祖先

236. 二叉树的最近公共祖先

思路:

  1. 判断祖先关系
    • 使用 isanc 函数判断一个节点是否是另一个节点的祖先。
    • 如果 pq 的祖先,直接返回 p;如果 qp 的祖先,直接返回 q
  2. 递归查找 LCA
    • 如果 pq 都在左子树中,递归到左子树查找。
    • 如果 pq 都在右子树中,递归到右子树查找。
    • 如果 pq 分别位于左右子树中,则当前节点就是它们的最近公共祖先。
/*** Definition for a binary tree node.* struct TreeNode {*     int val;*     struct TreeNode *left;*     struct TreeNode *right;* };*/// 判断 node 是否是 root 的子孙节点
bool isanc(struct TreeNode* root, struct TreeNode *node) {if (root == NULL) return false; // 如果 root 为空,返回 falseif (root == node) return true;  // 如果 root 就是 node,返回 true// 递归检查左子树和右子树return isanc(root->left, node) || isanc(root->right, node);
}// 查找 p 和 q 的最近公共祖先
struct TreeNode* lowestCommonAncestor(struct TreeNode* root, struct TreeNode* p, struct TreeNode* q) {if (root == NULL) return NULL; // 如果当前节点为空,返回 NULL// 如果 p 是 q 的祖先,直接返回 pif (isanc(p, q)) return p;// 如果 q 是 p 的祖先,直接返回 qif (isanc(q, p)) return q;// 如果 p 和 q 都在左子树中,递归到左子树查找if (isanc(root->left, p) && isanc(root->left, q)) {return lowestCommonAncestor(root->left, p, q);}// 如果 p 和 q 都在右子树中,递归到右子树查找if (isanc(root->right, p) && isanc(root->right, q)) {return lowestCommonAncestor(root->right, p, q);}// 如果 p 和 q 分别位于左右子树中,则当前节点就是 LCAreturn root;
}

优化:

  1. 递归遍历
    • 从根节点开始递归遍历树。
    • 如果当前节点是 pq,直接返回当前节点。
  2. 判断左右子树
    • 递归查找左子树和右子树。
    • 如果 pq 分别在左右子树中,则当前节点就是它们的最近公共祖先。
    • 如果 pq 都在左子树中,返回左子树的结果。
    • 如果 pq 都在右子树中,返回右子树的结果。
  3. 时间复杂度
    • 每个节点最多被访问一次,时间复杂度为 O(n),其中 n 是树的节点数。
  4. 空间复杂度
    • 递归栈的深度为树的高度,空间复杂度为 O(h),其中 h 是树的高度。
/*** Definition for a binary tree node.* struct TreeNode {*     int val;*     struct TreeNode *left;*     struct TreeNode *right;* };*/struct TreeNode* lowestCommonAncestor(struct TreeNode* root, struct TreeNode* p, struct TreeNode* q) {if (root == NULL) return NULL; // 如果当前节点为空,返回 NULL// 如果当前节点是 p 或 q,直接返回当前节点if (root == p || root == q) {return root;}// 递归查找左子树和右子树struct TreeNode* left = lowestCommonAncestor(root->left, p, q);struct TreeNode* right = lowestCommonAncestor(root->right, p, q);// 如果 p 和 q 分别在左右子树中,则当前节点是 LCAif (left && right) {return root;}// 如果 p 和 q 都在左子树中,返回左子树的结果if (left) {return left;}// 如果 p 和 q 都在右子树中,返回右子树的结果if (right) {return right;}// 如果 p 和 q 都不在当前子树中,返回 NULLreturn NULL;
}

二叉搜索树的最近公共祖先

235. 二叉搜索树的最近公共祖先

思路:用上一题的思路也能对。

下面是针对BST的改进

/*** Definition for a binary tree node.* struct TreeNode {*     int val;*     struct TreeNode *left;*     struct TreeNode *right;* };*/struct TreeNode* lowestCommonAncestor(struct TreeNode* root, struct TreeNode* p, struct TreeNode* q) {if (root == NULL) return NULL; // 如果当前节点为空,返回 NULL// 如果当前节点是 p 或 q,直接返回当前节点if (root == p || root == q) {return root;}//当前节点大于p,q的值,继续向左子树找if(root->val>q->val&&root->val>p->val)return lowestCommonAncestor(root->left,p,q);//当前节点小于p,q的值,继续向右子树找else if(root->val<q->val&&root->val<p->val)return lowestCommonAncestor(root->right,p,q);//当前节点位于p,q中间,该节点就是p,q的LCAelse return root;}

二叉搜索树中的插入操作

701. 二叉搜索树中的插入操作

  • 使用指针 p 遍历树,找到合适的插入位置。
  • 如果 val 小于当前节点值,尝试插入到左子树;否则尝试插入到右子树。
  • 如果当前节点的左子树或右子树为空,直接插入新节点。
/*** Definition for a binary tree node.* struct TreeNode {*     int val;*     struct TreeNode *left;*     struct TreeNode *right;* };*/struct TreeNode* insertIntoBST(struct TreeNode* root, int val) {// 创建新节点struct TreeNode *node = malloc(sizeof(struct TreeNode));node->val = val;node->left = NULL;node->right = NULL;// 如果树为空,直接返回新节点if (root == NULL) {return node;}// 使用指针 p 遍历树struct TreeNode* p = root;while (p) {// 如果 val 小于当前节点值,尝试插入到左子树if (val < p->val) {if (p->left == NULL) {p->left = node; // 左子树为空,直接插入break;} else {p = p->left; // 继续遍历左子树}}// 如果 val 大于等于当前节点值,尝试插入到右子树else {if (p->right == NULL) {p->right = node; // 右子树为空,直接插入break;} else {p = p->right; // 继续遍历右子树}}}// 返回根节点return root;
}

删除二叉搜索树中的节点

450. 删除二叉搜索树中的节点

方法一:递归

/*** Definition for a binary tree node.* struct TreeNode {*     int val;*     struct TreeNode *left;*     struct TreeNode *right;* };*/struct TreeNode* deleteNode(struct TreeNode* root, int key) {if (root == NULL) return NULL; // 如果树为空,直接返回 NULL// 如果 key 小于当前节点值,递归到左子树删除if (key < root->val) {root->left = deleteNode(root->left, key);}// 如果 key 大于当前节点值,递归到右子树删除else if (key > root->val) {root->right = deleteNode(root->right, key);}// 如果 key 等于当前节点值,删除当前节点else {// 情况 1:当前节点没有子节点if (root->left == NULL && root->right == NULL) {free(root); // 释放内存return NULL;}// 情况 2:当前节点只有右子树else if (root->left == NULL) {struct TreeNode* temp = root->right; // 保存右子树free(root); // 释放内存return temp; // 返回右子树}// 情况 3:当前节点只有左子树else if (root->right == NULL) {struct TreeNode* temp = root->left; // 保存左子树free(root); // 释放内存return temp; // 返回左子树}// 情况 4:当前节点有左右子树else {// 找到右子树中的最小值节点(最左节点)struct TreeNode* p = root->right;while (p->left) {p = p->left;}// 用最小值节点的值替换当前节点值root->val = p->val;// 递归删除右子树中的最小值节点root->right = deleteNode(root->right, p->val);}}// 返回根节点return root;
}

方法二:迭代

/*** Definition for a binary tree node.* struct TreeNode {*     int val;*     struct TreeNode *left;*     struct TreeNode *right;* };*/struct TreeNode* deleteNode(struct TreeNode* root, int key) {if (root == NULL) return NULL; // 如果树为空,直接返回 NULL// 如果当前节点是需要删除的节点if (root->val == key) {// 情况 1:当前节点是叶子节点(没有子节点)if (root->left == NULL && root->right == NULL) {free(root); // 释放内存return NULL; // 返回 NULL}// 情况 2:当前节点只有左子树else if (root->left != NULL && root->right == NULL) {struct TreeNode* leftChild = root->left; // 保存左子树free(root); // 释放当前节点内存return leftChild; // 返回左子树}// 情况 3:当前节点只有右子树else if (root->left == NULL && root->right != NULL) {struct TreeNode* rightChild = root->right; // 保存右子树free(root); // 释放当前节点内存return rightChild; // 返回右子树}// 情况 4:当前节点有左右子树else {// 找到左子树中的最大值节点(最右节点)struct TreeNode* pre = root; // 记录父节点struct TreeNode* p = root->left; // 从左子树开始while (p->right != NULL) {pre = p;p = p->right;}// 如果最大值节点不是左子树的根节点if (pre != root) {pre->right = p->left; // 将最大值节点的左子树挂到其父节点的右子树} else {pre->left = p->left; // 如果最大值节点是左子树的根节点,直接挂到左子树}// 用最大值节点替换当前节点p->left = root->left;p->right = root->right;free(root); // 释放当前节点内存return p; // 返回替换后的节点}}// 如果 key 小于当前节点值,递归到左子树删除else if (key < root->val) {root->left = deleteNode(root->left, key);}// 如果 key 大于当前节点值,递归到右子树删除else {root->right = deleteNode(root->right, key);}// 返回当前节点return root;
}

修剪二叉搜索树

669. 修剪二叉搜索树

思路:递归修剪

  • 如果当前节点为空,直接返回 NULL
  • 如果当前节点的值小于 low,说明当前节点及其左子树都不在范围内,直接递归修剪右子树。
  • 如果当前节点的值大于 high,说明当前节点及其右子树都不在范围内,直接递归修剪左子树。
  • 如果当前节点的值在 [low, high] 范围内,递归修剪其左右子树,并返回当前节点。
/*** Definition for a binary tree node.* struct TreeNode {*     int val;*     struct TreeNode *left;*     struct TreeNode *right;* };*/struct TreeNode* trimBST(struct TreeNode* root, int low, int high) {// 如果当前节点为空,直接返回 NULLif (root == NULL) return NULL;// 如果当前节点的值小于 low,说明当前节点及其左子树都不在范围内// 直接递归修剪右子树if (root->val < low) {return trimBST(root->right, low, high);}// 如果当前节点的值大于 high,说明当前节点及其右子树都不在范围内// 直接递归修剪左子树if (root->val > high) {return trimBST(root->left, low, high);}// 如果当前节点的值在 [low, high] 范围内// 递归修剪左子树和右子树root->left = trimBST(root->left, low, high);root->right = trimBST(root->right, low, high);// 返回当前节点return root;
}

将有序数组转换为二叉搜索树

108. 将有序数组转换为二叉搜索树

思路:递归

/*** Definition for a binary tree node.* struct TreeNode {*     int val;*     struct TreeNode *left;*     struct TreeNode *right;* };*/
struct TreeNode* sortedArrayToBST(int* nums, int numsSize) {if (numsSize == 0) return NULL;// 创建根节点,值为数组的中间元素struct TreeNode* root = malloc(sizeof(struct TreeNode));int mid = numsSize / 2;root->val = nums[mid];// 递归构建左子树和右子树root->left = sortedArrayToBST(nums, mid); // 左子树使用数组的左半部分root->right = sortedArrayToBST(nums + mid + 1, numsSize - mid - 1); // 右子树使用数组的右半部分return root;
}

把二叉搜索树转换为累加树

538. 把二叉搜索树转换为累加树

思路:累加树的新节点的值等于原树中值大于等于原值的值之和,可以利用二叉搜索树中序遍历有序这个性质,就可以得到所有大于等于该值的所有节点的值。

一般的中序遍历是左-根-右遍历,得到的是升序序列,我们这里可以用右-根-左的顺序遍历,得到的是降序遍历的序列,用sum记录累加和,就可以在遍历时对节点进行修改。

/*** Definition for a binary tree node.* struct TreeNode {*     int val;*     struct TreeNode *left;*     struct TreeNode *right;* };*/// 按照右、根、左的顺序遍历二叉树
void inorder(struct TreeNode* root, int *sum) {// 如果当前节点为空,直接返回if (root == NULL) return;// 1. 先遍历右子树(较大的值)inorder(root->right, sum);// 2. 处理当前节点*sum += root->val;  // 将当前节点的值累加到 sum 中root->val = *sum;   // 更新当前节点的值为累加后的 sum// 3. 最后遍历左子树(较小的值)inorder(root->left, sum);
}// 将二叉搜索树转换为累加树
struct TreeNode* convertBST(struct TreeNode* root) {int sum = 0;  // 初始化累加和为 0inorder(root, &sum);  // 调用辅助函数进行遍历和累加return root;  // 返回转换后的树
}

至此,二叉树部分告一段落!

相关文章:

代码随想录——二叉树(二)

文章目录 前言二叉树最大深度二叉树的最小深度翻转二叉树对称二叉树完全二叉树的节点个数平衡二叉树二叉树的所有路径左叶子之和找左下角的值路径总和从中序与后序序列构造二叉树最大二叉树合并二叉树二叉搜索树中的搜索验证二叉搜索树二叉搜索树的最小绝对差二叉树中的众数二叉…...

一个基于Python+Appium的手机自动化项目~~

本项目通过PythonAppium实现了抖音手机店铺的自动化询价&#xff0c;可以直接输出excel&#xff0c;并带有详细的LOG输出。 1.excel输出效果: 2. LOG效果: 具体文件内容见GitCode&#xff1a; 项目首页 - douyingoods:一个基于Pythonappium的手机自动化项目&#xff0c;实现了…...

深入剖析SpringBoot启动机制:run()方法详尽解读

摘要 本文深入解析SpringBoot的启动机制&#xff0c;以run()方法为核心&#xff0c;逐步追踪并详细解释其关键步骤。首先探讨run()方法的工作原理&#xff0c;然后深入代码层面分析各个关键环节。文章提供刷新后钩子和启动后任务的代码示例&#xff0c;帮助读者理解SpringBoot源…...

deepseek v1手机端部署

在iPhone上部署DeepSeekR1 1. 安装快捷指令&#xff1a; 打开iPhone上的Safari浏览器&#xff0c;访问[这个链接](https://www.icloud.com/shortcuts/e0bc5445c39d45a78b90e1dc896cd010)下载快捷指令。 下载后&#xff0c;按照提示完成安装。 2. 获取并配置API Key&a…...

idea对jar包内容进行反编译

1.先安装一下这个插件java Bytecode Decompiler 2.找到这个插件的路径&#xff0c;在idea的plugins下面的lib文件夹内&#xff1a;java-decompiler.jar。下面是我自己本地的插件路径&#xff0c;以作参考&#xff1a; D:\dev\utils\idea\IntelliJ IDEA 2020.1.3\plugins\java-d…...

KMP算法原理 JAVA实现

KMP算法原理 JAVA实现 一、什么是KMP算法二、为什么需要KMP算法1. 算法背景1.1 暴力匹配过程1.2 暴力匹配的优劣 2. KMP算法的诞生3. next数组3.1 kmp算法的关键 三、求解KMP 一、什么是KMP算法 实际上KMP只是发明这个算法的三个人的英文名首字母短称&#xff0c;KMP本身无意义…...

利用Redis实现数据缓存

目录 1 为啥要缓存捏&#xff1f; 2 基本流程&#xff08;以查询商铺信息为例&#xff09; 3 实现数据库与缓存双写一致 3.1 内存淘汰 3.2 超时剔除&#xff08;半自动&#xff09; 3.3 主动更新&#xff08;手动&#xff09; 3.3.1 双写方案 3.3.2 读写穿透方案 3.3.…...

基于 RAMS 的数据驱动建模与应用实践:从理论到具体操作

基于 RAMS 的数据驱动建模与应用实践&#xff1a;从理论到具体操作 RAMS&#xff08;区域大气建模系统&#xff09;因其模块化设计、高分辨率模拟能力和广泛的应用领域&#xff0c;成为区域大气建模的强大工具。而数据驱动建模技术的崛起&#xff0c;使得 RAMS 的能力得到进一…...

计算机图形学实验练习(实验1.2-4.1AND补充实验12)

实验1.2 OpenGL与着色器编程 1.理论知识 1.1 OpenGL的含义 OpenGL是一种应用程序编程接口(Application Programming Interface,API),它是一种可以对图形硬件设备特性进行访问的软件库。OpenGL最新的4.3版本包含了超过500个不同的命令,可以用于设置所需的对象、图像和操…...

javascript-es6 (一)

作用域&#xff08;scope&#xff09; 规定了变量能够被访问的“范围”&#xff0c;离开了这个“范围”变量便不能被访问 局部作用域 函数作用域&#xff1a; 在函数内部声明的变量只能在函数内部被访问&#xff0c;外部无法直接访问 function getSum(){ //函数内部是函数作用…...

uni-app 程序打包 Android apk、安卓夜神模拟器调试运行

1、打包思路 云端打包方案&#xff08;每天免费次数限制5&#xff0c;最简单&#xff0c;可以先打包尝试一下你的程序打包后是否能用&#xff09;&#xff1a; HBuilderX 发行App-Android云打包 选择Android、使用云端证书、快速安心打包本地打包&#xff1a; HBuilderX …...

yolov11 解读简记

1 文章详细介绍了YOLOv11的架构设计&#xff0c;包括以下几个关键组件&#xff1a; C3k2块&#xff1a;这是YOLOv11引入的一种新型卷积块&#xff0c;替代了之前版本中的C2f块。C3k2块通过使用两个较小的卷积核代替一个大的卷积核&#xff0c;提高了计算效率&#xff0c;同时保…...

CommonAPI学习笔记-1

CommonAPI学习笔记-1 一. 整体结构 CommonAPI分为两层&#xff1a;核心层和绑定层&#xff0c;使用了Franca来描述服务接口的定义和部署&#xff0c;而Franca是一个用于定义和转换接口的框架&#xff08;https://franca.github.io/franca/&#xff09;。 ​ 核心层和通信中间…...

从入门到精通:RabbitMQ的深度探索与实战应用

目录 一、RabbitMQ 初相识 二、基础概念速览 &#xff08;一&#xff09;消息队列是什么 &#xff08;二&#xff09;RabbitMQ 核心组件 三、RabbitMQ 基本使用 &#xff08;一&#xff09;安装与环境搭建 &#xff08;二&#xff09;简单示例 &#xff08;三&#xff09;…...

深入理解若依RuoYi-Vue数据字典设计与实现

深入理解若依数据字典设计与实现 一、Vue2版本主要文件目录 组件目录src/components&#xff1a;数据字典组件、字典标签组件 工具目录src/utils&#xff1a;字典工具类 store目录src/store&#xff1a;字典数据 main.js&#xff1a;字典数据初始化 页面使用字典例子&#xf…...

Cursor 帮你写一个小程序

Cursor注册地址 首先下载客户端 点击链接下载 1 打开微信开发者工具创建一个小程序项目 选择TS-基础模版 官方 2 然后使用Cursor打开小程序创建的项目 3 在CHAT聊天框输入自己的需求 比如 小程序功能描述&#xff1a;吃什么助手 项目名称&#xff1a; 吃什么小程序 功能目标…...

进程控制的学习

目录 1.进程创建 1.1 fork函数 1.2 fork函数返回值 1.3 写时拷贝 1.4 fork 常规用法 1.5 fork 调用失败的原因 2. 进程终止 2.1 进程退出场景 2.2 进程常见退出方法 2.2.1 从main 返回 2.2.2 echo $&#xff1f; 查看进程退出码 2.2.2.1 我们如何得到退出码代表的含…...

一文讲解Java中的接口和抽象类

抽象类和接口有什么区别&#xff1f; 一个类只能继承一个抽象类&#xff1b;但一个类可以实现多个接口。所以我们在新建线程类的时候&#xff0c;一般推荐使用Runnable接口的方式&#xff0c;这样线程类还可以继承其他类&#xff0c;而不单单是Thread类&#xff1b;抽象类符合…...

Vue 3 30天精进之旅:Day 05 - 事件处理

引言 在前几天的学习中&#xff0c;我们探讨了Vue实例、计算属性和侦听器。这些概念为我们搭建了Vue应用的基础。今天&#xff0c;我们将专注于事件处理&#xff0c;这是交互式Web应用的核心部分。通过学习如何在Vue中处理事件&#xff0c;你将能够更好地与用户进行交互&#…...

STM32完全学习——RT-thread在STM32F407上移植

一、写在前面 关于源码的下载&#xff0c;以及在KEIL工程里面添加操作系统的源代码&#xff0c;这里就不再赘述了。需要注意的是RT-thread默认里面是会使用串口的&#xff0c;因此需要额外的进行串口的初始化&#xff0c;有些人可能会问&#xff0c;为什么不直接使用CubMAX直接…...

Shodan Dorks安装指南,通过Shodan搜索漏洞

Shodan Dorks是一种基于Shodan的工具&#xff0c;不知道Shodan是什么的不必阅读下面的内容。简单的说就是&#xff0c;利用预定义的查询&#xff08;dorks&#xff09;&#xff0c;通过Shodan轻松搜索漏洞和机密信息。 推荐渗透测试人员自行测试。 安装方法&#xff1a; 1.确…...

poi在word中打开本地文件

poi版本 5.2.0 方法1&#xff1a;使用XWPFFieldRun&#xff08;推荐&#xff09; 比如打开当前相对路径的aaaaa.docx XWPFFieldRun run paragraph.createFieldRun();CTRPr ctrPr run.getCTR().addNewRPr();CTFonts font ctrPr.addNewRFonts();// 设置字体font.setAscii(&quo…...

Linux查看服务器的内外网地址

目录&#xff1a; 1、内网地址2、外网地址3、ping时显示地址与真实不一致 1、内网地址 ifconfig2、外网地址 curl ifconfig.me3、ping时显示地址与真实不一致 原因是dns缓存导致的&#xff0c;ping这种方法也是不准确的&#xff0c;有弊端不建议使用&#xff0c;只适用于测试…...

OAuth1和OAuth2授权协议

OAuth 1 授权协议 1. 概述 OAuth1 是 OAuth 标准的第一个正式版本&#xff0c;它通过 签名和令牌 的方式&#xff0c;实现用户授权第三方访问其资源的功能。在 OAuth1 中&#xff0c;安全性依赖于签名机制&#xff0c;无需传递用户密码。 2. 核心特性 使用 签名&#xff08…...

DeepSeek学术题目选择效果怎么样?

论文选题 一篇出色的论文背后&#xff0c;必定有一个“智慧的选题”在撑腰。选题足够好文章就能顺利登上高水平期刊&#xff1b;选题不行再精彩的写作也只能“当花瓶”。然而许多宝子们常常忽视这个环节&#xff0c;把大量时间花在写作上&#xff0c;选题时却像抓阄一样随便挑一…...

数据结构(一)顺序表和链表

目录 1. 时间复杂度和空间复杂度 2. 顺序表 3. 链表 1. 时间复杂度和空间复杂度 如何估算一个算法的效率高低一般就是使用到时间复杂度和空间复杂度; 时间复杂度是评价一个算法运行快慢的, 而空间复杂度是算法额外需要空间大小. 1.1 时间复杂度的计算: 准确来说时间复杂度是…...

单相可控整流电路——单相桥式全控整流电路

以下是关于单相桥式整流电路的介绍&#xff1a; 电路构成&#xff08;带阻性负载的工作情况&#xff09; - 二极管&#xff1a;是电路0的核心元件&#xff0c;通常采用四个同型号或根据需求选择不同型号的二极管&#xff0c;如1N4001、1N4007等&#xff0c;如图Vt1和Vt4是一对…...

DeepSeek-R1:性能对标 OpenAI,开源助力 AI 生态发展

DeepSeek-R1&#xff1a;性能对标 OpenAI&#xff0c;开源助力 AI 生态发展 在人工智能领域&#xff0c;大模型的竞争一直备受关注。最近&#xff0c;DeepSeek 团队发布了 DeepSeek-R1 模型&#xff0c;并开源了模型权重&#xff0c;这一举动无疑为 AI 领域带来了新的活力。今…...

【Maui】提示消息的扩展

文章目录 前言一、问题描述二、解决方案三、软件开发&#xff08;源码&#xff09;3.1 消息扩展库3.2 消息提示框使用3.3 错误消息提示使用3.4 问题选择框使用 四、项目展示 前言 .NET 多平台应用 UI (.NET MAUI) 是一个跨平台框架&#xff0c;用于使用 C# 和 XAML 创建本机移…...

001 mybatis入门

文章目录 mybatis是什么ORM是什么ORM框架和MyBatis的区别#{}和${}的区别编码流程UserDaoImpl.javaUserDao.javaUser.javadb.propertiesSqlMapConfig.xmlUserMapper.xmlMybatisTest.javapom.xmluser.sql 表现层 SpringMVC 业务层 Spring 持久层 Mybatis https://mybatis.org/myb…...

tomcat的accept-count、max-connections、max-threads三个参数的含义

tomcat的accept-count、max-connections、max-threads三个参数的含义 tomcat的accept-count、max-connections、max-threads三个参数的含义 max-connections&#xff1a;最大连接数 最大连接数是指&#xff0c;同一时刻&#xff0c;能够连接的最大请求数 需要注意的是&#x…...

8.2 从看图识字到智能解读:GPT-4 with Vision 开启多模态 AI 新纪元

从看图识字到智能解读:GPT-4 with Vision 开启多模态 AI 新纪元 引言:AI 的多模态跃迁 随着人工智能技术的快速发展,我们正迈入一个新的智能交互时代。传统的 AI 模型主要聚焦于文本处理,而多模态 AI 模型如 GPT-4 with Vision(GPT-4V) 则能够同时处理图像和文本。GPT-4…...

.strip()用法

.strip("") 是 Python 字符串方法 strip() 的一个用法&#xff0c;它会去除字符串两端指定字符集中的字符。 基本语法&#xff1a; string.strip([chars])string: 这是你要操作的字符串。chars: 可选参数&#xff0c;表示你想要去除的字符集&#xff08;默认为空格…...

蓝桥杯例题三

无论前方困难如何重重&#xff0c;我们都要坚定信念&#xff0c;勇往直前。面对挑战和困境&#xff0c;不要退缩&#xff0c;不要放弃&#xff0c;要坚持走下去。当我们感到疲惫时&#xff0c;要告诉自己&#xff1a;“我可以&#xff0c;我一定行&#xff01;”相信自己的实力…...

关于pygame窗口输入法状态异常切换现象的分析报告

一、问题描述 1.1 需求说明 我们准备使用Pygame开发一个键盘输入测试程序&#xff0c;需要确保输入时窗口始终处于英文输入模式&#xff0c;也就是禁止中文输入&#xff1b; 1.2 现象描述 控制台种显示&#xff0c;程序在初始化时&#xff0c;会有两次IMM状态切换操作&…...

【JavaEE进阶】应用分层

目录 &#x1f38b;序言 &#x1f343;什么是应用分层 &#x1f38d;为什么需要应用分层 &#x1f340;如何分层(三层架构) &#x1f384;MVC和三层架构的区别和联系 &#x1f333;什么是高内聚低耦合 &#x1f38b;序言 通过上⾯的练习,我们学习了SpringMVC简单功能的开…...

两数相加:链表操作的基础与扩展

两数相加&#xff1a;链表操作的基础与扩展 引言 链表&#xff08;Linked List&#xff09;是一种灵活且高效的数据结构&#xff0c;特别适用于动态增删操作。无论是初学者还是资深程序员&#xff0c;链表的基本操作都是算法学习中的重要一环。而 “两数相加” 问题则是链表操…...

ChatGPT从数据分析到内容写作建议相关的46个提示词分享!

在当今快节奏的学术环境中&#xff0c;研究人员面临着海量的信息和复杂的研究任务。幸运的是&#xff0c;随着人工智能技术的发展&#xff0c;像ChatGPT这样的先进工具为科研人员提供了强大的支持。今天就让我们一起探索如何利用ChatGPT提升研究效率进一步优化研究流程。 ChatG…...

解析“in the wild”——编程和生活中的俚语妙用

解析“in the wild”——编程和生活中的俚语妙用 看下面的技术文章中遇到 in the wild这个词&#xff0c;想要研究一下&#xff0c;遂产生此文。 Are there ever pointers to pointers to pointers? There is an old programming joke which says you can rate C programmers…...

rocketmq原理源码分析之控制器模式- dledger

简介 RocketMQ 4.5 版本之前&#xff0c;RocketMQ 的broker是 Master/Slave部署架构&#xff0c;一组 broker 有一个 Master &#xff0c;有0到若干Slave&#xff0c;Slave复制Master消息存储&#xff0c;随时替代下线的Master。Master/Slave部署架构提供一定的高可用性&#x…...

Hello Moto

“Hello Moto” 是摩托罗拉&#xff08;Motorola&#xff09;的一句经典广告口号&#xff0c;用于推广其品牌和产品&#xff0c;特别是在手机领域。以下是它的含义和背景&#xff1a; 1. 品牌宣传的标志性语句 直白含义&#xff1a;简单地向摩托罗拉打招呼&#xff08;“Hell…...

存储基础 -- SCSI命令格式与使用场景

SCSI命令格式与使用场景 1. SCSI命令描述符块&#xff08;CDB&#xff09; 1.1 CDB基本概念 SCSI命令通过**命令描述符块&#xff08;CDB, Command Descriptor Block&#xff09;**表示。 CDB长度&#xff1a;SCSI命令根据使用场景有不同长度的CDB&#xff0c;常见的有6字节…...

ceph基本概念,架构,部署(一)

一、分布式存储概述 1.存储分类 存储分为封闭系统的存储和开放系统的存储&#xff0c;而对于开放系统的存储又被分为内置存储和外挂存储。 外挂存储又被细分为直连式存储(DAS)和网络存储(FAS)&#xff0c;而网络存储又被细分网络接入存储(NAS)和存储区域网络(SAN)等。 DAS(D…...

CNN-GRU卷积门控循环单元时间序列预测(Matlab完整源码和数据)

CNN-GRU卷积门控循环单元时间序列预测&#xff08;Matlab完整源码和数据&#xff09; 目录 CNN-GRU卷积门控循环单元时间序列预测&#xff08;Matlab完整源码和数据&#xff09;预测效果基本介绍CNN-GRU卷积门控循环单元时间序列预测一、引言1.1、研究背景与意义1.2、研究现状1…...

Ubuntu 顶部状态栏 配置,gnu扩展程序

顶部状态栏 默认没有配置、隐藏的地方 安装使用Hide Top Bar 或Just Perfection等进行配置 1 安装 sudo apt install gnome-shell-extension-manager2 打开 安装的“扩展管理器” 3. 对顶部状态栏进行配置 使用Hide Top Bar 智能隐藏&#xff0c;或者使用Just Perfection 直…...

React应用深度优化与调试实战指南

一、渲染性能优化进阶 1.1 精细化渲染控制 typescript 复制 // components/HeavyComponent.tsx import React, { memo, useMemo } from react;interface Item {id: string;complexData: {// 复杂嵌套结构}; }const HeavyComponent memo(({ items }: { items: Item[] }) &g…...

Spring中的事件和事件监听器是如何工作的?

目录 一、事件&#xff08;Event&#xff09; 二、事件发布器&#xff08;Event Publisher&#xff09; 三、事件监听器&#xff08;Event Listener&#xff09; 四、使用场景 五、总结 以下是关于Spring中的事件和事件监听器的介绍与使用说明&#xff0c;结合了使用场景&…...

Vue.js组件开发-实现多个文件附件压缩下载

在 Vue 项目中实现多个附件压缩下载&#xff0c;可以借助 jszip 库来创建压缩文件&#xff0c;以及 file-saver 库来保存生成的压缩文件。 步骤 1&#xff1a;安装依赖 首先&#xff0c;在 Vue 项目中安装 jszip 和 file-saver&#xff1a; npm install jszip file-saver步骤…...

基于dlib/face recognition人脸识别推拉流实现

目录 一.环境搭建 二.推拉流代码 三.人脸检测推拉流 一.环境搭建 1.下载RTSP服务器MediaMTX与FFmpeg FFmpeg是一款功能强大的开源多媒体处理工具,而MediaMTX则是一个轻量级的流媒体服务器。两者结合,可以实现将本地视频或者实时摄像头画面推送到RTSP流,从而实现视频…...

qt QNetworkRequest详解

1、概述 QNetworkRequest是Qt网络模块中的一个核心类&#xff0c;专门用于处理网络请求。它封装了网络请求的所有关键信息&#xff0c;包括请求的URL、HTTP头部信息等&#xff0c;使得开发者能够方便地在Qt应用程序中执行网络操作&#xff0c;如文件下载、网页内容获取等。QNe…...