数据结构初阶---二叉树---堆

一、树

1.树的概念

树是一种非线性的数据结构,由n(n≥0)个有限结点组成的一个有层次关系的集合。形状类似一棵倒挂的树,根朝上,分支向下。

根结点没有前驱结点,可以有n(n≥0)个后继结点。

其余结点被分为M个互不相交的集合,每一个集合都是一棵子树,每棵子树的根节点均有1个前驱结点,n(n≥0)个后继结点。因此树是递归定义的。

注:除根外的结点被分为互不相交的集合,如果相交,就不是树,而是图了。

子树之间存在交集,不属于树形结构,某结点存在多个父结点即它的前驱结点>1,也不属于树形结构。

2.与树有关的概念

节点的度:一个节点含有的子树的个数称为该节点的度; 
叶节点(终端节点):度为0的节点称为叶节点;(重要)
分支节点(非终端节点):度不为0的节点;
父节点(双亲结点):若一个节点含有子节点,则这个节点称为其子节点的父节点;(重要)
子节点(孩子节点):一个节点含有的子树的根节点称为该节点的子节点;(重要)
兄弟节点:具有相同父节点的节点互称为兄弟节点;
树的度:一棵树中,最大的节点的度称为树的度;
节点的层次:从根开始定义起,根为第1层,根的子节点为第2层,以此类推;(重要)
树的高度或深度:树中节点的最大层次;(重要)
堂兄弟节点:双亲在同一层的节点互为堂兄弟;
节点的祖先:从根到该节点所经分支上的所有节点;(重要)
子孙:以某节点为根的子树中任一节点都称为该节点的子孙。                                                          森林:由m(m>0)棵互不相交的树的集合称为森林;

注:关于树的层次,一般而言是从1开始,即根所在层次为1,如果题目给定从0开始,那就从0开始,否则默认从1开始层次,因为对于无结点树而言,层次刚好是0,从0开始那么无结点树层次就为-1。

3.树的表示

如果我们使用数组来表示树,那么我们一般采用数组ArrayChild来存储孩子结点

#define N 6 //假设已知树的度为6
struct TreeNode
{TreeNodeTypeData val;struct TreeNode* ArrayChild[N];
};

但是,如果这样来表示树,那么相当于每一个分支结点,都有N个数组空间(N是树的度的值),但是树的分支结点不一定都有N个子结点,这样的表示就太过于浪费空间了。

如果用顺序表来表示树型结构,

struct TreeNode
{TreeNodeTypeData val;SeqList Childlist;
};

那么每个孩子结点都是一个顺序表,但是需要我们去构建一个顺序表,在C语言阶段也是非常费心费力的。

左孩子右兄弟表示法

树形结构的最优表示方法是左孩子右兄弟表示法

树形结构中创建两个指针,左孩子指针LeftChild与右兄弟指针RightBrother

struct TreeNode
{TreeNodeTypeData val;struct TreeNode* LeftChild;struct TreeNode* RightBrother;
};

我们通过每一次的根节点访问左孩子,就可以通过左孩子结点访问右兄弟指针找到该层次的其他结点;如果想访问下一层次,那么现在的左孩子结点访问它的左孩子结点,通过它的左孩子结点访问右兄弟指针能够找到其他所有结点。

上图中左孩子右兄弟表示形式里,未标识出来的左孩子LeftChild指针都应该指向NULL。

由此我们只需要访问上一层次某一棵子树的根节点的左孩子结点就可以遍历该层次属于该子树的所有结点。

如A的左孩子为B,A结点访问成员LeftChild得到B结点,B结点访问成员RightBrother就能够访问到同层次属于A孩子的C与D结点。如B的左孩子是E,那么通过B访问到E结点就可以遍历E、F结点。

4.树的实际运用

Linux树状目录结构。

Windows文件目录多盘形式---森林。

二、二叉树

1.概念

二叉树的结点的度均≤2(即每个结点的子结点不超过2个),同时二叉树区分左右子树(即区分左右结点),是有序树。

二叉树是特殊的树。

2.特殊的二叉树

①满二叉树

二叉树的所有结点的度都是2,那么就是满二叉树。对于满二叉树而言,每一层的结点数都达到了最大值。

假设满二叉树的深度为h,结点数为N,那么存在关系:2^h - 1 = N <==> h = log(N+1) (以2为底)

反过来说,如果一个二叉树的层数为K,它的结点数为2^K-1,那么这个二叉树就是满二叉树。

②完全二叉树

完全二叉树是效率很高的数据结构,完全二叉树是由满二叉树而引出来的。

对于深度为K的,有n个结点的二叉树,当且仅当其每一个结点都与深度为K的满二叉树中编号从1至n的结点一一对应时称之为完全二叉树。满二叉树是一种特殊的完全二叉树。

通俗来讲,除去最后一层的其它层结点数达到最大值,最后一层结点数不确定但是从左到右一定有序的二叉树就是完全二叉树。

对于完全二叉树来说,由于最后一层的结点有序,但是不确定最后一层结点个数,因此总的结点数N是一个范围,最小的时候最后一层只有1个结点,最大的时候是满二叉树。

那么N的范围为:[2^(h-1)-1+1 , 2^(h) -1]  化简--->[2^(h-1) , 2^(h) -1]

注意,对于完全二叉树来说,最后一层的结点一定是有序的!结点一定是从左至右增加的。

3.二叉树的性质

4.二叉树的存储结构

二叉树一般可以使用两种结构进行存储,顺序结构和链式结构。

①顺序存储

顺序结构存储就是使用数组来存储,一般使用数组只适合表示完全二叉树,因为不是完全二叉树会有空间的浪费。而现实中使用中只有堆才会使用数组来存储,二叉树顺序存储在物理上是一个数组,在逻辑上是一颗二叉树。

顺序存储--->使用数组存储,只适用于完全二叉树。如果用数组存储的方式对任意二叉树,那么由于每个结点的度不同,会有许多数组空间的浪费,因此数组存储只适用于完全二叉树。

将完全二叉树的结点一层一层的存入数组中,

我们会发现,父子结点的下标是存在联系的。

②链式存储

对于不是完全二叉树和满二叉树的树形结构,我们采取链式结构来进行数据存储。

二叉树的链式存储结构是指,用链表来表示一棵二叉树,即用链来指示元素的逻辑关系。通常的方法是链表中每个结点由三个域组成,数据域和左右指针域,左右指针分别用来给出该结点左孩子和右孩子所 在的链结点的存储地址 。链式结构又分为二叉链和三叉链,当前我们学习中一般都是二叉链,后面学到高阶数据结构如红黑树等会用到三叉链。

5.堆

如果有一个关键码的集合K = { , , ,…, },把它的所有元素按完全二叉树的顺序存储方式存储 在一个一维数组中,并满足: <= 且 <= ( >= 且 >= ) i = 0,1, 2…,则称为小堆(或大堆)。将根节点最大的堆叫做最大堆或大根堆,根节点最小的堆叫做最小堆或小根堆。

堆的性质:①堆中某个节点的值总是不大于或不小于其父节点的值;②堆总是一棵完全二叉树。

简单的说,如果一个数组的内容满足堆的性质,那么堆就把这个数组数据看做一棵完全二叉树。

堆只分为小(根)堆和大(根)堆,小堆表示所有的父结点的数据都小于等于其子结点;大堆表示所有的父结点的数据都大于等于其子结点。除此之外都不是堆。

有序数组是不是堆?是,但是堆不一定是有序数组。

跟之前数据结构讲的栈不同于内存中的栈类似,

数据结构的堆是一种管理数据的结构,是完全二叉树;

而操作系统中的堆,指的是一个内存区域,这个区域中的空间可以通过malloc、calloc开辟、realloc扩容使用,最后free释放。

堆的物理结构是数组;逻辑结构是完全二叉树。

堆的意义:

1.堆排序---时间复杂度O(N*logN)

2.top k问题

对于堆的实现,我们想象自己操纵的是完全二叉树,但是我们实际上操纵的是数组。

数组实现堆

结构如下,我们来实现一下大(根)堆:

//堆是由读取规定的---与栈和队列一样,堆顶开始读取然后删除
//大堆---父节点始终大于等于子节点
#define HeapDataType int
typedef struct Heap
{HeapDataType* a;int size;int capacity;
}Heap;

接口函数

①初始化HeapInit

void HeapInit(Heap* php);

//初始化
void HeapInit(Heap* php)
{assert(php);php->a = NULL;php->capacity = 0;php->size = 0;
}
②销毁HeapDestroy

void HeapDestroy(Heap* php);

//销毁
void HeapDestroy(Heap* php)
{assert(php);free(php->a);php->a = NULL;php->capacity = 0;php->size = 0;
}
③插入HeapPush---O(logN)

void HeapPush(Heap* php, HeapDataType x);

在一个堆中插入一个新的数据x,那么我们就必须得将该数据移动到合适的位置,确保插入后新的数组仍然是一个大堆。

首先先写出扩容判断的代码以及插入的代码:

	//检测容量---不用单独开接口,因为只有一个插入if (php->size == php->capacity){int NewCapacity = php->capacity == 0 ? 4 : php->capacity * 2;HeapDataType* tmp = (HeapDataType*)realloc(php->a, sizeof(HeapDataType) * NewCapacity);if (tmp == NULL){perror("realloc fail");exit(-1);}php->a = tmp;php->capacity = NewCapacity;}//插入数据php->a[php->size] = x;php->size++;

那么就差最后一步,判断是否需要更换位置:

我们知道,对于大堆而言,父节点的数据始终大于等于子节点的数据,对于目前插入在最后一层的新结点而言,我们需要将其与它的父辈、祖辈,与它的祖先进行比较,如果新结点的数据大于父节点,那么就需要将新结点与父节点交换,然后接着向上比较:

需要交换数据,那么我们封装为一个Swap函数用来交换数据:

//交换函数
void Swap(HeapDataType* p1, HeapDataType* p2)
{HeapDataType tmp = *p1;*p1 = *p2;*p2 = tmp;
}

所以对于插入的值的比较,我们需要通过该结点找到其父结点,根据前面学到的堆的父子结点关系,我们知道,子节点下标child,(child-1)/2就能够得到父节点下标parent

最后的比较要到什么时候终止呢?如果最慢的情况,插入的数据比原根节点都要大,那么就要换到A结点处--->因此循环的终止条件应为child == 0 。那么我们将判断和移动数据的过程封装成一个函数UpAdjust(向上调整)。

向上调整算法UpAdjust

void UpAdjust(Heap* php, int child);

    //向上调整算法UpAdjust(php, php->size - 1);

传入的child是插入结点的下标,由于前面插入后size自增1了,所以这个地方传进来的应该是size-1。

//向上调整算法---最坏情况调整深度h次,而完全二叉树的h与logN相关---因此时间复杂度logN
void UpAdjust(Heap* php, int child)
{assert(php);int parent = (child - 1) / 2;//父节点下标为子节点-1再除2while (child > 0){if (php->a[parent] < php->a[child]){Swap(&php->a[parent], &php->a[child]);child = parent;parent = (parent - 1) / 2;}elsebreak;}
}
④删除HeapPop---O(logN)

void HeapPop(Heap* php);

在堆的实现中的删除操作,删除的是堆顶元素---即根结点元素。

但是我们又不能够直接进行覆盖操作,如果直接进行覆盖,一会导致原来的这个父子下标关系改变,摧毁了这个堆;二是数组的头删挪位覆盖时间复杂度为O(N)。

所以就有一种方法:我们先交换堆顶元素与最后一个元素,然后进行尾删--->就删除了堆顶元素。

对于数组结构而言,尾删是非常方便的,size自减1即可。然后这个时候,除了堆顶元素改变,其他位置上的元素均没有变化,其实类似于向上调整---我们实现一个向下调整算法DownAdjust。

//删除堆顶结点---时间复杂度O(logN)
void HeapPop(Heap* php)
{assert(php);Swap(&php->a[0], &php->a[php->size - 1]);//交换--->尾删原堆顶php->size--;//尾删原堆顶//向下调整算法---O(logN),要将交换上去的结点向下调整DownAdjust(php->a, php->size, 0);
}

DownAdjust向下调整算法内,我们将堆顶元素与它的子节点元素中较大的进行判断,如果堆顶元素大,那么符合大堆,不用进行交换元素,反之,堆顶元素小于较大的子结点元素,那么交换二者,将交换后的处于子节点处的该元素继续与这个位置上它的子结点的较大元素进行比较。

向下调整算法DownAdjust

 void DownAdjust(HeapDataType* a, int size, int parent);

//向下调整---时间复杂度O(logN)
void DownAdjust(HeapDataType* a, int size, int parent)
{int child = parent * 2 + 1;while (child < size){if ( child+1 < size && a[child] < a[child + 1])//小心越界{child++;}if (a[child] > a[parent]){Swap(&a[child], &a[parent]);parent = child;child = 2 * child + 1;}elsebreak;}
}

为了防止if else判断两个子节点谁大的代码冗长,我们选择使用了假设判断法,先将其中的左节点下标给child,然后再if判断如果左节点数据小于右孩子结点,那么将右节点下标给child,实际上就与child自增1没有区别。 极端情况下,当我们判断两者大小时,可能出现child+1的越界访问的问题。 因此需要在if的条件中多加上一条child+1<size

找到更大的孩子节点、然后进行比较,这个整体是一个循环过程,因为交换到堆顶的数据最多需要进行深度h次交换,如果该数据足够小的话。循环进行条件就是chiild<size,即下标不能越界。在发现了该节点处的数据大于等于较大子节点的数据时,就满足了大堆性质,直接跳出循环即可。

⑤获取堆顶元素HeapTop

HeapDataType HeapTop(Heap* php);

//获取堆顶元素
HeapDataType HeapTop(Heap* php)
{assert(php);assert(php->size);return php->a[0];
}

有关于获取堆顶元素的接口,我们一般与删除堆顶元素联合起来使用,大堆的堆顶元素读取然后删除,执行k次,就能够找到这个堆中前k大的数据。

同时如果我们也可以这样获取降序的全部数据--->大堆就能够获取降序,小堆能够获取升序数据。

⑥获取堆有效元素个数HeapSize

int HeapSize(Heap* php);

//获取堆有效元素个数
int HeapSize(Heap* php)
{assert(php);return php->size;
}
⑦判断堆是否为空HeapEmpty

bool HeapEmpty(Heap* php);

//判断堆是否为空
bool HeapEmpty(Heap* php)
{assert(php);return php->size == 0;
}

堆的应用

1.堆排序问题

时间复杂度O(N*logN)。

上面通过数组实现了堆,那么给定一个数组,我们当然可以将数组的数据插入的堆结构中,进行向上调整,然后通过获取堆顶数据,删除堆顶向下调整的小套餐来打印排序后的数据,但是这样的操作有两个缺点:①需要一个完整的堆结构②原数组其实没有改变

那么实际上不需要如上操作进行排序,堆排序可以对该数组重新排序。

升序--->①建立大堆②进行排序

大堆父结点大于子节点,为什么会升序?===>这就涉及到伪删除,我们利用到堆接口函数删除堆顶元素的实现,本质是将尾部数据与堆顶数据互换再删除堆顶数据,那么其实此时堆顶数据在末尾,只要我们不删除,就能够实现每一次的伪删除会将最大的数据移动到数组尾部。每一次互换之后我们需要进行向下调整,当然调整不会包括互换后的尾结点数据。

降序--->①建立小堆②进行排序

降序为什么建立小堆同上。

下面演示升序:

①升序演示---建立大堆

建堆有两种方式:我们可以通过将每个数组数据从起始开始进行向上调整;也可以从尾结点的父结点开始进行向下调整。目的都是为了将数组数据排放成堆的形式。

从起始开始向上调整:时间复杂度O(N*logN)

	//数组建堆---//方法一:第一个数据成堆,其他数据依次入堆然后向上调整 O(N*logN)for (int i=1; i < n; i++){UpAdjust(a, i);//数据依次向上调整成大堆}

从尾结点的父结点开始向下调整:时间复杂度O(N)

	//方法二:从最后一个结点的父结点开始,倒着对所有非叶子结点进行向下调整 O(N)for (int i = (n - 1 - 1) / 2; i >= 0; i--){DownAdjust(a, n, i);}

起始我们也能看出来时间复杂度差在哪里,对于方式二而言,最后一层结点数最多,但是对于方式二却不用对它们进行任何次数的调整,因此时间复杂度比方式一要小。

②升序演示---伪删除/选数

将堆顶与尾部数据交换,然后传入DownAdjust向下调整的end每次遍历减1。第一次交换后传入的size应该是减1之后的===>即下面代码的end = n-1。

	//伪删除---堆顶数据与尾部交换,size--,向下调整===>那么数组尾部就是最大数据 O(NlogN)int end = n - 1;while (end > 0){Swap(&a[0], &a[end]);DownAdjust(a, end, 0);//依次向下调整end--;}

一个数据在向下调整最多层数h次,N个数据===>N*logN。

选数操作的时间复杂度O(N*logN)。 

推排序升序数组:

//堆排序
//堆排序是将给出的数组数据先排序成堆,然后再进行排序
//而不是将数据依次插入实现好的堆中
void HeapSort()//测试---堆排序
{int a[] = { 1,5,3,6,8,2,1,5,8 };int n = sizeof(a) / sizeof(a[0]);//数组建堆---//方法一:第一个数据成堆,其他数据依次入堆然后向上调整 O(N*logN)for (int i=1; i < n; i++){UpAdjust(a, i);//数据依次向上调整成大堆}//方法二:从最后一个结点的父结点开始,倒着对所有非叶子结点进行向下调整 O(N)for (int i = (n - 1 - 1) / 2; i >= 0; i--){DownAdjust(a, n, i);}//伪删除---堆顶数据与尾部交换,size--,向下调整===>那么数组尾部就是最大数据 O(NlogN)int end = n - 1;while (end > 0){Swap(&a[0], &a[end]);DownAdjust(a, end, 0);//依次向下调整end--;}for (int i = 0; i < n; i++){printf("%d ", a[i]);}
}

2.top k问题
思路一

前面接口函数中提到了top k问题的解决方法,我们可以通过将这些数据以大堆的形式全部插入到堆中,那么再循环k次来删除k次堆顶数据并获取前k个最大的数据。但是如果数据基数N非常大,那么我们需要为这个大堆开辟的空间是非常非常大的,举个例子,N=100亿--->100亿个整型--->约为400亿字节--->约为40G的内存大小,现如今普通人家的电脑内存大小,16-32G,无法完成,而且就算能够完成,也不适用。

思路二---最优解

在top k问题中,如果要在一个非常非常大的N个数据中,找出k个最大的数据,那么我们可以通过这样的办法实现:

创建一个k个数据的小堆,先将最开始的k个数据存入小堆中,然后在剩余的数据依次与小堆堆顶元素比较,如果大于堆顶元素,那么覆盖堆顶元素并向下调整,使其依然满足小堆。那么到最后,小堆中就是最大的k个数据了。这种方法是非常便捷的,时间复杂度为O(N*logk)即O(N),但是空间复杂度只有O(1),而思路一的空间复杂度达到了O(N),而且基本上难以实现。

时间复杂度O(N*logk)==O(N)。

那么通过最优解,就可以从N个数据中找k个最大的数据,假设我们在文件中保存1000000个随机值,然后通过堆来找出k个最大的值:

首先我们在文件中写入N=1000000个随机值:

//TopK问题---在N个数据中找到K个最大的数据
void CreatNData()//在文件中创建1000000个数据
{FILE* fin = fopen("data.txt", "w");if (fin == NULL){perror("fopen fail");return;}int n = 10000000;srand(time(0));while (n--){int x = (rand() + n) % 1000000;//数据都是小于1000000的fprintf(fin, "%d\n", x);}fclose(fin);
}

这里写入的数据模了1000000方便我们后续测试是否成功取出k个最大数据。

我们执行一次CreatNdatra函数,那么就能够创建这个data.txt文件并在其中写入1000000个数据。

由于我们需要取较大数据,那么就需要用小堆===>即原来适用于大堆的向上调整与向下调整算法需要进行细微的修改。

下面是小堆的向上向下调整算法(其实只需调整几个大于小于号)

//小堆的向上向下调整算法
//向上调整算法---最坏情况调整深度h次,而h大概率等于logN相关---因此时间复杂度logN
void UpAdjust_lowheap(HeapDataType* a, int child)
{int parent = (child - 1) / 2;//父节点下标为子节点-1再除2while (child > 0){if (a[parent] > a[child]){Swap(&a[parent], &a[child]);child = parent;parent = (parent - 1) / 2;}elsebreak;}
}
//向下调整---时间复杂度O(logN)---小堆
void DownAdjust_lowheap(HeapDataType* a, int size, int parent)
{int child = parent * 2 + 1;while (child < size){if (child + 1 < size && a[child] > a[child + 1])//小心越界{child++;}if (a[child] < a[parent]){Swap(&a[child], &a[parent]);parent = child;child = 2 * child + 1;}elsebreak;}
}

写一个函数PrintTopK,解决topk问题:

void PrintTopK(FILE* file, int k);

创建一个minheap数组存储k个数据,VS不支持变长数组,那么我们使用malloc开辟k个数据大小的空间给minheap。

int* minheap = (int*)malloc(sizeof(int) * k);

第一步将该文件中的前k个数据存入小堆进行向上调整;

	//读前k个数据建小堆for (int i = 0; i < k; i++){fscanf(fout, "%d", &minheap[i]);UpAdjust_lowheap(minheap, i);}

第二步将文件中剩余的N-k个数据依次遍历与堆顶数据比较,大于等于堆顶数据则替换堆顶数据然后进行向下调整使之依然成小堆。

	//剩下N-K个数据依次遍历与堆顶数据比较,大于等于堆顶数据进入堆int x = 0;while (fscanf(fout, "%d", &x) != EOF)//先读到x中,若x大于等于堆顶,放入堆顶并向下调整{if (x >= minheap[0]){minheap[0] = x;DownAdjust_lowheap(minheap, k, 0);}}

fscanf读取结束返回EOF,那么循环条件可以直接写作如上图所示,将每次遍历的文件中的数据存入x中,x与堆顶数据进行判断,若大于等于堆顶数据,替换堆顶数据并向下调整。

注:scanf函数以及fscanf等函数,默认遇到空格或者换行\n终止。 

最后我们可以打印minheap来观察数据,记得最后需要释放堆中开辟的空间以及关闭文件。

	for(int i=0;i<k;i++){printf("%d ",minheap[i]);}free(minheap);minheap = NULL;fclose(fout);

前面手动添加了取模1000000,我们可以通过手动修改data.txt中的5个数据使其大于1000000,再次执行程序来观察最大值的变化,从而判断程序是否正确无误。

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

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

运维 mysql、redis 、RocketMQ性能排查

MySQL查看数据库连接数 1. SHOW STATUS命令-查询当前的连接数 MySQL 提供了一个 SHOW STATUS 命令&#xff0c;可以用来查看服务器的状态信息&#xff0c;包括当前的连接数。 SHOW STATUS LIKE Threads_connected;这个命令会返回当前连接到服务器的线程数&#xff0c;即当前…

.NET6 WebAPI从基础到进阶--朝夕教育

1、环境准备 1. Visual Studio 2022 2. .NET6 平台支持 3. Internet Information Services 服务器&#xff08; IIS &#xff09; 4. Linux 服务器 【 CentOS 系统】 ( 跨平台部署使用 ) 5. Linux 服务器下的 Docker 容器&#xff08; Docker 部署使用&#xff09; …

STM32仿真——01创建工程

目录 1.需要用到的软件工具​编辑 2.第一步Proteus软件新建工程​编辑 3.第二步——stm32cubumx 4、MDK代码编写 #注意安装的过程或者使用过程使用英文&#xff0c;以防报错&#xff1b; 1.需要用到的软件工具 2.第一步Proteus软件新建工程 选中&#xff0c;默认 先布局&…

相位小数偏差(UPD)估计基本原理

PPP中的一个关键性难题在于非差模糊度固定&#xff0c;成功固定非差模糊度可以使 PPP 的收敛速度和定位精度得到显著提升 。 相位小数偏差 (UPD) 是致使相位模糊度失去整数特性的主要因素&#xff0c;精确估计并校正 UPD 是实现非差模糊度固定的重要前提&#xff0c;也是实现…

我们来编程 -- win11多jdk版本切换

题记 售前的酒喝到位了调研需求及文档整理出来了开发要入场了&#xff0c;真惨啊&#xff01;年底了&#xff0c;手里活干的好好的&#xff0c;因为flyback在项目地&#xff0c;硬是被安排进来了拥抱变化&#xff0c;我呸…不得不切换系统&#xff0c;构建代码&#xff0c;一股…

飞腾派4g版本笔记一

飞腾派4g版本开箱体验 开箱包裹内容 打开包装&#xff0c;你可以看到以下物品 一个绿联的usb3.0读卡器、sandisk的32g内存卡(太好了)飞腾派4g版本开发板带散热风扇&#xff08;通过DDR存储的丝印看到是长鑫存储&#xff0c;即为4g内存版本&#xff09;输出为12v 3A的电源适配…

从零用java实现 小红书 springboot vue uniapp (2)主页优化

前言 移动端演示 http://8.146.211.120:8081/#/ 前面的文章我们基本完成了主页的布局 今天我们具体的去进行实现 并且分享我开发时遇到的问题 首先先看效果 java仿小红书主页 实现效果为 1.顶端全屏切换 2.上划加载更多 3.下拉当前页整体刷新 顶端全屏切换我们选择 gui-switch…

3D 生成重建031-One-2-3-45多视图+Neus生成3D

3D 生成重建031-One-2-3-45多视图Neus生成3D* 文章目录 0 论文工作论文方法2 实验结果 0 论文工作 单图像三维重建是一项重要但极具挑战性的任务&#xff0c;需要对现实世界有深入的了解。许多现有的方法通过在二维扩散模型的引导下优化神经辐射场来解决这个问题&#xff0c;但…

JAVA:访问者模式(Visitor Pattern)的技术指南

1、简述 访问者模式(Visitor Pattern)是一种行为型设计模式,允许你将操作分离到不同的对象中,而无需修改对象本身的结构。这种模式特别适合复杂对象结构中对其元素进行操作的场景。 本文将介绍访问者模式的核心概念、优缺点,并通过详细代码示例展示如何在实际应用中实现…

【C++】数的性质问题分析与优化

博客主页&#xff1a; [小ᶻ☡꙳ᵃⁱᵍᶜ꙳] 本文专栏: C 文章目录 &#x1f4af;前言&#x1f4af;题目解析与分析题目描述题目分析 &#x1f4af;我的解法与详细解读初始代码实现解法分析 &#x1f4af;老师解法与其改进思路老师代码实现改进与优势 &#x1f4af;对比分析…

让文案生成更具灵活性/chatGPT新功能canvas画布编辑

​ ​ OpenAI最近在2024年12月发布了canvas画布编辑功能&#xff0c;这是一项用途广泛的创新工具&#xff0c;专为需要高效创作文案的用户设计。 无论是职场人士、学生还是创作者&#xff0c;这项功能都能帮助快速生成、优化和编辑文案&#xff0c;提升效率的同时提高内容质量…

分布式事物XA、BASE、TCC、SAGA、AT

分布式事务——Seata 一、Seata的架构&#xff1a; 1、什么是Seata&#xff1a; 它是一款分布式事务解决方案。官网查看&#xff1a;Seata 2.执行过程 在分布式事务中&#xff0c;会有一个入口方法去调用各个微服务&#xff0c;每一个微服务都有一个分支事务&#xff0c;因…

深度学习——激活函数、损失函数、优化器

深度学习——激活函数、损失函数、优化器 1、激活函数1.1、一些常见的激活函数1.1.1、sigmoid1.1.2、softmax1.1.3、tanh1.1.4、ReLU1.1.5、Leaky ReLU1.1.6、PReLU1.1.7、GeLU1.1.8、ELU 1.2、激活函数的特点1.2.1、非线性1.2.2、几乎处处可微1.2.3、计算简单1.2.4、非饱和性1…

opencv-python的简单练习

1、读取一张彩色图像并将其转换为灰度图。 import cv2 img cv2.imread("../1iamge/a.jpg") # 灰度化 img_gray cv2.cvtColor(img, cv2.COLOR_BGRA2GRAY) cv2.imshow(img, img) cv2.imshow(img_gray, img_gray) cv2.waitKey(0) 2、编写程序&#xff0c;读取一张彩色…

Java、鸿蒙与嵌入式开发:技术选择与职业发展分析

在当今快速发展的科技领域中&#xff0c;Java、鸿蒙和嵌入式开发代表着不同的技术方向和职业机遇。每个方向都有其独特的市场价值和发展前景&#xff0c;让我们深入分析这三个领域的特点、发展趋势和职业规划。 Java开发方向已经发展了二十多年&#xff0c;仍然在软件开发领域…

【mybatis】缓存

目录 1. mybatis的运行 1.1 引言 1.2 具体运行&#xff1a; 1.3 sqlSession 介绍local catch 2. 缓存 2.1 概念 2.2 使用缓存的原因 2.3 什么样的数据能使用缓存 3. Mybatis缓存 3.1 一级缓存 3.1.1 测试一级缓存 3.1.2 缓存失效的四种情况 $1 sqlSession不同 $…

前端成长之路:CSS元素显示模式

元素显示模式 网页中的标签非常的多&#xff0c;在不同的地方会使用到不同类型的标签&#xff0c;了解这些标签的特点可以更好的布局我们的网页。 元素显示模式就是元素&#xff08;标签&#xff09;按照什么方式进行显示&#xff0c;比如&#xff1a;div标签会自己独占一行&a…

Unity类银河战士恶魔城学习总结(P178 Archer s arrow 弓箭手的箭)

【Unity教程】从0编程制作类银河恶魔城游戏_哔哩哔哩_bilibili 教程源地址&#xff1a;https://www.udemy.com/course/2d-rpg-alexdev/ 本章节制作了一个弓箭手的箭 Arrow_Controller.cs 1.OnTriggerEnter2D方法 功能&#xff1a;检测箭矢与其他对象的碰撞。逻辑&#xff1…

机器学习周报(12.9-12.15)

文章目录 摘要Abstract 1 Swin Transformer1.1 输入1.2 Patch Partition1.3 Linear Embedding1.4 Patch Merging1.5 Swin Transformer Block1.6 代码总结 摘要 本篇博客介绍了采用类似于卷积核的移动窗口进行图像特征提取的Swin Transformer网络模型&#xff0c;这是一种基于T…

【C++游记】Vector的使用和模拟实现

枫の个人主页 你不能改变过去&#xff0c;但你可以改变未来 算法/C/数据结构/C Hello&#xff0c;这里是小枫。C语言与数据结构和算法初阶两个板块都更新完毕&#xff0c;我们继续来学习C的内容呀。C是接近底层有比较经典的语言&#xff0c;因此学习起来注定枯燥无味&#xf…