速通数据结构与算法第六站 树堆

系列文章目录

速通数据结构与算法系列

1   速通数据结构与算法第一站 复杂度          http://t.csdnimg.cn/sxEGF

2   速通数据结构与算法第二站 顺序表          http://t.csdnimg.cn/WVyDb

3   速通数据结构与算法第三站 单链表          http://t.csdnimg.cn/cDpcC

4   速通数据结构与算法第四站 双链表          http://t.csdnimg.cn/0VyDl

5   速通数据结构与算法第五站 栈&&队列     http://t.csdnimg.cn/MRU83

感谢佬们支持!


目录

系列文章目录

  • 前言
  • 一、树
  •      1 树的概念
  •      2 树的相关概念
  •      3 树的表示方式
  • 二、二叉树
  •      1 概念
  •      2 特殊的二叉树
  •      3 二叉树的性质
  •      4 二叉树的存储结构
  • 三、堆
  •      1 堆的概念结构
  •      2 堆的实现
  •      3 堆排序
  •      4 堆的应用--Topk问题
  • 四、链式二叉树
  •      1 结构体
  •      2 三种遍历方式
  •      3 计算节点个数
  •      4 计算深度
  •      5 第k层的节点个数
  •      6 相同的树
  •      7 单值二叉树
  •      8 二叉树查找值为x的节点
  •      9 对称二叉树
  •     10 层序遍历
  •     11判断一棵树是否为完全二叉树
  •     12 另一棵树的子树
  • 补充概念:DFS、BFS
  • 总结

前言

上篇博客我们学了简单的栈和队列,这次我们来学学不太容易的树和堆,大家要做好狠狠递归的准备哦~。树的学习实际上前期还不那么重要,重要的是后期的AVL和红黑树,但是有很多考验分治

和递归的关于树的OJ题。


一、树

1 树的概念

 有关于树的定义,树是一种非线性的结构,它是由n(n>=0)个有限节点组成的一个具有层次关系的集合,它很像一颗倒挂的树,即跟在上而叶朝下的树

其中,最上面的节点称为根节点

除根结点外,其余节点被分成M(M>0)个互不相等的集合T1、T2……Tm,其中每个集合Ti(1<=i<M)又是一颗结构与数相同的子树。

所以说,树是递归定义的

(如下图所示)

注意:子树之间不能相交,否则就不能叫树,而是叫图(关于图 我们将在速通数据结构高阶中学习~)


2 树的相关概念

树的相关概念有一大堆,我们来看一下~(以上图为例)

节点的度:一个节点包含子树的个数称为该节点的度;如:A的度为6

叶子节点/终端节点:度为0的节点;如上图的B、C、H、I等

非终端节点/分支节点:度不为0的节点;如上图的D、E等

双亲节点/父节点:若一个节点含有子节点,则这个节点称为其子节点的父节点;

如上图A为B的父节点

孩子节点/子节点:一个节点含有子树的根节点称为该节点的子节点;如上图,B是A的孩子节

兄弟节点:具有相同父节点的节点互称为兄弟节点;如上图:B,C是兄弟节点

树的度:一棵树中,最大的节点的度称为树的度;如上图,树的度为6

节点的层次:从跟开始定义起,根所在的为第一层,根的子节点为第二层,以此类推

树的高度/深度:树中节点的最大层次;如上图,树的高度为4

堂兄弟节点:双亲在同一层的节点互称为堂兄弟;如上图,H,I互为堂兄弟节点

节点的祖先:从根到该根节点所经分支上的节点;如上图,A是所有节点的祖先

子孙:以某节点为根的子树中任一节点都为该节点的子孙;如上图,所有节点是A的子孙

森林:由m(m>0)棵互不相交的子树的集合称为森林(也就是之后我们要学的并查集)


   3 树的表示方式

如何表示树也是一个值得思考的问题

1 假设我们已知树的度为5,我们可以这样表示

struct TreeNode
{int data;struct TreeNode* sub[5];
};

2 如果我们不知道树的度,那只能给一个顺序表而非静态的结构体数组

struct TreeNode
{int data;//struct TreeNode* sub[5];Seqlist q;
};

显然,这样开销太大了,由此我们有了下面的方法

3 用一个结构数组存储,每个结构存数据及父母的下标

struct TreeNode
{int data;int parent;
};

就像下图这样

但是这样也差点意思,我们引出最后一种牛逼的方法

4 左孩子右兄弟法

两个指针left、right;left只指向左孩子,而right只指向右边第一个兄弟

typedef struct BinaryTreeNode
{BTDataType data;struct BinaryTreeNode* left;struct BinaryTreeNode* right;
}BTNode;

二、二叉树

1 概念

二叉树就是度小于等于二的树。一棵二叉树有三部分组成:根,左子树,右子树

如图所示

当然,这其中也有一些特殊情况。有的树是空树,有的树只有根节点,有的树只有左子树,有的树只有右子树;这都属于二叉树


2 特殊的二叉树

满二叉树:一个二叉树,如果每一层节点数都达到最大值,那么就是满二叉树。也就是说

suppose一个二叉树有k层,如果他节点个数为2^k -1,那么就为满二叉树

完全二叉树:完全二叉树的前k-1层是满的,而最后一层要求从左到右连续

完全二叉树是效率很高的数据结构,后面的AVL,RBTree每次增删操作都是

在尽可能向完全二叉树靠拢;显然,满二叉树为一种特殊的完全二叉树

 放一波图~


3 二叉树的性质

二叉树有一些乱七八糟的性质,而这些性质最喜欢在选择题里考,大家可以边画图边做理解

1 若规定根节点层数为1,则一颗非空二叉树的第i层上最多也2^(i-1)个节点

2 若规定根节点层数为1,则深度为h的二叉树最大节点数为2^h-1

3 对任意一颗二叉树,如果度为0的叶子节点个数为n0,度为2的节点个数为n2,则有

n0=n2+1

4 若规定根节点层数为1,具有n个节点的满二叉树深度为h=log2(n+2)

5 对于具有n个节点的完全二叉树,如果按照从上至下从左至右的顺序对所有节点

从0开始编号,,则对于序号为i的节点有

  1 若i>0 i位置的双亲序号:(i-1)/2    if(i==0) i为根节点,无双亲节点

  2 若2i+1<n,左孩子序号:2i+1,2i+1>=n则无左孩子

  3 若2i+2<n,右孩子序号:2i+2,2i+2>=n则无右孩子


4 二叉树的存储结构

二叉树的存储也分顺序存储和链式存储两种

1 顺序存储

顺序存储就是数组,显然,这种存储方式一般只适合表示完全二叉树,因为别的二叉树中间可能会有空节点,造成空间的浪费。而下一个标题我们要学的堆就是顺序存储

(如图所示)

2 链式存储

二叉树的链式存储是指用类似链表的方法来表示一棵二叉树。

常见的方法是3个成员的二叉链表和4个成员的三叉链表

二叉链表只存值和左右孩子,三叉链表在此基础上存了父节点

(双叉链)

(三叉链)


三、堆

堆的概念结构

二叉树的顺序存储的一个应用就是堆

如果对K={k0,k1,k2……kn-1},把他所有元素按照完全二叉树的顺序存储方式存在一个一维顺序表中,并满足Ki<=K2*i+1且Ki<=K2*i+2,则称为大堆,反之,称为小堆。

将根节点最大的堆称为最大堆(大根堆),根节点最小的堆称为最小堆(小根堆)

对应于STL为priority_queue,只需控制比较的仿函数即可适配出最大、最小堆

堆的性质

1 堆中的某个节点的值总是不大于/不小于其父节点的值

2 堆是一颗完全二叉树

堆的应用

1 堆可以排序

2 堆可以解决Topk问题

以大根堆为例,我们来看看它的逻辑结构和物理结构

如果我们要插入一个20,那就会是这样的

(显然是符合要求的,我们只需和父节点30比较即可得出)

再插一个60呢?

显然,这会影响部分祖先,所以我们要向上调整。

简单来说就是将60向上交换,直到它小于父节点为止。

显然可知,我们最多只需swap高度次(lgN),即最多交换至根即可,我们称这个过程为向上调整

多的不说,我们迅速写一波代码


堆的实现

先搞3个文件,heap.h,heap.c,test.c

简单看一下我们要介绍的接口:heap.h

#pragma once#include<stdio.h>
#include<stdlib.h>
#include<assert.h>
#include<stdbool.h>typedef int HPDataType;//大堆
typedef struct Heap
{HPDataType* a;int size;int capacity;
}HP;//初始化
void HeapInit(HP*hp);//销毁
void HeapDestroy(HP* hp);//push
void HeapPush(HP* hp, HPDataType x);//pop
void HeapPop(HP* hp);//empty
bool HeapEmpty(HP* hp);//size
int HeapSize(HP* hp);//堆顶
HPDataType HeapTop(HP* hp);//向上调整
void AdjustUp(HPDataType* a,int child);//向下调整
void AdjustDown(HPDataType* a,int n,int parent);

我们先简单写一下初始化和销毁,由于是适配器,所以其实就和写顺序表差不多的

// 初始化
void HeapInit(HP* hp)
{assert(hp);hp->a = (HPDataType*)malloc(sizeof(HPDataType) * 4);if (hp->a == NULL){perror("malloc fail");exit(-1);}hp->size = 0;hp->capacity = 4;
}//销毁
void HeapDestroy(HP* hp)
{assert(hp);free(hp->a);hp->a = NULL;hp->capacity = hp->size = 0;
}

由于我们要进行很多交换操作,所以单独封装一个swap出来

//swap
void swap(HPDataType* px, HPDataType* py)
{HPDataType tmp = *px;*px = *py;*py = tmp;
}

 重头戏在插入上,在上面的例子中我们知道,插入之后可能是要进行向上调整操作的

所以在push的逻辑中我们最后要调用向上调整

void HeapPush(HP* hp, HPDataType x)
{assert(hp);if (hp->size == hp->capacity){//扩容HPDataType tmp = (HPDataType*)realloc(hp->a, sizeof(HPDataType)*hp->capacity*2);if (tmp == NULL){perror("reolloc");exit(-2);}hp->capacity *= 2;hp->a = tmp;}hp->a[hp->size++] = x;//向上调整AdjustUp(hp->a, hp->size - 1);
}

下来就看如何实现向上调整了

其实逻辑还是很简单的,我们只需每次算出父节点的位置,和当前值进行比较,如果当前值更大,就进行swap,直到调整到根为止

代码如下~

void AdjustUp(HPDataType* a, int child)
{int parent = (child - 1) / 2;while (child > 0)//最多调到根{if (a[child] > a[parent]){swap(&a[child], &a[parent]);child = parent;parent = (child - 1) / 2;}else{break;}}
}

堆排序

例:

{2,1,5,7,6,8,0,4,3,9}

堆排序,我们首先要有一个堆,所以可以对数组原地建堆

假设我们要排升序,那是建大堆/小堆?

如果按照依次取最小的数来排序的话,应该来建小堆。

这个时候问题来了,第一个数取到了,剩下的一堆数就不满足堆的性质了


所以我们应该建大堆

每次拿最大的数,拿的时候和最后一个数swap,这样最后一个数就会到第一个。

此时剩下的数仍然是一个最大堆

再让堆顶其向下调整即可,最后一个数的位置就好了


还有一个要考虑的事情就是如何建堆

最简单的就是从第二个数开始向上调整,一直调整到最后一个数,这称为向下调整建堆

还有一种是向下调整法

由于叶子节点没有孩子,显然不用向下调整

所以向下调整应该从最后一个非叶子节点开始,一直调到根

显然,向下调整建堆(O(N))的时间复杂度优于向上调整(O(N^2lgN))

毕竟一颗二叉树最后一层的节点数可能占总结点的一半呢


代码如下~

//swap
void swap(int* px, int* py)
{int tmp = *px;*px = *py;*py = tmp;
}//排升序--建大堆(向上调整建堆)
void HeapSort1(int* a, int n)
{for (int i = 1; i < n; ++i)//O(lgN){AdjustUp(a, i);}int end = n - 1;while(end>0){swap(&a[0], &a[end]);AdjustDown(a, end, 0);end--;}
}//排升序--建大堆(向下调整建堆)
void HeapSort2(int* a, int n)
{for (int i = (n - 1 - 1) / 2; i >= 0; --i){AdjustDown(a, n, i);}int end = n - 1;while (end > 0){swap(&a[0], &a[end]);AdjustDown(a, end, 0);end--;}
}

堆的应用--Topk问题

Topk问题:即求数据集合中前k个最大/最小的元素,一般数据总量较大

比如:专业前10,世界500强,富豪榜等等

对于Topk问题,最简单的方式当然是排序,但是数据量大的话,无法一次全加载到内存中

,(当然归并排序是可以外排序的)用数据库又太麻烦,最简单的就是用堆排序

思路如下:

1 用数据集合的前k个建堆

        前k个最大元素,则建小堆

        前k个最小元素,则建大堆

2 用剩下的N-k个元素一次比较,如果大于/小于堆顶就入堆

这里列了一道题,大家可以试一试

(但其实涉及到了双关键字排序,在学C++可能不是特别好操作)

. - 力扣(LeetCode)

class Solution 
{
public:struct Greater{bool operator()(const pair<string,int>&p1,const pair<string,int>&p2){return p1.second>p2.second;}};vector<string> topKFrequent(vector<string>& words, int k) {map<string,int> m;for(auto &e:words){m[e]++;}vector<pair<string,int>> res(m.begin(),m.end());vector<string> ret;stable_sort(res.begin(),res.end(),Greater());for(int i=0;i<k;++i){ret.push_back(res[i].first);}return ret;}
};

四、链式二叉树

显然,如果随意来一个二叉树,就不适合用数组存储了

1 结构体
struct TreeNode {int val;struct TreeNode* left;struct TreeNode* right;};

2 三种遍历方式

三种遍历方式对应为前序、中序、后序

分别对应"根左右","左根右","左右根"的顺序

(值得注意的是,在多叉树中只有前序、中序两种遍历方式)

我们给到一颗二叉树

从根节点分为根,左子树,右子树

我们对上面的左子树,右子树也可以做同样的操作,直到一棵树的左右子树均为空,表示这棵树已经到叶子了。

针对递归类问题,我们要有这样的解题思路

1 先找到相同的子问题。(关注函数头是如何设计的)

2 只关心某一个子问题是如何解决的。(函数体如何设计?)

3 注意一下递归函数出口+返回值用不用接收?

以前序遍历为例

相同的子问题就是遍历根,再遍历根的左,再遍历根的右

所以我们只用传一个根即可,返回值设为void即可

    void preorder(TreeNode* root) 

函数体可以这么设计

if(root==nullptr)
{return ;
}
cout<<root->val<<" ";//根左右_preorder(root->left);_preorder(root->right);

同理,根据访问根时机的不同,我们可以写出中序、后序

中序

void inorder(TreeNode*root)
{if(root==nullptr){return ;}inorder(root->left);cout<<root->val<<" ";inorder(root->right);
}

后序

void postorder(TreeNode* root){if (root == nullptr)return;//左右根postorder(root->left);postorder(root->right);cout<<root->val<<" ";        }

二叉树遍历还是很简单的,关键一点是我们要学到递归类问题的解决思路。

等后面深搜暴搜剪枝的时候就难了。


当然,三种递归方式也是可以用非递归实现的,我们需要用到栈这个我们就放到C++啦~


链式二叉树的学习会涉及到大量的递归,下面给大家放一些算法题叭~

类似二叉树的题一般都是通过递归做的,非递归就太麻烦了。

后期更深入的就是深搜,全排列,剪枝等等了

3 计算节点个数

. - 力扣(LeetCode)

递归出口:

显然,当我们遍历到空,就没有节点了

相同子问题:

每次遍历我们需要计算根的节点个数(1个)和他下面的左、右子树的节点个数

由于我们要算的是总共的个数,所以每一层的结果我们都要记录下来

我们直接加起来即可

我们可以直接写一个3目运算符

int countNodes(struct TreeNode* root)
{return root == NULL ? 0 : countNodes(root->left) + countNodes(root->right) + 1;
}

 4 计算深度

. - 力扣(LeetCode)

计算深度也很简单

函数的的递归出口:

递归出口同样是遍历到空之后就不用走了

计算深度就是一直往最深的那层走,所以我们每次都要去找左、右子树中大的那个走

所以相同的子函数

每次选左右子树中较大的那个,并加上根的高度(1)

 int calculateDepth(TreeNode* root){return root == nullptr ? 0 : std::max(calculateDepth(root->left), calculateDepth(root->right)) + 1;}

5 第k层的节点个数

仿照上面的“节点个数”,这个题就很简单了,只需额外加上一些判断即可

int TreeKlevel(struct TreeNode* root,int k)
{assert(k > 0);if (root == NULL)return 0;if (k == 1)return 1;return TreeKlevel(root->left, k - 1) + TreeKlevel(root->right, k - 1);
}

6 相同的树

. - 力扣(LeetCode)

比较两颗树的关键是相同的根的左子树是否等于右子树

但是比较有分很多情况

如果两个节点都为空,那说明相同,返回true

一个为空一个不为空,那说明不相同,返回false

两个均不为空但值不同,说明不相同,返回false

两个均不为空值相同,说明不相同,返回true,需要再往下遍历

 其中,递归出口为

如果两个节点都为空,那说明相同,返回true(都遍历到空了,还怎么遍历?)

一个为空一个不为空,那说明不相同,返回false

两个均不为空但值不同,说明不相同,返回false

相同的子函数为

比较两个根的左子树是否相同,再比较两个根的右子树是否相同

 由于两棵树相同要同时满足两颗树的左子树相同和两棵树的右子树相同,所以最终结果要并在一起

bool isSameTree(struct TreeNode* p, struct TreeNode* q)
{if (p == NULL && q == NULL)return true;if (p == NULL || q == NULL)return false;if (p->val != q->val)return false;return isSameTree(p->left, q->left) && isSameTree(p->right, q->right);
}


7 单值二叉树

. - 力扣(LeetCode)

这个题我们可以有两种思路

第一种是将树真正的根的值作为参数递归下去,每次比较当前根的值是否等于我们参数中传递的值

这样的话,我们的递归出口就是

当遍历到空就返回true

如果当前根的值不等于我们的参数,就返回false

函数头的设计

由于原题的函数头只有一个TreeNode* ,而我们每次都要传递我们最开始根的值

所以设计一个子函数

    bool _isUnivalTree(TreeNode* root,const int val)

(如果是C++,我们可以写为下面这样,以减少拷贝)

    bool _isUnivalTree(TreeNode* root,const int& val)

相同的子函数

如果当前根的值等于我们的参数,就再遍历左子树+右子树

同理,我们要将结果并在一起

class Solution {
public:bool _isUnivalTree(TreeNode* root,const int val){if(root==nullptr)return true;if(root->val!=val)return false;return _isUnivalTree(root->left,val)&&_isUnivalTree(root->right,val);}bool isUnivalTree(TreeNode* root) {if(root==nullptr)return true;const int val=root->val;return _isUnivalTree(root->left,val)&&_isUnivalTree(root->right,val);}
};

另一种思路是这样的

我们不传递最开始根的值了,每次计算当前根的值,和他的左子树、右子树的值相比较

当然,比较之前要先判空

函数出口为

如果根为空,返回true

如果左子树不为空且值不等于根的值,返回false

如果右子树不为空且值不等于根的值,返回false

相同的子函数

如果当前根和左、右子树的值都相等,就需要比较左子树的根和他的左、右子树是否相等

还有右子树和他的左、右子树是否相等

(感觉也是非常的绕,而且还得判空)

bool isUnivalTree(struct TreeNode* root)
{if (root == NULL)return true;int val = root->val;//比左右孩子时要记得判空if (root->left != NULL && root->left->val != val)return false;if (root->right != NULL && root->right->val != val)return false;return isUnivalTree(root->left) && isUnivalTree(root->right);
}

8 二叉树查找值为x的节点

(默认树中最多只有一个节点等于x)

//找值为x的节点
BTNode* BinaryTreefind(BTNode* root,BTDataType x)
{if (root == NULL){return NULL;}if (root->data == x)return root;BTNode* lret = BinaryTreefind(root->left, x);if(lret!=NULL)return lret;BTNode* rret = BinaryTreefind(root->right, x);if (rret != NULL)return rret;return NULL;
}

这个题就简单了,如果根不是x,左子树先找,如果找到了,就返回;

如果找不到,再去右子树找;找到了,就返回

如果最终都没找到,就返回空


9 对称二叉树

. - 力扣(LeetCode)

这个题就简单了,对称二叉树的关键在于

我的左等于你的右,我的右等于你的左

有了这个认识,这个题就转化成了“相同的树”

当然由于我们比较时需要同时用到左子树、右子树

所以我们还是需要写个子函数

bool _isSymmetric(struct TreeNode* left, struct TreeNode* right)

代码是这样的~

//子函数
bool _isSymmetric(struct TreeNode* left, struct TreeNode* right)
{if (left == NULL && right == NULL)return true;if (left == NULL || right == NULL)return false;if (left->val != right->val)return false;return _isSymmetric(left->left, right->right) && _isSymmetric(left->right, right->left);
}
bool isSymmetric(struct TreeNode* root)
{if (root == NULL)return true;//我的左要等于你的右&&我的右要等于你的左return isSymmetric(root->left, root->right);}


10 层序遍历

. - 力扣(LeetCode)

层序遍历,顾名思义就是将一棵树一层一层遍历

(好消息是这道题终于不是一个递归的题目了)

最简单的就是用队列(queue)

出上一层的同时,引入下一层

他的思路是这样的

1 入根(如果根不为空)

2 当队列不为空的时候,取队头

3 如果队头的左不为空,就入根左

4 如果队头的右不为空,就入根右

5 队列为空时,遍历结束(叶子节点的左右为空,所以队列没有新元素了)

我们给一颗树大概演示一下

1、入根。

队列是这样的

2、 根出队列,打印(或其他操作),3的左右均不为空,9,20入队列

队列是这样的

3、 队头是9,出队头,但他的左右均为空,所以没有元素入队列

队列是这样的

4、 队头是20,出队头,入15,7

队列是这样的

5、 出15

6 、出7,结束#


(为了省事,直接上C++了)

 vector<vector<int>> levelOrder(TreeNode* root){queue<TreeNode*> q;vector<vector<int>> vv;int qsize = 0;if (root){//第一层只有一个qsize = 1;q.push(root);}while (!q.empty()){//每次一个vectorvector<int> v;//用qsize控制循环次数for (size_t i = 0; i < qsize; ++i){TreeNode* front = q.front();q.pop();v.push_back(front->val);if (front->left){q.push(front->left);}if (front->right){q.push(front->right);}}vv.push_back(v);qsize = q.size();}return vv;}
};

11 判断一棵树是否为完全二叉树

判断是不是完全二叉树_牛客题霸_牛客网 (nowcoder.com)

这个题的关键在于完全二叉树按层序遍历走,非空节点连续

因此我们走一边层序遍历,如果在遇到空节点都还能遇到非空节点,说明不是完全二叉树

bool isCompleteTree(BTNode* root)
{queue<TreeNode*> q;if (root)q.push(root);while (!q.empty()){auto front = q.front();q.pop();if (front == NULL){break;}else//空也插入 之后要用这个判断{q.push(front->left);q.push(front->right);}}while (!q.empty()){auto front = q.front();q.pop();if (front == NULL){return false;}}return true;
}

12 另一棵树的子树

. - 力扣(LeetCode)

有了上面的经验,这道题就简单很多了,当前根节点的值和所给根节点的值相同,

我们就去判断当前根所构成的数和所给的数是否为相同的树即可


 //use相同的树
bool isSameTree(struct TreeNode* p, struct TreeNode* q)
{if (p == NULL && q == NULL)return true;if (p == NULL || q == NULL)return false;if (p->val != q->val)return false;return isSameTree(p->left, q->left) && isSameTree(p->right, q->right);//小心别丢了返回值
}bool isSubtree(struct TreeNode* root, struct TreeNode* subRoot)
{if (root == NULL)return false;//找root的val==subRoot->valif (root->val == subRoot->val){if (isSameTree(root, subRoot)){return true;}}return isSubtree(root->left, subRoot)|| isSubtree(root->right, subRoot);
}

补充:BFS DFS

BFS:(Breadth-First Search)广度优先遍历

DFS:((Depth-First Search)深度优先遍历

这两种遍历是遍历树、图时的两种方式。


总结

 做总结,今天讲的是树,基础数据结构的树还是简单的,等到了二叉搜索树、AVL、红黑树、

B,B+树,这个时候就老实了。

水平有限,还请各位大佬指正。如果觉得对你有帮助的话,还请三连关注一波。希望大家都能拿到心仪的offer哦。

每日gitee侠:今天你交gitee了嘛

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

如若内容造成侵权/违法违规/事实不符,请联系一条长河网进行投诉反馈,一经查实,立即删除!

相关文章

学习记录:js算法(四十四):二叉树的最大深度

文章目录 二叉树的最大深度我的思路网上思路 总结 二叉树的最大深度 给定一个二叉树 root &#xff0c;返回其最大深度。 二叉树的 最大深度 是指从根节点到最远叶子节点的最长路径上的节点数。 图一&#xff1a; 示例 1&#xff1a;(如图一) 输入&#xff1a;root [3,9,20,…

Javase学习day1-前置知识

1、什么是计算机 2、 硬件及冯诺依曼结构 3、软件及软件开发 4、常用的快捷键 5、常用的Dos命令 常用的Dos命令&#xff1a;(基本都是在cmd里面写的) #盘符切换&#xff1a;直接输入那个盘符的名字加一个冒号就行。 #切换目录&#xff1a; cd change directory&#xff08;这是…

第十七章:c语言内存函数

1. memcpy使⽤和模拟实现 2. memmove使⽤ 3. memset函数的使⽤ 4. memcmp函数的使⽤ 天行健 君子以自强不息一、memcpy的使用和模拟实现 作用&#xff1a; 1. 函数memcpy从source的位置向后复制num个字节的数据到destination指向的内存位置。 2. 这个函数在遇到‘\0’的时…

用Python实现运筹学——Day 7: 线性规划的对偶理论

一、学习内容 1. 对偶问题的概念与对偶定理 线性规划的对偶理论是一种非常重要的理论&#xff0c;能揭示线性规划问题中的原问题和对偶问题之间的关系。给定一个线性规划的原问题&#xff0c;可以通过构造一个相关的对偶问题来帮助理解原问题的解&#xff0c;或者直接求解对偶…

详细分析Java中的StopWatch基本知识(附Demo)

目录 前言1. 基本知识2. Demo 前言 对于Java的基本知识推荐阅读&#xff1a; java框架 零基础从入门到精通的学习路线 附开源项目面经等&#xff08;超全&#xff09;【Java项目】实战CRUD的功能整理&#xff08;持续更新&#xff09; 1. 基本知识 StopWatch 是 Spring Fra…

【TabBar嵌套Navigation案例-新特性页面-背景图片 Objective-C语言】

一、接下来,我们来做这个背景图片的这个功能啊 1.首先呢,我们command + R跑一下,现在都是有一堆颜色, 大体的这个框架啊,我们都已经搭好了, 接下来,我们把这几个颜色啊,CollectionView的背景图片,给它设置一下, 首先呢,这个设置啊,我们这么着来做,我们呢,肯定…

解决:使用layui.treeTable.updateNode,更新表格数据后,done里面的事件丢失问题

1. 背景 在给树形表格添加行点击事件&#xff0c;并且只更新当前行数据。 treeTable.updateNode("SpeProjListId", result.LAY_DATA_INDEX, result);更新数据后&#xff0c;点击事件失效。 1. 给字段绑定事件&#xff1a; class"link_a link_style" , {…

草莓病虫害数据集1000张分5类 草莓植株黑斑病、草莓灰霉菌病、正常草莓、草莓粉霉菌病、草莓橡胶病

草莓病虫害数据集 1000张 分5类 草莓植株黑斑病、草莓灰霉菌病、正常草莓、草莓粉霉菌病、草莓橡胶病 草莓病虫害数据集介绍 名称 草莓病虫害数据集 规模 图像数量&#xff1a;1000张高质量图像类别数量&#xff1a;5类 草莓植株黑斑病 (Black Spot Disease)草莓灰霉菌病 (…

【Python】Curdling:Python 包管理的高效工具

Curdling 是一个轻量级的 Python 包管理工具&#xff0c;旨在加速 Python 包的安装和管理流程。与传统的包管理工具&#xff08;如 pip&#xff09;相比&#xff0c;Curdling 更加注重性能优化和效率&#xff0c;特别是在处理大规模依赖项和项目构建时表现优异。它通过并行化的…

360° 镜头检测铝件内壁划痕与杂质:保障铝件内孔制造质量的精准方案

在铝件内孔制造的过程中&#xff0c;内壁的质量把控是至关重要的环节。制造过程中产生的碎屑残留以及划痕等问题&#xff0c;不仅会影响铝件的外观&#xff0c;更可能对其性能和使用寿命造成严重的损害。为了精准检测这些问题&#xff0c;我们提出了一套基于 360 镜头的检测方案…

3. 将GitHub上的开源项目导入(clone)到本地pycharm上——深度学习·科研实践·从0到1

目录 1. 在github上搜项目 (以OpenOcc为例&#xff09; 2. 转移到码云Gitee上 3. 下载整个项目到本地 4. 在pycharm中打开项目 1. 在github上搜项目 (以OpenOcc为例&#xff09; 把链接复制下来&#xff0c;转移到国内Gitee上&#xff0c;会更稳定 2. 转移到码云Gitee上 &…

深度学习-11

线性层及其它层介绍 归一化层 在深度学习中&#xff0c;归一化层&#xff08;Normalization Layers&#xff09;是神经网络中非常重要的一部分&#xff0c;它们有助于在训练过程中稳定网络&#xff0c;加速收敛&#xff0c;以及提高模型的泛化能力。以下是PyTorch框架中一些常…

6.1 微服务 服务发现 架构模式分类 应用实践

微服务 服务发现 架构模式分类 应用实践 目录概述需求&#xff1a; 设计思路实现思路分析1.类型-客户端发现2.类型-服务端服务发现3.工具-Eureka4.工具-Consul5.工具-zookper服务发现的挑战服务发现的最佳实践 参考资料和推荐阅读 Survive by day and develop by night. talk …

谷歌Gemini 1.5 AI模型升级:成本更低、性能更强、响应更快

AITOP100获悉&#xff0c;9月24日&#xff0c;谷歌谷歌Gemini 1.5 AI模型升级&#xff1a;成本更低、性能更强、响应更快对其旗下Gemini 1.5 AI模型进行了升级&#xff0c;推出了Gemini-1.5-Pro-002和Gemini-1.5-Flash-002两款新模型。这两款模型在成本、性能和响应速度方面均有…

在线PDF怎么转换成JPG图片?分享14种转换操作!

作为一名社畜&#xff0c;俺也经常要将PDF转换为图片格式&#xff01; 如何进行快速转换&#xff0c;包括电脑端、在线端和手机端&#xff0c;今天俺就测评了50款工具&#xff0c;给你得出了下面这些渠道&#xff0c;不少也是免费的&#xff0c;相信对你有帮助哦&#xff01; …

Spring - @Import注解

文章目录 基本用法源码分析ConfigurationClassPostProcessorConfigurationClass SourceClassgetImportsprocessImports处理 ImportSelectorImportSelector 接口DeferredImportSelector 处理 ImportBeanDefinitionRegistrarImportBeanDefinitionRegistrar 接口 处理Configuratio…

2-3树(2-3 Tree):原理、常见算法及其应用

目录 引言 2-3树的基本概念 常见算法 查找节点 插入节点 删除节点 2-3树的应用场景 1. 文件系统目录管理 应用原理 场景描述 2. 字典编码 应用原理 场景描述 总结 优势对比 自平衡特性 灵活的节点结构 高效的操作性能 简单的实现 广泛的应用场景 数据一致…

遥感图像分割

遥感图像分割是一种应用于遥感图像的计算机视觉技术&#xff0c;用于将图像划分为不同的区域&#xff0c;每个区域代表地表的不同特征&#xff0c;如水体、森林、城市区域等。这种分割帮助我们更好地理解和分析地球表面的变化&#xff0c;对于环境监测、城市规划、农业、灾害管…

AR技术在电商行业的应用及优势有哪些?

AR&#xff08;增强现实&#xff09;技术在电商行业的应用广泛且深入&#xff0c;为消费者带来了全新的购物体验&#xff0c;同时也为商家带来了诸多优势。以下是AR技术在电商行业的主要应用场景及其优势&#xff1a; 一、应用场景 1、虚拟商品展示与试用 家具AR摆放&#x…

济南站活动回顾|IvorySQL中的Oracle XML函数使用示例及技术实现原理

近日&#xff0c;由中国开源软件推进联盟PG分会 & 齐鲁软件园联合发起的“PostgreSQL技术峰会济南站”在齐鲁开源社举办。瀚高股份IvorySQL作为合作伙伴受邀参加此次活动。 瀚高股份IvorySQL技术工程师 向逍 带来「IvorySQL中的Oracle XML函数兼容」的议题分享。在演讲中&a…