用链表来表示一棵二叉树,即用指针指向来指示元素的逻辑关系。通常的方法是链表中每个结点由三个域组成,数据域和左右指针域,左右指针分别用来给出该结点左孩子和右孩子所在的链结点的存储地址 。

我们之前就已经说过,二叉树是递归定义的,也就是说,一棵二叉树是由根节点,左子树,右子树组成的,而左子树和右子树本身又是一棵二叉树。我们要牢记二叉树是递归定义的这一特点,在后续操作中我们将会体会递归的暴力美学。

二叉树的前中后序遍历

前序遍历(先根遍历):先遍历根节点,再遍历左子树,最后遍历右子树。(根左右)

中序遍历(中根遍历):先遍历左子树,再遍历根节点,最后遍历右子树。(左根右)

后序遍历(后根遍历):先遍历左子树,再遍历右子树,最后再遍历根节点。(左右根)

根据上述定义,写出这个二叉树分别按三种遍历方式得到的序列:

二叉树相关方法的代码实现

二叉树的结构

//定义树的结构
typedef char btdatatype;typedef struct btnode
{btdatatype data;  //节点数据struct btnode* left;//左孩子结点struct btnode* right;//右孩子结点
}btnode;

二叉树节点的创建

//建立新的树节点
btnode* buynode(btdatatype x)
{btnode* newnode = (btnode*)malloc(sizeof(btnode));newnode->data = x;newnode->left = NULL;newnode->right = NULL;
}

二叉树的前序遍历

//前序遍历树的节点
void preorder(btnode* root)
{//递归的终止条件:遍历到空节点if (root == NULL){return;}//先访问根节点printf("%c ", root->data);//再遍历左子树preorder(root->left);//再遍历右子树preorder(root->right);
}

画图说明递归过程(红色的线表示递推,绿色的线表示回归,递推就意味着要创建函数栈桢,回归表示函数栈桢销毁,同时要返回值):

二叉树的中序遍历

//中序遍历树的节点
void inorder(btnode* root)
{if (root == NULL){return;}//先遍历左子树inorder(root->left);//再访问根节点printf("%c ", root->data);//再遍历右子树inorder(root->right);
}

在前面两个递归过程的解释中,我们发现,在递归调用时,产生的函数栈桢形成的结构与二叉树的形态结构长得一模一样,所以接下来我们模拟递归过程的函数栈桢的调用时,就不用再使用具体的函数栈桢图了,可以直接使用二叉树的每个节点来进行模拟比较。

二叉树的后序遍历

//后序遍历树的节点
void postorder(btnode* root)
{if (root == NULL){return;}//先遍历左子树postorder(root->left);//再遍历右子树postorder(root->right);//最后访问根节点printf("%c ", root->data);
}

三种遍历方式下,产生的函数栈桢图一样。

二叉树中结点的个数

//二叉树节点个数
void binarytreesize_1(btnode* root, int size)
{if (root == NULL){return;}size++;binarytreesize_1(root->left, size);binarytreesize_1(root->right, size);
}

大家看看上面的代码能正确完成任务嘛,我们可以再写一个测试代码来测试一下:

屏幕前的家人们觉得结果会是6嘛?

上面的代码应该是想实现在递归过程中只要遇到的节点不是空节点就对size进行累加,而且我们传入的size的初始值是0,按理来说应该会完成任务,但是size的大小竟然没有发生改变。

这是因为,我们采用的是传值调用,形参的改变不会影响实参,所以size的值不会发生改变,还是0.但我们也得到了解决思路,直接采用传址调用就好了呀:

void binarytreesize_2(btnode* root, int* psize)
{if (root == NULL){return;}(*psize)++;binarytreesize_2(root->left, psize);binarytreesize_2(root->right, psize);
}

我们用相同的测试用例测试一下结果:

看来这个代码好像确实可以帮我们完成任务。

上面代码的递归过程图解:

如果我们在多测试几次代码:

我们会发现size的值在不断递增,这是因为第二次和第三次调用函数时,我们没有将size重新赋值为0,直接在原来值“6”的基础上进行累加了,这也正是我们这个代码的缺点所在:每一次调用函数前都要将size的值重新置为0.

那还有没有别的算法解决问题呢,有的兄弟们,有的。

我们知道,二叉树是递归定义的,它是由根节点、左子树、右子树组成。要求整棵二叉树节点个数,不就是求根节点的个数加上左子树中节点的个数再加上右子树中节点的个数嘛,而根节点的个数本来就是1,这样我们就把一个大问题拆解成相似的小问题了,这种情况下,使用递归简直就不要太容易了。

int binarytreesize(btnode* root)
{if (root == NULL){return 0;}return 1 + binarytreesize(root->left) + binarytreesize(root->right);
}

我们再来用相同的测试用例测试一下:

可以看到,这个代码可以通过测试样例,同时避免了上一个代码的缺点。

再来模拟一下递归调用的过程:

二叉树中叶子结点的个数

//二叉树叶子结点个数
int binarytreeleafsize(btnode* root)
{if (root == NULL){return 0;}if (root->left == NULL && root->right == NULL){return 1;}return binarytreeleafsize(root->left) + binarytreeleafsize(root->right);
}

求第k层节点的个数

//二叉树第k层节点个数
int binarytreelevelksize(btnode* root, int k)
{if (root == NULL){return 0;}if (k == 1)//树的根在第一层{return 1;}return binarytreelevelksize(root->left, k - 1) + binarytreelevelksize(root->right, k - 1);
}

求二叉树的深度

//二叉树的深度/高度:深度从1开始哦
int binarytreedepth(btnode* root)
{if (root == NULL){return 0;}int leftlen = binarytreedepth(root->left);int rightlen = binarytreedepth(root->right);return 1 + (leftlen > rightlen ? leftlen : rightlen);
}

在树中查找节点

//二叉树查找值为x的节点
btnode* binarytreefind(btnode* root, btdatatype x)
{if (root == NULL){return NULL;}if (root->data == x){return root;}//如果在左子树中找到了,就不用在右子树中找了btnode* leftfind = binarytreefind(root->left, x);if (leftfind){return leftfind;}//说明左子树中没有找到//再到右子树中继续找btnode* rightfind = binarytreefind(root->right, x);if (rightfind){return rightfind;}//说明右子树中也没有找到return NULL;
}

二叉树的销毁

//二叉树的销毁
//二叉树的销毁:要销毁二叉树,肯定是要先遍历二叉树,那么我们应该如何遍历二叉树呢?
//可以参考已有的遍历方式:前序?中序?后序?
//前序和中序在遍历完子树前都会先遍历完根,先销毁根的话,就没办法再找到根的子树了,所以这两种遍历方式都不可取,我们只能后序遍历来销毁二叉树
void  binarytreedestroy(btnode** proot)
{if (*proot == NULL){return;}binarytreedestroy(&((*proot)->left));binarytreedestroy(&((*proot)->right));free(*proot);*proot = NULL;
}

二叉树的层序遍历

二叉树的层序遍历是指 按照树的层级顺序,从上到下、从左到右依次访问所有节点。这种遍历方式也称为 广度优先搜索(BFS),通常需要借助 队列 数据结构来实现。

在这个层序遍历的实现中,我们需要使用队列来辅助,所以我们之前写的队列的实现方法就派上用场了,还没看的朋友可以点击链接:数据结构里的 “排队系统”:队列的生活映射与代码实现-CSDN博客进行观看哦。

如果之前已经实现了队列相关方法的盆友,可以按照以下步骤直接添加:

找到解决方案资源管理器->右键单击头文件->添加->现有项->来到你所写的队列的实现方法的项目路径下:

按住ctrl键,选中queue.c和queue.h两个文件,直接ctrl+c复制两个文件,再来到当前二叉树实现方法的路径下,将两个文件粘贴到当前路径:

此时两个文件还是选中的状态,我们直接点击添加就好。

添加好以后,将queue.c文件拖拽到源文件的地方就好了。

现在既然我们要是用队列中的相关方法,还要包含对应的头文件,那就要在当前二叉树的项目中的头文件中添加queue.h的头文件包含:

同时,之前我们在实现队列的时候,队列中存储的元素类型是int类型的,现在我们要将它改成二叉树节点类型的,但是队列的实现方法中并没有关于二叉树的结构声明,难道需要在队列实现方法的头文件中加上:"include binarytree.h"?这样就会造成头文件的相互包含,就会引发错误,正确的方法就是在queue.h文件中加上这样一句代码:

typedef struct btnode* Qdatatype;

它的作用是:告诉队列:你要存的数据类型是一个指向 struct btnode 的指针,但我不关心它长什么样。换句话说:

  • 队列只负责“存指针、传指针”,不关心这个指针指向的结构体有哪些成员。
  • 真正的 struct btnode 定义可以放在别的文件里(比如 tree.c),queue.h 不需要知道细节

那现在我们就来手动实现一下层序遍历的代码吧:

//层序遍历
void levelorder(btnode* root)
{if (root == NULL){return;}//先创建一个队列Queue q;QueueInit(&q);//先让队头元素入队列QueuePush(&q, root);//不断循环直到队列为空while (!QueueEmpty(&q)){//先取队头元素btnode* top = QueueFront(&q);//出队QueuePop(&q);//访问队头元素printf("%c ", top->data);//如果左孩子不为空,左孩子入队if (top->left){QueuePush(&q, top->left);}//如果右孩子不为空,右孩子入队if (top->right){QueuePush(&q, top->right);}}QueueDesTroy(&q);
}

判断是否为完全二叉树

//判断是否为完全二叉树
bool binarytreecomplete(btnode* root)
{if (root == NULL){return true;}//利用层序遍历的思想判断是否为完全二叉树//先创建一个队列Queue q;QueueInit(&q);//先让队头元素入队列QueuePush(&q, root);//不断循环直到队列为空while (!QueueEmpty(&q)){//先取队头元素btnode* top = QueueFront(&q);//出队QueuePop(&q);//如果队头元素为空,就直接跳出循环if (top == NULL){break;}//走到这里,说明队列不为空,那么就让队头元素的左右孩子都入队列QueuePush(&q, top->left);QueuePush(&q, top->right);}//现在就只需要判断第一次得到空的队头元素后,剩下的元素是否是既有空节点又有非空节点(非完全二叉树)//如果剩下的元素只有空节点(完全二叉树)while (!QueueEmpty(&q)){//取队头元素btnode* top = QueueFront(&q);QueuePop(&q);if (top != NULL){QueueDesTroy(&q);return false;}}QueueDesTroy(&q);return true;
}

今天的内容就是这些,我们可以看到在这一小节中我画了很多图,其实画图是很利于我们理解算法的,小伙伴们自己也要多练练哦!!

代码整合

//binarytree.h
#pragma once#include"queue.h"
#include<stdio.h>
#include<stdlib.h>
#include<assert.h>
#include<stdbool.h>//定义树的结构
typedef char btdatatype;typedef struct btnode
{btdatatype data;struct btnode* left;struct btnode* right;
}btnode;//建立新的树节点
btnode* buynode(btdatatype x);//前序遍历树的节点
void preorder(btnode* root);//中序遍历树的节点
void inorder(btnode* root);//后序遍历树的节点
void postorder(btnode* root);//二叉树节点个数
int binarytreesize(btnode* root);
void binarytreesize_1(btnode* root, int size);
void binarytreesize_2(btnode* root, int* psize);//二叉树叶子结点个数
int binarytreeleafsize(btnode* root);//二叉树第k层节点个数
int binarytreelevelksize(btnode* root, int k);//二叉树的深度/高度
int binarytreedepth(btnode* root);//二叉树查找值为x的节点
btnode* binarytreefind(btnode* root, btdatatype x);//二叉树的销毁
void  binarytreedestroy(btnode* root);//层序遍历
void levelorder(btnode* root);//判断是否为完全二叉树
bool binarytreecomplete(btnode* root);
//binarytree.c
#define  _CRT_SECURE_NO_WARNINGS 1#include"binarytree.h"//建立新的树节点
btnode* buynode(btdatatype x)
{btnode* newnode = (btnode*)malloc(sizeof(btnode));newnode->data = x;newnode->left = NULL;newnode->right = NULL;
}//前序遍历树的节点
void preorder(btnode* root)
{//递归的终止条件:遍历到空节点if (root == NULL){return;}//先访问根节点printf("%c ", root->data);//再遍历左子树preorder(root->left);//再遍历右子树preorder(root->right);
}//中序遍历树的节点
void inorder(btnode* root)
{if (root == NULL){return;}//先遍历左子树inorder(root->left);//再访问根节点printf("%c ", root->data);//再遍历右子树inorder(root->right);
}//后序遍历树的节点
void postorder(btnode* root)
{if (root == NULL){return;}//先遍历左子树postorder(root->left);//再遍历右子树postorder(root->right);//最后访问根节点printf("%c ", root->data);
}//二叉树节点个数
void binarytreesize_1(btnode* root, int size)
{if (root == NULL){return;}size++;binarytreesize_1(root->left, size);binarytreesize_1(root->right, size);
}void binarytreesize_2(btnode* root, int* psize)
{if (root == NULL){return;}(*psize)++;binarytreesize_2(root->left, psize);binarytreesize_2(root->right, psize);
}int binarytreesize(btnode* root)
{if (root == NULL){return 0;}return 1 + binarytreesize(root->left) + binarytreesize(root->right);
}//二叉树叶子结点个数
int binarytreeleafsize(btnode* root)
{if (root == NULL){return 0;}if (root->left == NULL && root->right == NULL){return 1;}return binarytreeleafsize(root->left) + binarytreeleafsize(root->right);
}//二叉树第k层节点个数
int binarytreelevelksize(btnode* root, int k)
{if (root == NULL){return 0;}if (k == 1)//树的根在第一层{return 1;}return binarytreelevelksize(root->left, k - 1) + binarytreelevelksize(root->right, k - 1);
}//二叉树的深度/高度
int binarytreedepth(btnode* root)
{if (root == NULL){return 0;}int leftlen = binarytreedepth(root->left);int rightlen = binarytreedepth(root->right);return 1 + (leftlen > rightlen ? leftlen : rightlen);
}//二叉树查找值为x的节点
btnode* binarytreefind(btnode* root, btdatatype x)
{if (root == NULL){return NULL;}if (root->data == x){return root;}//如果在左子树中找到了,就不用在右子树中找了btnode* leftfind = binarytreefind(root->left, x);if (leftfind){return leftfind;}//说明左子树中没有找到//再到右子树中继续找btnode* rightfind = binarytreefind(root->right, x);if (rightfind){return rightfind;}//说明右子树中也没有找到return NULL;
}//二叉树的销毁
//二叉树的销毁:要销毁二叉树,肯定是要先遍历二叉树,那么我们应该如何遍历二叉树呢?
//可以参考已有的遍历方式:前序?中序?后序?
//前序和中序在遍历完子树前都会先遍历完根,先销毁根的话,就没办法再找到根的子树了,所以这两种遍历方式都不可取,我们只能后序遍历来销毁二叉树
void  binarytreedestroy(btnode** proot)
{if (*proot == NULL){return;}binarytreedestroy(&((*proot)->left));binarytreedestroy(&((*proot)->right));free(*proot);*proot = NULL;
}
//
//层序遍历
void levelorder(btnode* root)
{if (root == NULL){return;}//先创建一个队列Queue q;QueueInit(&q);//先让队头元素入队列QueuePush(&q, root);//不断循环直到队列为空while (!QueueEmpty(&q)){//先取队头元素btnode* top = QueueFront(&q);//出队QueuePop(&q);//访问队头元素printf("%c ", top->data);//如果左孩子不为空,左孩子入队if (top->left){QueuePush(&q, top->left);}//如果右孩子不为空,右孩子入队if (top->right){QueuePush(&q, top->right);}}QueueDesTroy(&q);
}//判断是否为完全二叉树
bool binarytreecomplete(btnode* root)
{if (root == NULL){return true;}//利用层序遍历的思想判断是否为完全二叉树//先创建一个队列Queue q;QueueInit(&q);//先让队头元素入队列QueuePush(&q, root);//不断循环直到队列为空while (!QueueEmpty(&q)){//先取队头元素btnode* top = QueueFront(&q);//出队QueuePop(&q);//如果队头元素为空,就直接跳出循环if (top == NULL){break;}//走到这里,说明队列不为空,那么就让队头元素的左右孩子都入队列QueuePush(&q, top->left);QueuePush(&q, top->right);}//现在就只需要判断第一次得到空的队头元素后,剩下的元素是否是既有空节点又有非空节点(非完全二叉树)//如果剩下的元素只有空节点(完全二叉树)while (!QueueEmpty(&q)){//取队头元素btnode* top = QueueFront(&q);QueuePop(&q);if (top != NULL){QueueDesTroy(&q);return false;}}QueueDesTroy(&q);return true;
}
//test.c
#define  _CRT_SECURE_NO_WARNINGS 1#include"binarytree.h"btnode* creattree()
{btnode* A = buynode('A');btnode* B = buynode('B');btnode* C = buynode('C');btnode* D = buynode('D');btnode* E = buynode('E');btnode* F = buynode('F');A->left = B;A->right = C;B->left = D;C->left = E;C->right = F;return A;
}void test1()
{btnode* root=creattree();preorder(root);
}void test2()
{btnode* root = creattree();inorder(root);
}void test3()
{btnode* root = creattree();postorder(root);
}void test4()
{btnode* root = creattree();int size = 0;binarytreesize_1(root, size);printf("树中的节点个数:%d\n", size);
}void test5()
{btnode* root = creattree();int size = 0;binarytreesize_2(root, &size);printf("树中的节点个数:%d\n", size);binarytreesize_2(root, &size);printf("树中的节点个数:%d\n", size);binarytreesize_2(root, &size);printf("树中的节点个数:%d\n", size);
}void test6()
{btnode* root = creattree();int size = binarytreesize(root);printf("树中的节点个数:%d\n", size);size = binarytreesize(root);printf("树中的节点个数:%d\n", size);size = binarytreesize(root);printf("树中的节点个数:%d\n", size);
}void test7()
{btnode* root = creattree();int leafsize = binarytreeleafsize(root);printf("叶子结点的个数:%d\n", leafsize);
}
void test8()
{btnode* root = creattree();int levelksize = binarytreelevelksize(root,3);printf("第三层共有%d个节点\n", levelksize);
}void test9()
{btnode* root = creattree();int depth= binarytreedepth(root);printf("树的深度:%d\n", depth);
}void test10()
{btnode* root = creattree();btnode* find = binarytreefind(root, 'C');if (find){printf("yes:%c\n", find->data);}else{printf("no\n");}
}void test11()
{btnode* root = creattree();levelorder(root);
}void test12()
{btnode* root = creattree();if (binarytreecomplete(root)){printf("是完全二叉树\n");}else{printf("不是完全二叉树\n");}
}
int main()
{//test1();//test2();//test3();//test4();//	test5();//test6();//test7();//test8();//test9();//test10();//test11();test12();return 0;
}
//queue.c
#define  _CRT_SECURE_NO_WARNINGS 1#include"queue.h"//初始化
void QueueInit(Queue* pq)
{assert(pq);pq->phead = pq->ptail = NULL;pq->size = 0;
}//销毁
void QueueDesTroy(Queue* pq)
{QueueNode* pcur = pq->phead;while (pcur){QueueNode* pnext = pcur->next;free(pcur);pcur = pnext;}pq->phead = pq->ptail = NULL;pq->size = 0;
}//入队列
//入队列是在队尾入的,所以入队列相当于链表的尾插
void QueuePush(Queue* pq, Qdatatype x)
{assert(pq);//申请新的节点空间QueueNode* newnode = (QueueNode*)malloc(sizeof(QueueNode));newnode->next = NULL;newnode->data = x;//尾插//如果此时队列中一个元素都没有if (pq->phead == NULL){pq->phead = pq->ptail = newnode;}else//队列本来就有元素{pq->ptail->next = newnode;pq->ptail = newnode;}(pq->size)++;
}//判空
bool QueueEmpty(Queue* pq)
{assert(pq);return pq->size == 0;
}//出队列
//出队列是在队头出的,相当于链表的头删
void QueuePop(Queue* pq)
{assert(pq);assert(!QueueEmpty(pq));//如果链表中只有一个元素if (pq->phead->next == NULL){free(pq->phead);pq->phead = pq->ptail = NULL;}else//直接头删{QueueNode* newhead = pq->phead->next;free(pq->phead);pq->phead = newhead;}(pq->size)--;
}//取队头数据
Qdatatype QueueFront(Queue* pq)
{assert(pq);assert(!QueueEmpty(pq));return pq->phead->data;
}//取队尾数据
Qdatatype QueueBack(Queue* pq)
{assert(pq);assert(!QueueEmpty(pq));return pq->ptail->data;
}//队列有效元素个数
int QueueSize(Queue* pq)
{assert(pq);return pq->size;
}
//queue.h
#pragma once#include<stdio.h>
#include<stdlib.h>
#include<stdbool.h>
#include<assert.h>//队列的结构
//先定义队列中节点的结构——队列的底层是链表
typedef struct btnode* Qdatatype;typedef struct QueueNode
{Qdatatype data;struct QueueNode* next;
}QueueNode;//队列的结构定义:
typedef struct Queue
{QueueNode* phead;//队头QueueNode* ptail;//队尾int size;//队列中有效数据个数
}Queue;//初始化
void QueueInit(Queue* pq);
//销毁
void QueueDesTroy(Queue* pq);//入队列
void QueuePush(Queue* pq, Qdatatype x);
//出队列
void QueuePop(Queue* pq);
//取队头数据
Qdatatype QueueFront(Queue* pq);
//取队尾数据
Qdatatype QueueBack(Queue* pq);
//判空
bool QueueEmpty(Queue* pq);
//队列有效元素个数
int QueueSize(Queue* pq);

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

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

相关文章

【Spring Boot把日志记录到文件里面】

<?xml version"1.0" encoding"UTF-8"?> <configuration><!-- 日志输出格式 --><property name"LOG_PATTERN" value"%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n" /><!-- 日志…

大数据服务完全分布式部署- 其他组件(阿里云版)

ZooKeeper 安装 官网 解压 cd /export/server/ tar -zxvf /export/server/apache-zookeeper-3.9.3-bin.tar.gz -C /export/server/软链接 ln -s /export/server/apache-zookeeper-3.9.3-bin /export/server/zookeeper配置 cd /export/server/zookeeper/ mkdir zkDatamyid…

Windows 平板/电脑 上使用 DHCPSRV 搭建 DHCP 服务器

一、DHCPSRV 核心优势 轻量便携:单文件绿色软件,无需安装 全图形界面:比命令行工具更友好 支持IPv4/IPv6:满足现代网络需求 低资源占用:适合平板电脑运行(内存<10MB) 租约管理:可查看实时IP分配情况 二、超详细配置流程 1. 下载与初始化 官网下载:http://www…

ArcGIS动态表格批量出图

前言&#xff1a;产品介绍&#xff1a;ArcGIS动态表格扩展模块Mapping and Charting Solutions&#xff0c;可用于插入动态表格&#xff0c;与数据驱动结合&#xff0c;出图效率无敌。注&#xff1a;优先选择arcgis10.2.2。 一、首先是根据自身携带的arcgis数据进行下载对应的…

Linux小白加油站,第三周周考

1.如何查看当前系统中所有磁盘设备及其分区结构(如磁盘名称、大小、挂载点等)? lsblk # 显示磁盘名称、大小、挂载点&#xff08;P21&#xff09;2.若需对空闲磁盘(如/dev/sdb)进行交互式划分&#xff0c;如何进入操作界面并创建一个5GB的主分区(类型为Linux默认文件系统)? …

SEO的红利没了,下一个风口叫GEO

一、 搜索在退场&#xff0c;答案在上台过去二十多年&#xff0c;我们习惯了这样的路径&#xff1a;输入关键词 → 点开一堆蓝色链接 → 慢慢筛出想要的信息。SEO&#xff08;搜索引擎优化&#xff09;就是围绕这套游戏规则展开的&#xff0c;谁玩得溜&#xff0c;谁就有流量、…

Kubernetes 的 YAML 配置文件-apiVersion

Kubernetes的YAML配置文件–apiVersion 关于 Kubernetes 的 apiVersion 说明 以及 生产环境中推荐使用的版本 的完整指南,帮助你正确、安全地编写 Kubernetes 配置文件。 一、什么是 apiVersion? 在 Kubernetes 的 YAML 配置文件中,apiVersion 字段用于指定你所使用的 Kub…

uniapp 5+App项目,在android studio模拟器上运行调试

1.安装android studio&#xff0c;默认安装即可 点击下载android studio 2.安装完成后&#xff0c;添加设备 选择机型并下载 启动模拟机&#xff08;启动比较慢&#xff0c;稍等一会即可&#xff09; 3.等待模拟器启动后&#xff0c;在uniapp上运行项目到模拟器 如果下…

Qt猜数字游戏项目开发教程 - 从零开始构建趣味小游戏

Qt猜数字游戏项目开发教程 - 从零开始构建趣味小游戏 项目概述 本项目是一个基于Qt框架开发的猜数字游戏&#xff0c;具有现代化的UI设计和完整的游戏逻辑。项目采用C语言开发&#xff0c;使用Qt的信号槽机制实现界面交互&#xff0c;通过随机数生成和状态管理实现完整的游戏…

初识CNN05——经典网络认识2

系列文章目录 初识CNN01——认识CNN 初识CNN02——认识CNN2 初识CNN03——预训练与迁移学习 初识CNN04——经典网络认识 文章目录系列文章目录一、GoogleNet——Inception1.1 1x1卷积1.2 维度升降1.3 网络结构1.4 Inception Module1.5 辅助分类器二、ResNet——越深越好2.1 梯…

学习笔记分享——基于STM32的平衡车项目

学习笔记分享——基于STM32的平衡车项目前言笔记正文结语前言 本文是我在学习铁头山羊的平衡车教程的过程中&#xff0c;记录的笔记&#xff0c;里面不但有Up主的讲解&#xff0c;也有我个人的学习心得&#xff0c;还有查阅的资料&#xff0c;由于内容太多&#xff0c;不方便逐…

学习strandsagents的http_request tool

今天我们通过来拆strandsagents官网的一个例子来学习strandsagents的http_request tool https://strandsagents.com/latest/documentation/docs/examples/python/agents_workflows/ 看上去能做实事核查,实际上没那么高大上。 Show me the code https://github.com/strands-…

大模型对齐算法(四): DAPO,VAPO,GMPO,GSPO, CISPO,GFPO

DAPO DAPO 在 GRPO 的基础上做了 4 处关键升级&#xff0c;既保持 GRPO 的“无价值函数 组内归一化”思想&#xff0c;又通过 剪枝、采样、Token 级梯度、长度惩罚 解决长 Chain-of-Thought RL 的四大痛点。 1 剪枝范围解耦&#xff1a;Clip-Higher GRPO&#xff1a;单一对称…

OpenHarmony之「星链Data」—— 分布式数据管理子系统核心架构与实战解密

目录 系统概述 架构设计 核心模块详解 数据库实现与设计原理 关键函数调用流程链 实际案例分析 常见需求与Bug分析 性能监控与调优

基于SpringBoot+Vue的写真馆预约管理系统(邮箱通知、WebSocket及时通讯、协同过滤算法)

&#x1f388;系统亮点&#xff1a;邮箱通知、WebSocket及时通讯、协同过滤算法&#xff1b;一.系统开发工具与环境搭建1.系统设计开发工具前后端分离项目架构&#xff1a;B/S架构 运行环境&#xff1a;win10/win11、jdk17前端&#xff1a; 技术&#xff1a;框架Vue.js&#xf…

linux下timerfd和posix timer为什么存在较大的抖动?

在linux中开发引用&#xff0c;timerfd和posix timer是最常用的定时器。timerfd是linux特有的定时器&#xff0c;通过fd来实现定时器&#xff0c;体现了linux"一切皆文件"的思想&#xff1b;posix timer&#xff0c;只要符合posix标准的操作系统&#xff0c;均应支持…

网络聚合链路与软件网桥配置指南

网络聚合链路与软件网桥配置指南一、聚合链路&#xff08;Team&#xff09; 网络组队&#xff08;聚合链路&#xff09;是一种将多个网络接口控制器&#xff08;NIC&#xff0c;Network Interface Controller&#xff09;以逻辑方式组合在一起的技术&#xff0c;通过这种方式可…

IDE/去读懂STM32CubeMX 时钟配置图(有源/无源晶振、旁路/晶振模式、倍频/分频)

文章目录概述配置图元素说明RCCHSI/LSI/HSE/LSEAHB 和 APBSYSCLK 和 HCLKMux 多路复用器Prescaler 预分频器PLL 锁相环PLL 配置寄存器时钟物理源内部时钟和驱动无源晶振和驱动有源晶振和驱动MCO 时钟信号音频时钟配置晶体振荡器&#xff1f;外部时钟源类型RCC 如何选择旁路模式…

8 文本分析

全文检索与常规关系型数据库SQL查询的显著区别&#xff0c;就是全文检索具备对大段文本进行分析的能力&#xff0c;它可以通过文本分析把大段的文本切分为细粒度的分词。 elasticsearch在两种情况下会用到文本分析&#xff1a; 原始数据写入索引时&#xff0c;如果索引的某个字…

告别 Count Distinct 慢查询:StarRocks 高效去重全攻略

在大数据分析中&#xff0c;去重计算&#xff08;如 Count Distinct&#xff09;是一个常见但计算开销极高的操作&#xff0c;尤其在高基数和高并发场景下&#xff0c;常常成为查询性能的瓶颈。以用户访问行为为例&#xff0c;同一用户一天内多次访问页面时&#xff0c;PV 会累…