二叉树和堆

树概念及结构(了解)

树的概念(看看就行)
树是一种 非线性 的数据结构,它是由 n n>=0 )个有限结点组成一个具有层次关系的集合。 把它叫做树是因 为它看起来像一棵倒挂的树,也就是说它是根朝上,而叶朝下的 。 有一个特殊的结点,称为根结点 ,根节点没有前驱结点。除根节点外,其余结点被分成 M(M>0) 个互不相交的集合 T1 T2 …… Tm ,其中每一个集合 Ti(1<= i <= m)又是一棵结构与树类似的子树。每棵子树的根结点有且只有一个前驱,可以有 0 个或多个后继 因此,树是递归定义 的。
注意,二叉树习惯把问题切分为子问题
注意:树形结构中,子树之间不能有交集,否则就不是树形结构
树的相关概念(了解)
节点的度 :一个节点含有的子树的个数称为该节点的度; 如上图: A 的为 6
叶节点或终端节点 :度为 0 的节点称为叶节点; 如上图: B C H I... 等节点为叶节点
非终端节点或分支节点 :度不为 0 的节点; 如上图: D E F G... 等节点为分支节点
双亲节点或父节点 :若一个节点含有子节点,则这个节点称为其子节点的父节点; 如上图: A B 的父节点
孩子节点或子节点 :一个节点含有的子树的根节点称为该节点的子节点; 如上图: B A 的孩子节点
兄弟节点 :具有相同父节点的节点互称为兄弟节点; 如上图: B C 是兄弟节点
树的度 :一棵树中,最大的节点的度称为树的度; 如上图:树的度为 6
节点的层次 :从根开始定义起,根为第 1 层,根的子节点为第 2 层,以此类推;
树的高度或深度 :树中节点的最大层次; 如上图:树的高度为 4
堂兄弟节点 :双亲在同一层的节点互为堂兄弟;如上图: H I 互为兄弟节点
节点的祖先 :从根到该节点所经分支上的所有节点;如上图: A 是所有节点的祖先
子孙 :以某节点为根的子树中任一节点都称为该节点的子孙。如上图:所有节点都是 A 的子孙
森林 :由 m m>0 )棵互不相交的树的集合称为森林;

树的表示(了解)

树结构相对线性表就比较复杂了,要存储表示起来就比较麻烦了, 既然保存值域,也要保存结点和结点之间 的关系 ,实际中树有很多种表示方式如:双亲表示法,孩子表示法、孩子双亲表示法以及孩子兄弟表示法等。我们这里就简单的了解其中最常用的孩子兄弟表示法
typedef int DataType;
struct Node
{struct Node* _firstChild1; // 第一个孩子结点struct Node* _pNextBrother; // 指向其下一个兄弟结点DataType _data; // 结点中的数据域
};

树在实际中的运用(表示文件系统的目录树结构)

二叉树概念及结构

概念
一棵二叉树是结点的一个有限集合,该集合 :
1. 或者为空
2. 由一个根节点加上两棵别称为左子树和右子树的二叉树组成
从上图可以看出:
1. 二叉树不存在度大于2的结点
2. 二叉树的子树有左右之分,次序不能颠倒,因此二叉树是有序树
注意:对于任意的二叉树都是由以下几种情况复合而成的:
现实中的二叉树:
特殊的二叉树:(满二叉树是一种特殊的完全二叉树)
高度为h的满二茶树最多有多少节点
高度是h的完全二叉树节点范围
二叉树的性质(小题会用到)
n0=n2+1
根据这个式子和完全二叉树的特点(度为1的结点只有1个或0个)度意思是孩子个数

二叉树的存储结构

二叉树一般可以使用两种结构存储,一种顺序结构,一种链式结构。
1. 顺序存储
完全二叉树适合顺序存储,可以看成数组,后边的堆就是顺序存储,左孩子下标=父亲下标*2+1,右孩子下标等于父亲下标*2+2
父亲下标=(孩子下标-1)/2-》以上只适用于完全二叉树,非完全二叉树适合链式存储
2. 链式存储
链式存储适合不连续的存储

堆的概念及结构(是数据结构,不是内存划分的那个堆)

堆是非线性结构,完全二叉树

大堆:树中任意一个父亲都>=孩子      小堆:树中任意一个父亲都<=孩子    

底层物理结构是数组,逻辑结构是完全二叉树

注意:小堆不代表底层数组是升序的,仅仅是任意父亲与孩子之间满足关系而已

所以小堆的根就是整个堆的最小值

这道题就是将给的值进行顺序排列,从第一层往下写,最后观察是否满足堆的规则,可以看到这个是大堆,满足任意父亲大于=孩子

这里有几道选择题可以做做,答案全是c,因为很简单,就不赘述了

堆的实现

堆向下调整算法(logN)

void AdjustDown(HPDataType* a, int n, int parent)//a是数组首元素地址,n是元素个数,parent是父亲下标
{int child = parent * 2 + 1;//求出左孩子下标while (child < n)(孩子下标超过数组元素个数,说明已经越界,跳出循环){if (child + 1 < n && a[child + 1] <a[child])//首先判断是否有右孩子,并且找出小的那个孩子{++child;}if (a[child] < a[parent]){Swap(&a[child], &a[parent]);// 继续往下调整parent = child;child = parent * 2 + 1;}else{break;//不需要再调整}}
}

假设是小堆调整

基本思想:向下调整算法前提下边是小堆或者大堆,如果是小堆,把(左右孩子)小的往上调,大的往下调;如果是大堆,把(左右孩子)大的往上调,小的往下调-》基本思路就是大/小的往下沉,小/大的往上调

堆的向上调整算法(假设是小堆)(logN)

void AdjustUp(HPDataType* a, int child)
{int parent = (child - 1) / 2;//父亲下标while (child > 0)//循环结束条件孩子下标=0{if (a[child] < a[parent]){Swap(&a[child], &a[parent]);child = parent;parent = (parent - 1) / 2;}else//满足小堆的特点,父亲小于孩子,就跳出{break;}}
}

向上调整算法的思路也很简单,以小堆为例,孩子小于父亲就交换,可以看出每轮交换结束任意一个非叶子节点和其两个孩子都满足父亲小于孩子。

总结:两种调整算法都需满足堆的特性,向上调整需满足前面的数据是堆,向下调整需满足下面的数据是堆,整体思路就是让每个节点都满足父亲大于等于孩子(大堆),父亲小于等于孩子(小堆)

上述具体代码处有解释,包能学会代码

堆的创建

从倒数第一个非叶子结点开始调(也就是最后一个叶子节点的父亲)

最后一个叶子结点下标是n-1,其父亲就是((n-1)-1)/2,调整到i=0位置结束

为什么可以这样调整呢,假设期望调整为大堆,每一轮循环调整都是将三个数中最大的调整为父亲,两个儿子都比当前节点小,从倒数第一个父亲开始到第一个父亲,每次调整都满足这个关系-》父亲>=两个儿子,父亲小就往下调,儿子大的就往上调。最后成为一个名副其实的大堆

可以将三个数据看成一个整体更好理解(父节点+两个孩子节点)基本思想就是父亲恒大于或者小于孩子

           调整前                                             调整后

向上调整建堆

从第二个数据开始向上调整,每次都可以把前面的数据看成堆,调整到最后一个数据,下标是i=n-1结束

建堆时间复杂度(记住结论即可)

堆的插入

void HeapPush(HP* php, HPDataType x)
{assert(php);// 扩容if (php->size == php->capacity){int newCapacity = php->capacity == 0 ? 4 : php->capacity * 2;HPDataType* tmp = (HPDataType*)realloc(php->a, sizeof(HPDataType) * newCapacity);if (tmp == NULL){perror("realloc fail");exit(-1);}php->a = tmp;php->capacity = newCapacity;}php->a[php->size] = x;php->size++;AdjustUp(php->a, php->size - 1);
}

简单的尾插,空间不够就扩容,然后向上调整(每次都从当前最后一个数据向上调整),可以保证每次插入前都是一个堆结构

堆的删除

void HeapPop(HP* php)
{assert(php);assert(php->size > 0);Swap(&php->a[0], &php->a[php->size - 1]);--php->size;AdjustDown(php->a, php->size, 0);
}

先交换第一个和最后一个数据,再删除最后一个数据,再从第一个元素位置向下调整,接着重复这个过程

堆的应用

堆排序(时间复杂福是N*logN)

堆排序即利用堆的思想来进行排序,总共分为两个步骤:
1. 建堆
升序:建大堆
降序:建小堆
2. 利用堆删除思想来进行排序
建堆和堆删除中都用到了向下调整,因此掌握了向下调整,就可以完成堆排序。
 //升序
void HeapSort(int* a, int n)
{// 建堆 (大堆)for (int i = 1; i < n; i++){AdjustUp(a, i);}int end = n - 1;while (end > 0){Swap(&a[0], &a[end]);AdjustDown(a, end, 0);--end;}
}

堆排序思路很简单,就是先建堆(在原数组上建堆就可以),升序建大堆,大堆第一个数据总是最大的,然后将第一个数据和最后一个数据交换,end--,将前面的数据看成一个数组,再次选出最大的和最后一个交换,再end--,循环往复这个过程,就能得到升序数组了

测试排序数组,先测降序

升序

topk问题

void PrintTopK(const char* filename, int k)
{// 1. 建堆--用a中前k个元素建堆FILE* fout = fopen(filename, "r");if (fout == NULL){perror("fopen fail");return;}int* minheap = (int*)malloc(sizeof(int) * k);if (minheap == NULL){perror("malloc fail");return;}for (int i = 0; i < k; i++){fscanf(fout, "%d", &minheap[i]);}// 前k个数建小堆for (int i = (k - 2) / 2; i >= 0; --i){AdjustDown(minheap, k, i);}// 2. 将剩余n-k个元素依次与堆顶元素交换,不满则则替换int x = 0;while (fscanf(fout, "%d", &x) != EOF){if (x > minheap[0]){// 替换你进堆minheap[0] = x;AdjustDown(minheap, k, 0);}}for (int i = 0; i < k; i++){printf("%d ", minheap[i]);}printf("\n");free(minheap);fclose(fout);
}// fprintf  fscanfvoid CreateNDate()
{// 造数据int n = 10000000;srand(time(0));const char* file = "data.txt";FILE* fin = fopen(file, "w");if (fin == NULL){perror("fopen error");return;}for (int i = 0; i < n; ++i){int x = (rand() + i) % 10000;//表示数据最大值也小于10000fprintf(fin, "%d\n", x);}fclose(fin);
}int main()
{CreateNDate();//PrintTopK("data.txt", 10);return 0;
}

TopK问题思路也很简单,就是先造n个数据并写入文件-》将前k个数建立一个小堆-》将剩余n-k个元素和堆中第一个数据比较,大于就交换,然后从第一个数据向下调整,直到数据流最后一个数据被读取结束,堆中存放的是我们造的数据前100大元素,堆顶是第100大元素

如图是前10大数据

堆实现完整代码

Heap.h

#pragma once
#include<stdio.h>
#include<stdlib.h>
#include<assert.h>
#include<stdbool.h>
#include<string.h>
#include<time.h>typedef int HPDataType;
typedef struct Heap
{HPDataType* a;int size;int capacity;
}HP;void AdjustUp(HPDataType* a, int child);
void AdjustDown(HPDataType* a, int n, int parent);void Swap(HPDataType* p1, HPDataType* p2);
void HeapPrint(HP* php);
void HeapInit(HP* php);void HeapDestroy(HP* php);//堆的销毁
void HeapPush(HP* php, HPDataType x);
void HeapPop(HP* php);
HPDataType HeapTop(HP* php);//堆顶元素
bool HeapEmpty(HP* php);//堆的判空

Heap.c

#include"Heap.h"void HeapInit(HP* php)
{assert(php);php->a = NULL;php->size = 0;php->capacity = 0;
}void HeapDestroy(HP* php)
{assert(php);free(php->a);php->a = NULL;php->size = php->capacity = 0;
}void Swap(HPDataType* p1, HPDataType* p2)
{HPDataType tmp = *p1;*p1 = *p2;*p2 = tmp;
}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 = (parent - 1) / 2;}else{break;}}
}void AdjustDown(HPDataType* a, int n, int parent)
{int child = parent * 2 + 1;while (child < n){if (child + 1 < n && a[child + 1] <a[child]){++child;}if (a[child] <a[parent]){Swap(&a[child], &a[parent]);// 继续往下调整parent = child;child = parent * 2 + 1;}else{break;}}
}void HeapPush(HP* php, HPDataType x)
{assert(php);// 扩容if (php->size == php->capacity){int newCapacity = php->capacity == 0 ? 4 : php->capacity * 2;HPDataType* tmp = (HPDataType*)realloc(php->a, sizeof(HPDataType) * newCapacity);if (tmp == NULL){perror("realloc fail");exit(-1);}php->a = tmp;php->capacity = newCapacity;}php->a[php->size] = x;php->size++;AdjustUp(php->a, php->size - 1);
}void HeapPrint(HP* php)
{assert(php);for (size_t i = 0; i < php->size; i++){printf("%d ", php->a[i]);}printf("\n");
}void HeapPop(HP* php)
{assert(php);assert(php->size > 0);Swap(&php->a[0], &php->a[php->size - 1]);--php->size;AdjustDown(php->a, php->size, 0);
}HPDataType HeapTop(HP* php)
{assert(php);assert(php->size > 0);return php->a[0];
}bool HeapEmpty(HP* php)
{assert(php);return php->size == 0;
}

Test.c

#define _CRT_SECURE_NO_WARNINGS 1
#include"Heap.h"int main()
{int a[] = { 65,100,70,32,50,60 };HP hp;HeapInit(&hp);//堆初始化for (int i = 0; i < sizeof(a)/sizeof(int); i++){HeapPush(&hp, a[i]);}HeapPrint(&hp);//打印堆int k = 5;//打印前K大数据while (!HeapEmpty(&hp) && k--){printf("%d ", HeapTop(&hp));HeapPop(&hp);}HeapDestroy(&hp);return 0;
}

二叉树链式结构的实现

二叉树的遍历
再看二叉树基本操作前,再回顾下二叉树的概念, 二叉树是:
前序遍历:根 左子树 右子树(局部和宏观都满足这个顺序才行)
中序遍历:左子树  根 右子树
后序遍历:左子树 右子树 根
层序遍历:一层一层往后遍历
记住!!二叉树基本上都是递归实现的,递归看似复杂实际上也就是大问题化小问题,只需在意每个子问题的解法
比如前序遍历代码,确实会一层一层展开递归,但是每一层递归都是先判空,然后打印根,递归左子树,递归右子树
如何理解递归
递归不要太在意递归展开图,要不然很复杂的题目根本没时间想展开图,先找相同子问题,比如后序遍历就是先递归左子树,再递归右子树,再打印根,我们写这一个函数,内部是层层递归实现的,所以我们只关心某一个子问题如何解决,再注意一下递归函数出口(根节点结束),基本上就实现递归的功能了

手动构建二叉树的代码

#include<stdio.h>
#include<stdlib.h>
#include<assert.h>// 不是增删查改,学习二叉树结构
typedef struct BinaryTreeNode
{struct BinaryTreeNode* left;struct BinaryTreeNode* right;int val;
}BTNode;BTNode* BuyNode(int x)
{BTNode* node = (BTNode*)malloc(sizeof(BTNode));if (node == NULL){perror("malloc fail");exit(-1);}node->val = x;node->left = NULL;node->right = NULL;return node;
}void PrevOrder(BTNode* root) {if (root == NULL) {printf("NULL ");return;}printf("%d ", root->val);PrevOrder(root->left);PrevOrder(root->right);
}void InOrder(BTNode* root)
{if (root == NULL){printf("NULL ");return;}InOrder(root->left);printf("%d ", root->val);InOrder(root->right);
}void PostOrder(BTNode* root)
{if (root == NULL){printf("NULL ");return;}PostOrder(root->left);PostOrder(root->right);printf("%d ", root->val);
}// 节点个数
//int TreeSize(BTNode* root)
//{
//	static int size = 0;
//	if (root == NULL)
//		return 0;
//	else
//		++size;
//
//	TreeSize(root->left);
//	TreeSize(root->right);
//
//	return size;
//}//int size = 0;
//int TreeSize(BTNode* root)
//{
//	if (root == NULL)
//		return 0;
//	else
//		++size;
//
//	TreeSize(root->left);
//	TreeSize(root->right);
//
//	return size;
//}int TreeSize(BTNode* root)
{return root == NULL ? 0 : TreeSize(root->left) + TreeSize(root->right) + 1;
}// 叶子节点个数
int TreeLeafSize(BTNode* root)
{if (root == NULL)return 0;if (root->left == NULL && root->right == NULL){return 1;}return TreeLeafSize(root->left) + TreeLeafSize(root->right);
}// 第k层的节点个数
int TreeKLevel(BTNode* 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);
}int main()
{// 手动构建BTNode* node1 = BuyNode(1);BTNode* node2 = BuyNode(2);BTNode* node3 = BuyNode(3);BTNode* node4 = BuyNode(4);BTNode* node5 = BuyNode(5);BTNode* node6 = BuyNode(6);node1->left = node2;node1->right = node4;node2->left = node3;node4->left = node5;node4->right = node6;PrevOrder(node1);printf("\n");InOrder(node1);printf("\n");PostOrder(node1);printf("\n");printf("%d\n", TreeSize(node1));//size = 0;printf("%d\n", TreeSize(node1));return 0;
}

该代码包括前中后序基本代码和求结点个数/叶子结点个数/第k层节点个数

前中后序很简单,我们说一下求总结点和叶子结点

求总结点有三种办法

第一种,结点是空返回0,反之++size-》递归左子树-》递归右子树

每一个栈帧都是执行这个过程,走到空节点返回栈帧,逐层返回,最后一次返回第一个栈帧

size=6,没想明白的可以画一下递归展开图,根据原理很好画的,这里我画图不好看就不画了(画图之后就很好理解了,递归初期要多画图,慢慢理解领悟递归的魅力)

第二次打印size要置空,因为size是全局变量,只有main函数结束才会销毁,不置空会打印12,也可以将size定义成静态全局变量(也是出main函数才会销毁,不可定义成局部静态,因为作用域是局部的,外边访问不到),是一个道理

最简洁写法 是空返回0,非空返回自身+左子树+右子树,没想明白可以画图,递归划分成子问题-》分治思想-》求当前树结点-》自身结点+左子树+右子树,左子树也看成自身结点+左子树+右子树,右子树也是自身结点+左子树+右子树-》加完之后层层返回得到总结点

求叶子结点个数也很简单,就是控制两个出口(节点为空返回0,叶子结点返回1),如果不是空节点和叶子结点,进入左子树递归+右子树递归,最后层层返回也就是叶子结点之和

二叉树系列代码补充

using namespace std;
#include<iostream>
#include<queue>
#include<list>
// 不是增删查改,学习二叉树结构
typedef struct BinaryTreeNode
{struct BinaryTreeNode* left;struct BinaryTreeNode* right;int val;
}BTNode;BTNode* BuyNode(int x)
{BTNode* node = (BTNode*)malloc(sizeof(BTNode));if (node == NULL){perror("malloc fail");exit(-1);}node->val = x;node->left = NULL;node->right = NULL;return node;
}// 二叉树销毁
void TreeDestroy(BTNode* root)
{if (root == NULL){return;}TreeDestroy(root->left);TreeDestroy(root->right);free(root);root = NULL;
}// 二叉树查找值为x的结点
BTNode* TreeFind(BTNode* root, int x)
{if (root == NULL)return NULL;if (root->val == x)return root;BTNode* ret = NULL;ret = TreeFind(root->left, x);if (ret)return ret;ret = TreeFind(root->right, x);if (ret)return ret;return NULL;
}void LevelOrder(BTNode* root)
{queue< BTNode*, list< BTNode*>> q1;if (root)q1.push(root);while (!q1.empty()){BTNode* front = q1.front();cout << front->val<<" ";if (front->left)q1.push(front->left);if (front->right)q1.push(front->right);q1.pop();}cout << endl;}//int TreeHeight(BTNode* root)
//{
//	if (root == NULL)
//		return 0;
//
//	return TreeHeight(root->left) > TreeHeight(root->right)
//		? TreeHeight(root->left) + 1 : TreeHeight(root->right) + 1;
//}正确写法但是不提倡这样写会超时,每次判断高度都会进入递归,应该保存高度//int TreeHeight(BTNode* root)
//{
//	if (root == NULL)
//		return 0;
//
//	int leftHeight = TreeHeight(root->left);
//	int rightHeight = TreeHeight(root->right);//	return leftHeight > rightHeight ? leftHeight + 1 : rightHeight + 1;
//}int TreeHeight(BTNode* root)
{if (root == NULL)return 0;return fmax(TreeHeight(root->left), TreeHeight(root->right)) + 1;//左右子树高度都算出来,将参数传给fmax函数
}int main()
{// 手动构建BTNode* node1 = BuyNode(1);BTNode* node2 = BuyNode(2);BTNode* node3 = BuyNode(3);BTNode* node4 = BuyNode(4);BTNode* node5 = BuyNode(5);BTNode* node6 = BuyNode(6);BTNode* node7 = BuyNode(7);node1->left = node2;node1->right = node4;node2->left = node3;node4->left = node5;node4->right = node6;node2->right = node7;printf("TreeHeight:%d\n", TreeHeight(node1));LevelOrder(node1);//层序遍历TreeDestroy(node1);//树的销毁node1 = NULL;return 0;
}

代码中的二叉树长这样

二叉树销毁很简单,走一下后序遍历,将每个节点free掉就行

二叉树中查找值为x的节点,也很简单,控制结束条件,根为空返回NULL,值是我们要查找的就返回此节点,将大问题划分为子问题-》先判断自己是否为要找的值-》递归左子树-》递归右子树,找到了就逐层返回,左右子树和自身都找不到就返回NULL

求树高度

还是用到了分治思想,空节点返回NULL,非空结点返回1+左右子树高度高的一方就是树的高度了,求出下边的高度返回给上边

最后再说一下层序遍历,思想很简单(这里用c++代码实现会轻松些,queue是一种容器适配器,我在之前的博客讲过这个,不会的可以看看我之前写的),大致思路就是先入根节点-》打印根并且将左右不为空的结点入到队列中-》弹出队头-》循环往复此过程会将结点一层层打印出来

以上就是我对二叉树基础部分的全部理解

二叉树oj题

还有一些二叉树oj题,可以直接在力扣上搜题目,可以练习一下

感谢大家的支持!!!

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

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

相关文章

C语言 指针_野指针 指针运算

野指针&#xff1a; 概念&#xff1a;野指针就是指针指向的位置是不可知的&#xff08;随机的、不正确的、没有明确限制的&#xff09; 指针非法访问&#xff1a; int main() {int* p;//p没有初始化&#xff0c;就意味着没有明确的指向//一个局部变量不初始化&#xff0c;放…

腾讯 Hunyuan3D-2: 高分辨率3D 资产生成

腾讯 Hunyuan3D-2&#xff1a;高分辨率 3D 资产生成的突破 前言 在当今数字化时代&#xff0c;3D 资产生成技术正变得越来越重要。无论是游戏开发、影视制作还是虚拟现实领域&#xff0c;高质量的 3D 模型和纹理都是创造沉浸式体验的关键。然而&#xff0c;传统的 3D 资产制作…

Java如何实现反转义

Java如何实现反转义 前提 最近做的一个需求&#xff0c;是热搜词增加换一批的功能。功能做完自测后&#xff0c;交给了测试伙伴&#xff0c;但是测试第二天后就提了一个bug&#xff0c;出现了未知词 levis。第一眼看着像公司售卖的一个品牌-李维斯。然后再扒前人写的代码&…

Java 高级工程师面试高频题:JVM+Redis+ 并发 + 算法 + 框架

前言 在过 2 个月即将进入 3 月了&#xff0c;然而面对今年的大环境而言&#xff0c;跳槽成功的难度比往年高了很多&#xff0c;很明显的感受就是&#xff1a;对于今年的 java 开发朋友跳槽面试&#xff0c;无论一面还是二面&#xff0c;都开始考验一个 Java 程序员的技术功底…

后端:MyBatis

文章目录 1. MyBatis1-1. Mybatis 工具类的封装1-2. Mybatis 通过集合或实体类传递参数-实现插入数据(增)1-3. MyBatis 实现删除数据(删)1-4. MyBatis 实现修改数据(改)1-5. MyBatis 实现查询数据(查) 2. MyBatis 配置文件中的一些标签和属性2-1.environments标签2-2. dataSour…

安卓14自由窗口圆角处理之绘制圆角轮廓线

背景&#xff1a; 前面文章已经分享过&#xff1a; 如何一行代码搞定自由窗口的圆角处理&#xff1f;-wms/自由窗口/sf实战开发 但是又有学员朋友提出另一个blog的成果&#xff1a; 安卓aosp14上自由窗口划线边框Freeform Caption实战开发-千里马framework实战 想要把划线和…

【Unity3D】3D物体摆放、场景优化案例Demo

目录 PlaceManager.cs(放置管理类) Ground.cs(地板类) 和 GroundData.cs(地板数据类) 额外知识点说明 1、MeshFilter和MeshRenderer的Bounds区别 2、Gizmos 绘制一个平行于斜面的立方体 通过网盘分享的文件&#xff1a;PlaceGameDemo2.unitypackage 链接: https://pan.baid…

高效沟通驱动LabVIEW项目成功

在LabVIEW项目开发中&#xff0c;由于涉及软件、硬件及多方协作&#xff0c;项目沟通效率的高低直接影响开发进度与最终质量。不明确的需求、信息传递中的误解以及跨部门协作的阻碍&#xff0c;常导致项目延误甚至失败。因此&#xff0c;建立高效的沟通机制&#xff0c;确保信息…

信息收集(下)

一.端口信息收集 1.端口基础认知 A.端口简介 在 Internet 环境中&#xff0c;各主机依据 TCP/IP 协议实现数据包的发送与接收。数据包凭借目的主机的 IP 地址&#xff0c;在互联网络里完成路由选择&#xff0c;进而精准地传至目标主机。然而&#xff0c;当目的主机同时运行多…

springBoot 整合ModBus TCP

ModBus是什么&#xff1a; ModBus是一种串行通信协议&#xff0c;主要用于从仪器和控制设备传输信号到主控制器或数据采集系统&#xff0c;例如用于测量温度和湿度并将结果传输到计算机的系统。&#xff08;百度答案&#xff09; ModBus 有些什么东西&#xff1a; ModBus其分…

composer安装指定php版本, 忽略平台原因导致的报错

windows下 //composer安装指定php版本, 写出完整的php和composer.phar路径 D:\phpstudy_pro\Extensions\php\php8.1.11nts\php.exe D:\phpstudy_pro\Extensions\composer1.8.5\composer.phar install windows下一些扩展不支持, 如下图, 所以本地composer安装组件时可以忽略 …

【论文投稿】Python 网络爬虫:探秘网页数据抓取的奇妙世界

目录 前言 一、Python—— 网络爬虫的绝佳拍档 二、网络爬虫基础&#xff1a;揭开神秘面纱 &#xff08;一&#xff09;工作原理&#xff1a;步步为营的数据狩猎 &#xff08;二&#xff09;分类&#xff1a;各显神通的爬虫家族 三、Python 网络爬虫核心库深度剖析 &…

大模型应用与部署 技术方案

大模型应用与部署 技术方案 一、引言 人工智能蓬勃发展,Qwen 大模型在自然语言处理领域地位关键,其架构优势尽显,能处理文本创作等多类复杂任务,提供优质交互。Milvus 向量数据库则是向量数据存储检索利器,有高效索引算法(如 IVF_FLAT、HNSWLIB 等)助力大规模数据集相似…

Postman接口测试工具详解

(创作不易&#xff0c;感谢有你&#xff0c;你的支持&#xff0c;就是我前行的最大动力&#xff0c;如果看完对你有帮助&#xff0c;还请三连支持一波哇ヾ(&#xff20;^∇^&#xff20;)ノ&#xff09; 目录 引言 Postman简介 Postman的特点 Postman的下载与安装 Postman…

电路研究9.2——合宙Air780EP使用AT指令

这里正式研究AT指令的学习了&#xff0c;之前只是接触的AT指令&#xff0c;这里则是深入分析AT指令了。 软件的开发方式&#xff1a; AT&#xff1a;MCU 做主控&#xff0c;MCU 发 AT 命令给模组的开发方式&#xff0c;模组仅提供标准的 AT 固件&#xff0c; 所有的业务控制逻辑…

百度APP iOS端磁盘优化实践(上)

01 概览 在APP的开发中&#xff0c;磁盘管理已成为不可忽视的部分。随着功能的复杂化和数据量的快速增长&#xff0c;如何高效管理磁盘空间直接关系到用户体验和APP性能。本文将结合磁盘管理的实践经验&#xff0c;详细介绍iOS沙盒环境下的文件存储规范&#xff0c;探讨业务缓…

Sharding-JDBC 5.4.1+SpringBoot3.4.1+MySQL8.4.1 使用案例

最近在升级 SpringBoot 项目&#xff0c;原版本是 2.7.16&#xff0c;要升级到 3.4.0 &#xff0c;JDK 版本要从 JDK8 升级 JDK21&#xff0c;原项目中使用了 Sharding-JDBC&#xff0c;版本 4.0.0-RC1&#xff0c;在升级 SpringBoot 版本到 3.4.0 之后&#xff0c;服务启动失败…

【Django】多个APP设置独立的URL

目录 方法一&#xff1a;各个App下设置自己的URL 1、在各自的App当中创建urls.py文件​编辑 2、在主urls当中包含子url 3、各App的urls中设置url 4、设置后台函数 5、最终结果 总结&#xff1a; 方法二&#xff1a;利用as方法&#xff0c;在总的URL中对views重命名 实…

Vue2 项目二次封装Axios

引言 在现代前端开发中&#xff0c;HTTP请求管理是构建健壮应用的核心能力之一。Axios作为目前最流行的HTTP客户端库&#xff0c;其灵活性和可扩展性为开发者提供了强大的基础能力。 1. 为什么要二次封装Axios&#xff1f; 1.1 统一项目管理需求 API路径标准化&#xff1a;…

【算法】经典博弈论问题——巴什博弈 python

目录 前言巴什博弈(Bash Game)小试牛刀PN分析实战检验总结 前言 博弈类问题大致分为&#xff1a; 公平组合游戏、非公平组合游戏&#xff08;绝大多数的棋类游戏&#xff09;和 反常游戏 巴什博弈(Bash Game) 一共有n颗石子&#xff0c;两个人轮流拿&#xff0c;每次可以拿1~m颗…