手撕⼆叉树——堆

news2024/11/26 12:51:38

1.

1.1 树的概念与结构

树是⼀种⾮线性的数据结构,它是由 n n>=0 ) 个有限结点组成⼀个具有层次关系的集合。把它
叫做 树是因为它看起来像⼀棵倒挂的树,也就是说它是根朝上,⽽叶朝下的。
有⼀个特殊的结点,称为根结点,根结点没有前驱结点。
除根结点外,其余结点被分成 M(M>0) 个互不相交的集合 T1 T2 …… Tm ,其中每⼀个集合
  Ti(1 <= i <= m) ⼜是⼀棵结构与树类似的⼦树。每棵⼦树的根结点有且只有⼀个前驱,可以有 0
个或多个后继。因此,树是递归定义的。
树形结构中,⼦树之间不能有交集,否则就不是树形结构
⾮树形结构:
⼦树是不相交的(如果存在相交就是图了,图以后得课程会有讲解)
除了根结点外,每个结点有且仅有⼀个⽗结点
⼀棵N个结点的树有N-1条边

1.2 树相关术语

⽗结点/双亲结点 :若⼀个结点含有⼦结点,则这个结点称为其⼦结点的⽗结点; 如上图:A是B                                  的⽗结点
⼦结点/孩⼦结点 :⼀个结点含有的⼦树的根结点称为该结点的⼦结点; 如上图:B是A的孩⼦结点
结点的度 :⼀个结点有⼏个孩⼦,他的度就是多少;⽐如A的度为6,F的度为2,K的度为0
树的度:⼀棵树中,最⼤的结点的度称为树的度; 如上图:树的度为 6
叶⼦结点/终端结点:度为 0 的结点称为叶结点; 如上图: B C H I... 等结点为叶结点
分⽀结点/⾮终端结点:度不为 0 的结点; 如上图: D E F G... 等结点为分⽀结点
兄弟结点:具有相同⽗结点的结点互称为兄弟结点(亲兄弟); 如上图: B C 是兄弟结点
结点的层次:从根开始定义起,根为第 1 层,根的⼦结点为第 2 层,以此类推;
树的⾼度或深度:树中结点的最⼤层次; 如上图:树的⾼度为 4
结点的祖先:从根到该结点所经分⽀上的所有结点;如上图: A 是所有结点的祖先
路径:⼀条从树中任意节点出发,沿⽗节点-⼦节点连接,达到任意节点的序列;⽐如A到Q的路径             为: A-E-J-Q;H到Q的路径H-D-A-E-J-Q
⼦孙:以某结点为根的⼦树中任⼀结点都称为该结点的⼦孙。如上图:所有结点都是A的⼦孙
森林:由 m m>0 ) 棵互不相交的树的集合称为森林;

1.3 树的表⽰

孩⼦兄弟表⽰法:
树结构相对线性表就⽐较复杂了,要存储表⽰起来就⽐较⿇烦了,既然保存值域,也要保存结点和
结 点之间的关系,实际中树有很多种表⽰⽅式如:双亲表⽰法,孩⼦表⽰法、孩⼦双亲表⽰法以
及孩⼦ 兄弟表⽰法等。我们这⾥就简单的了解其中最常⽤的孩⼦兄弟表⽰法
struct TreeNode
{
    struct Node* child; // 左边开始的第⼀个孩⼦结点
    struct Node* brother; // 指向其右边的下⼀个兄弟结点
    int data; // 结点中的数据域
};

1.4 树形结构实际运⽤场景

⽂件系统是计算机存储和管理⽂件的⼀种⽅式,它利⽤树形结构来组织和管理⽂件和⽂件夹。在⽂
件 系统中,树结构被⼴泛应⽤,它通过⽗结点和⼦结点之间的关系来表⽰不同层级的⽂件和⽂件
夹之间 的关联。

2. ⼆叉树

2.1 概念与结构

在树形结构中,我们最常⽤的就是⼆叉树,⼀棵⼆叉树是结点的⼀个有限集合,该集合由⼀个根结
点加上两棵别称为左⼦树和右⼦树的⼆叉树组成或者为空

从上图可以看出⼆叉树具备以下特点:
1. ⼆叉树不存在度⼤于 2 的结点
2. ⼆叉树的⼦树有左右之分,次序不能颠倒,因此⼆叉树是有序树
注意:对于任意的⼆叉树都是由以下⼏种情况复合⽽成的
现实中的⼆叉树

2.2 特殊的⼆叉树

2.2.1 满⼆叉树
⼀个⼆叉树,如果每⼀个层的结点数都达到最⼤值,则这个⼆叉树就是满⼆叉树。也就是说,如果
⼀ 个⼆叉树的层数为 K ,且结点总数是 2 k − 1 ,则它就是满⼆叉树。
2.2.2 完全⼆叉树
完全⼆叉树是效率很⾼的数据结构,完全⼆叉树是由满⼆叉树⽽引出来的。对于深度为 K 的,有 n
个结点的⼆叉树,当且仅当其每⼀个结点都与深度为K的满⼆叉树中编号从 1 n 的结点⼀⼀对
应时称之为完全⼆叉树。要注意的是满⼆叉树是⼀种特殊的完全⼆叉树.
💡 ⼆叉树性质
        根据满⼆叉树的特点可知:
        1)若规定根结点的层数为 1 ,则⼀棵⾮空⼆叉树的第i层上最多有 2 i −1 个结点
        2)若规定根结点的层数为 1 ,则深度为 h 的⼆叉树的最⼤结点数是 2 h − 1
        3)若规定根结点的层数为 1 ,具有 n 个结点的满⼆叉树的深度 ( log
              以2为底, n+1 为对数)

2.3 ⼆叉树存储结构

2.3.1 顺序结构
顺序结构存储就是使⽤数组来存储,⼀般使⽤数组只适合表⽰完全⼆叉树,因为不是完全⼆叉树会
有空间的浪费,完全⼆叉树更适合使⽤顺序结构存储。

现实中我们通常把堆(⼀种⼆叉树)使⽤顺序结构的数组来存储,需要注意的是这⾥的堆和操作系
统 虚拟进程地址空间中的堆是两回事,⼀个是数据结构,⼀个是操作系统中管理内存的⼀块区域
分段。
2.3.2 链式结构
⼆叉树的链式存储结构是指,⽤链表来表⽰⼀棵⼆叉树,即⽤链来指⽰元素的逻辑关系。 通常的
⽅法 是链表中每个结点由三个域组成,数据域和左右指针域,左右指针分别⽤来给出该结点左孩
⼦和右孩 ⼦所在的链结点的存储地址 。链式结构⼜分为⼆叉链和三叉链,当前我们学习中⼀般都
是⼆叉链。后 ⾯课程学到⾼阶数据结构如红⿊树等会⽤到三叉链。

3. 实现顺序结构⼆叉树

⼀般堆使⽤顺序结构的数组来存储数据,堆是⼀种特殊的⼆叉树,具有⼆叉树的特性的同时,还具
备其他的特性。

3.1 堆的概念与结构

如果有⼀个关键码的集合 ,把它的所有元素按完全⼆叉树的顺序存储⽅ 式存储,在⼀个⼀维数组
中,并满⾜: ( 且 ), i = 0、 1 2... ,则称为⼩堆(或⼤堆)。将根结点最⼤的堆叫做最⼤堆或⼤
根堆,根结点最⼩的堆叫做最⼩堆或⼩根堆。
堆具有以下性质
堆中某个结点的值总是不⼤于或不⼩于其⽗结点的值;
堆总是⼀棵完全⼆叉树。
💡 ⼆叉树性质
对于具有 n 个结点的完全⼆叉树,如果按照从上⾄下从左⾄右的数组顺序对所有结点从
  0 开始编号,则对于序号为 i 的结点有:
1. i>0 i 位置结点的双亲序号: (i-1)/2 i=0 i 为根结点编号,⽆双亲结点
2. 2i+1<n ,左孩⼦序号: 2i+1 2i+1>=n 否则⽆左孩⼦
3. 2i+2<n ,右孩⼦序号: 2i+2 2i+2>=n 否则⽆右孩⼦

3.2 堆的实现

堆底层结构为数组,因此定义堆的结构为:
Heap.h
#pragma once
#include<stdio.h>
#include<assert.h>
#include<stdlib.h>
#include<stdbool.h>

//结构体  堆  ---数组
typedef int HPDataType;
typedef struct Heap
{
	HPDataType* arr;
	int size;//有效数据个数
	int capacity;//空间大小
}HP;

//初始化
void HPInit(HP* php);
 //销毁
void HPDestroy(HP* php);
//向上调整
void	AddjustUp(HPDataType* arr,int size);
//插入数据
void HPPush(HP* php, HPDataType x);
//删除数据
void HPPop(HP* php);
// 判空
bool HPEmpty(HP* php);
//打印堆顶数据
HPDataType HPTop(HP* php);

test.c

#define _CRT_SECURE_NO_WARNINGS 1
#include"Heap.h"
void test01()
{
	HP hp;
	HPInit(&hp);
	int arr[] = { 15,13,10,20,17,19 };
	for (int i = 0; i < 6; i++)
	{
		HPPush(&hp, arr[i]);
	}
	while (!HPEmpty(&hp))
	{
		printf("%d ", HPTop(&hp));
		HPPop(&hp);
	}


	HPDestroy(&hp);
}
int main()
{
	test01();
	return 0;
}

Heap.c

初始化

void HPInit(HP* php) {
	assert(php);
	php->arr = NULL;
	php->size = php->capacity = 0;
}

插入数据

//交换
void Swap(int* x, int* y)
{
	int tmp = *x;
	*x = *y;
	*y= tmp;
}
//向上调整
void	AddjustUp(HPDataType* arr, int size)
{
	int parent = (size - 1) / 2;
	while (size>=0)
	{
		if (arr[size] < arr[parent])
		{
			Swap(&arr[size], &arr[parent]);
			size = parent;
			parent = (size - 1) / 2;
		}
		else
		{
			break;
		}
	}
}
void HPPush(HP* php, HPDataType x)
{
	assert(php);
	if (php->capacity == php->size)
	{
		int newcapacity = php->capacity == 0 ? 4 : 2 * php->capacity;
		HPDataType* tmp = (HP*)realloc(php->arr, newcapacity * sizeof(HPDataType));
		if (tmp == NULL)
		{
			perror("realloc fail!");
			exit(1);
		}
		php->arr = tmp;
		php->capacity = newcapacity;
	}
	php->arr[php->size] = x;
	AddjustUp(php->arr, php->size);
	++php->size;
}

删除数据

void Swap(int* x, int* y)
{
	int tmp = *x;
	*x = *y;
	*y= tmp;
}
void AddjustDown(HPDataType* arr, int parent, int n)
{
	int child = parent * 2 + 1;
	while (child<n)
	{
		if (child+1<n && arr[child] > arr[child + 1])
		{
			child++;
		}
		if (arr[child] < arr[parent])
		{
			Swap(&arr[child], &arr[parent]);
			parent = child;
			child = parent * 2 + 1;
		}
		else
		{
			break;
		}
	}
}
void HPPop(HP* php)
{
	assert(php && php->size);
	Swap(&php->arr[0], &php->arr[php->size - 1]);
	--php->size;
	AddjustDown(php->arr, 0, php->size);
}

判空

bool HPEmpty(HP* php)
{
	assert(php);
	return php->size == 0;
}

打印堆顶数据

HPDataType HPTop(HP* php)
{
	assert(php && php->size);
	return php->arr[0];
}
3.2.1 向上调整算法
堆的插⼊
将新数据插⼊到数组的尾上,再进⾏向上调整算法,直到满⾜堆。
💡 向上调整算法
先将元素插⼊到堆的末尾,即最后⼀个孩⼦之后
插⼊之后如果堆的性质遭到破坏,将新插⼊结点顺着其双双亲往上调整到合适位置即可
 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 HPPush(HP* php, HPDataType x)
{
    assert(php);
    if (php->size == php->capacity)
    {
        size_t newCapacity = php->capacity == 0 ? 4 : php->capacity * 2;
        HPDataType* tmp = realloc(php->a, sizeof(HPDataType) * newCapacity);
        if (tmp == NULL)
        {
            perror("realloc fail");
            return;
        }
        php->a = tmp;
        php->capacity = newCapacity;
    }
    php->a[php->size] = x;
    php->size++;
    AdjustUp(php->a, php->size-1);
}

计算向上调整算法建堆时间复杂度
因为堆是完全⼆叉树,⽽满⼆叉树也是完全⼆叉树,此处为了简化使⽤满⼆叉树来证明(时间复杂度本来看的就是近似值,多⼏个结点不影响最终结果)
分析:
第1层, 2 0 个结点,需要向上移动0层
第2层, 2 1 个结点,需要向上移动1层
第3层, 2 2 个结点,需要向上移动2层
第4层, 2 3 个结点,需要向上移动3层
......
第h层, 2 h −1 个结点,需要向上移动h-1层
则需要移动结点总的移动步数为:每层结点个数 * 向上调整次数(第⼀层调整次数为0)
T ( h ) = 2 1 ∗ 1 + 2 2 ∗ 2 + 2 3 ∗ 3 + .. + 2 h −2 ∗ ( h − 2) + 2 h −1 ∗ ( h − 1)
2 ∗ T ( h ) = 2 2 ∗ 1 + 2 3 ∗ 2 + 2 4 ∗ 3 + .. + 2 h −1 ∗ ( h − 2) + 2 h ∗ ( h − 1)
② ⼀ ① 错位相减:
T ( h ) = −2 1 ∗ 1 − (2 2 + 2 3 + .. + 2 h −2 + 2 h −1 ) + 2 h ∗ ( h − 1)
T ( h ) = −2 0 − 2 1 ∗ 1 − (2 2 + 2 3 + .. + 2 h −2 + 2 h −1 ) + 2 h ∗ ( h − 1) + 2 0
T ( h ) = −(2 0 + 2 1 ∗ 1 + 2 2 + 2 3 + .. + 2 h −2 + 2 h −1 ) + 2 h ∗ ( h − 1) + 2 0
T ( h ) = −(2 h − 1) + 2 h ∗ ( h − 1) + 2 0
T ( h ) = −(2 h − 1) + 2 h ∗ ( h − 1) + 2 0
根据⼆叉树的性质: n = 2 h − 1 h = log 2 ( n + 1)
T ( n ) = − N + 2 h ∗ ( h − 1) + 2 0
F ( h ) = 2 h ( h − 2) + 2
F ( n ) = ( n + 1)(log 2 ( n + 1) − 2) + 2
由此可得:
💡 向上调整算法建堆时间复杂度为: O ( n ∗ log 2 n )
3.2.2 向下调整算法
堆的删除
删除堆是删除堆顶的数据,将堆顶的数据根最后⼀个数据⼀换,然后删除数组最后⼀个数据,再进⾏向下调整算法。
向下调整算法有⼀个前提:左右⼦树必须是⼀个堆,才能调整。
💡 向下调整算法
        • 将堆顶元素与堆中最后⼀个元素进⾏交换
        • 删除堆中最后⼀个元素
        • 将堆顶元素向下调整到满⾜堆特性为⽌
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 HPPop(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);
}
计算向下调整算法建堆时间复杂度
分析:
第1层, 2 0 个结点,需要向下移动h-1层
第2层, 2 1 个结点,需要向下移动h-2层
第3层, 2 2 个结点,需要向下移动h-3层
第4层, 2 3 个结点,需要向下移动h-4层
......
第h-1层, 2 h −2 个结点,需要向下移动1层
则需要移动结点总的移动步数为:每层结点个数 * 向下调整次数
T ( h ) = 2 0 ∗ ( h − 1) + 2 1 ∗ ( h − 2) + 2 2 ∗ ( h − 3) + 2 3 ∗ ( h − 4) + .. + 2 h −3 ∗ 2 + 2 h −2 ∗ 1
2 ∗ T ( h ) = 2 1 ∗ ( h − 1) + 2 2 ∗ ( h − 2) + 2 3 ∗ ( h − 3) + 2 4 ∗ ( h − 4) + ... + 2 h −2 ∗ 2 + 2 h −1 ∗ 1
② ⼀ ① 错位相减:
T ( h ) = 1 − h + 2 1 + 2 2 + 2 3 + 2 4 + .. + 2 h −2 + 2 h −1
T ( h ) = 2 0 + 2 1 + 2 2 + 2 3 + 2 4 + . + 2 h −2 + 2 h −1 h
T ( h ) = 2 h − 1 − h
根据⼆叉树的性质: n = 2 h − 1 h = log 2 ( n + 1)
T ( n ) = n log 2 ( n + 1) ≈ n
💡 向下调整算法建堆时间复杂度为: O ( n )

3.3 堆的应⽤

3.3.1 堆排序
版本⼀:基于已有数组建堆、取堆顶元素完成排序版本
// 1、需要堆的数据结构
// 2、空间复杂度 O(N)
void HeapSort(int* a, int n)
{
    HP hp;
    for(int i = 0; i < n; i++)
    {
        HPPush(&hp,a[i]);
    }
    int i = 0;
    while (!HPEmpty(&hp))
    {
        a[i++] = HPTop(&hp);
        HPPop(&hp);
    }
    HPDestroy(&hp);
}
该版本有⼀个前提,必须提供有现成的数据结构堆
版本⼆:数组建堆,⾸尾交换,交换后的堆尾数据从堆中删掉,将堆顶数据向下调整选出次⼤的数据
// 升序,建⼤堆
// 降序,建⼩堆
// O(N*logN)
void HeapSort(int* a, int n)
{
    // a数组直接建堆 O(N)
    for (int i = (n-1-1)/2; i >= 0; --i)
    {
        AdjustDown(a, n, i);
    }
    // O(N*logN)
    int end = n - 1;
    while (end > 0)
    {
        Swap(&a[0], &a[end]);
        AdjustDown(a, end, 0);
        --end;
    }
}
堆排序时间复杂度计算
分析:
第1层, 个结点,交换到根结点后,需要向下
移动0层
2 0
第2层, 个结点,交换到根结点后,需要向下
移动1层
2 1
第3层, 个结点,交换到根结点后,需要向下
移动2层
2 2
第4层, 个结点,交换到根结点后,需要向下
移动3层
2 3
......
第h层, 个结点,交换到根结点后,需要向
下移动h-1层
通过分析发现,堆排序第⼆个循环中的向下调整与建堆中的向上调整算法时间复杂度计算⼀致,此处不再赘述。因此,堆排序的时间复杂度为 O ( n + n ∗ log n ) ,即 O ( n log n )
💡 堆排序时间复杂度为: O ( n log n )
3.3.2 TOP-K问题
TOP-K问题:即求数据结合中前K个最⼤的元素或者最⼩的元素,⼀般情况下数据量都⽐较⼤。
⽐如:专业前10名、世界500强、富豪榜、游戏中前100的活跃玩家等。
对于Top-K问题,能想到的最简单直接的⽅式就是排序,但是:如果数据量⾮常⼤,排序就不太可取了
(可能数据都不能⼀下⼦全部加载到内存中)。最佳的⽅式就是⽤堆来解决,基本思路如下:
1)⽤数据集合中前K个元素来建堆
前k个最⼤的元素,则建⼩堆
前k个最⼩的元素,则建⼤堆
2)⽤剩余的N-K个元素依次与堆顶元素来⽐较,不满⾜则替换堆顶元素
将剩余N-K个元素依次与堆顶元素⽐完之后,堆中剩余的K个元素就是所求的前K个最⼩或者最⼤的元素
void CreateNDate()
{
	// 造数据
	int n = 100000;
	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) % 1000000;
		fprintf(fin, "%d\n", x);
	}
	fclose(fin);
}
void topk()
{
	printf("请输⼊k:>");
	int k = 0;
	scanf("%d", &k);
	const char* file = "data.txt";
	FILE* fout = fopen(file, "r");
	if (fout == NULL)
	{
		perror("fopen error");
		return;
	}
	int val = 0;
	int* minheap = (int*)malloc(sizeof(int) * k);
	if (minheap == NULL)
	{
		perror("malloc error");
		return;
	}
	for (int i = 0; i < k; i++)
	{
		fscanf(fout, "%d", &minheap[i]);
	}
	// 建k个数据的⼩堆
	for (int i = (k - 1 - 1) / 2; i >= 0; i--)
	{
		AdjustDown(minheap, k, i);
	}
	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]);
	}
	fclose(fout);
}
时间复杂度: O ( n ) = k + ( n k )log 2 k

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

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

相关文章

AntSK:在无网络环境中构建你的本地AI知识库的终极指南

亲爱的读者朋友们&#xff0c;我是许泽宇&#xff0c;今天我将深入探讨一个引人注目的开源工具——AntSK。这个工具让您在没有互联网连接的情况下&#xff0c;仍然能够进行人工智能知识库的对话和查询。想象一下&#xff0c;即使身处无网络环境中&#xff0c;您也可以轻松与AI进…

class 1: vue初识

目录 特点Vue.js 2和Vue.js 3优缺点对比Vue.js 2缺点Vue.js 3优点 架构createApp的对象参数template属性data属性methods属性 vue是由Evan You开源的轻量级前端框架&#xff0c;诞生于2014年&#xff0c;Vue.js 3采用MVVM架构&#xff0c;支持声明式编程、组件化开发、前端路由…

【IEEE独立出版】第三届人工智能、物联网和云计算技术国际会议(AIoTC 2024,9月13-15)

第三届人工智能、物联网与云计算技术国际会议(AIoTC 2024)将于2024年9月13日-15日在中国武汉举行。 本次会议由华中师范大学伍伦贡联合研究院与南京大学联合主办、江苏省大数据区块链与智能信息专委会承办、江苏省概率统计学会、江苏省应用统计学会、Sir Forum、南京理工大学、…

防火墙iptalbes和firewalld

一、IPtables介绍 Iptables是unix/linux自带的一款开源的基于包过滤(对OSI模型的四层或者是四层以下进行过滤)的防火墙工具&#xff0c;它的功能十分强大&#xff0c;可以对流入和流出服务器的数据包进行很精细的控制。 iptables其实并不是真正的防火墙&#xff0c;我们可以把…

软信天成:国内企业需要什么样的国产主数据管理平台?(三)

在前两期的内容里&#xff0c;我们探讨了当前国内企业使用国产主数据管理平台的紧迫需求&#xff0c;概述了软信自主研发的智能主数据管理平台如何通过“数据建模”、“个性化配置”、“PIM管理”以及“权限管理”四大功能推动企业高效运作。 本期&#xff0c;我们将为您介绍平…

瑞吉外卖--登录退出功能的实现

登录功能 需求分析 1. Controller 定义&#xff1a;Controller是MVC&#xff08;Model-View-Controller&#xff09;设计模式中的一部分&#xff0c;负责处理HTTP请求并返回HTTP响应。在Spring MVC中&#xff0c;Controller通常是一个处理特定HTTP请求的类。作用&#xff1a; …

可调电阻是否有正负极?

可调电阻没有固定的正负极&#xff0c;但是有一根箭头表示旋转的方向。 可调电阻在形状上类似于一个圆柱形或方形的小盒子&#xff0c;通常带有三个引脚或更多&#xff0c;其中一个是中心引脚&#xff0c;其余的是两个端部引脚。不像电解电容或二极管那样有固定的正负极&#…

PHP轻创推客集淘客地推任务平台于一体的综合营销平台系统源码

&#x1f680;轻创推客&#xff0c;营销新纪元 —— 集淘客与地推任务于一体的全能平台&#x1f310; &#x1f308;【开篇&#xff1a;营销新潮流&#xff0c;轻创推客引领未来】 在瞬息万变的营销世界里&#xff0c;你还在为寻找高效、全面的营销渠道而烦恼吗&#xff1f;&…

Android经典实战之跳转到系统设置页面或其他系统应用页面大全

本文首发于公众号“AntDream”&#xff0c;欢迎微信搜索“AntDream”或扫描文章底部二维码关注&#xff0c;和我一起每天进步一点点 在Android开发中&#xff0c;经常需要跳转到系统设置页面或其他系统应用页面。以下是一些常见的跳转方式及对应的Intent动作&#xff1a; 1. 跳…

全渠道分销零售行业免费开源ERP解决方案

引言 协助分销及零售连锁企业快速搭建新零售格局&#xff0c;充分整合移动社交流量、门店流量&#xff0c;实现企业线上线下 全渠道管理运营。 业务挑战 传统的经营模式&#xff0c;客户流失严重 分销及零售企业还处于传统经营模式&#xff0c;单一的商品、收银方式、会员维护…

绝对能让你效率起飞的10个AI工具及ChatGPT国内镜像集合【2024必备】

随着GPT的兴起&#xff0c;一大批AI工具随之诞生&#xff0c;其中有很多堪称神器 分享10个国内可以使用使用的网站和AI搬砖工具&#xff0c;摸鱼起飞就靠他们了。 1&#xff1a; AI Plus【AI对话】 推荐指数&#xff1a;⭐️⭐️⭐️⭐️⭐️ 适合人群&#xff1a;学生党、打…

前端面试题每日一学_2

今日一题&#xff1a; 下面JS代码执行后&#xff0c;最终的三个console.log的输出结果是什么&#xff1f; var obj { num: 1 }; var arr1 [1, 2, obj]; var arr2 arr1.slice(1); arr2[0]; arr2[1].num; obj.num; arr1[2].num; console.log(arr1[1] arr2[0]); console.log…

家政预约小程序源码+快速搭建全攻略

前言&#xff1a; 家政预约小程序是一种便捷的家政服务预约平台&#xff0c;它通过互联网技术将家政服务资源与用户连接起来&#xff0c;为用户提供高效、便捷的家政服务预约体验。 一、家政服务小程序开发架构 前端&#xff1a;小程序前端开发主要使用的技术包括HTML、CSS、…

向量嵌入是什么?理解LLM数据表示的基础

向量嵌入将数据转换为数学方程&#xff0c;赋予了人工智能的认知能力&#xff0c;但它们是如何使机器能够“学习”和“成长”的呢&#xff1f; 向量嵌入是什么&#xff1f; 向量嵌入这玩意儿&#xff0c;其实就是给数据穿上一件数学的外衣&#xff0c;让它们之间的关系和相似…

【SQL】三角形判断

目录 题目 分析 代码 题目 表: Triangle ------------------- | Column Name | Type | ------------------- | x | int | | y | int | | z | int | ------------------- 在 SQL 中&#xff0c;(x, y, z)是该表的主键列。 该表的每一行包…

生信圆桌x生信豆芽菜:生物信息学新手的学习与成长平台

生信豆芽菜是一个专门为生物信息学初学者创建的学习与交流平台&#xff0c;致力于帮助新手们快速入门并掌握生信分析的基础知识与技能。随着生物信息学在科研中的重要性日益提升&#xff0c;越来越多的学生和研究人员开始接触这一领域。生信豆芽菜正是为了满足这些新手的需求&a…

土壤墒情固定监测站的工作原理

TH-GTS03土壤墒情固定监测站是一种专门用于监测土壤墒情信息的设备&#xff0c;它通过一系列精密的传感器和数据处理系统&#xff0c;实时、准确地获取土壤的水分含量、温度以及其他相关参数&#xff0c;为农业生产、生态保护和水资源管理等提供重要依据。 土壤墒情固定监测站通…

实用库/函数之链表的使用

目录 1.1结点的建立 1.2为链表结点分配内存空间 1.stdlib:malloc函数与free函数 2.new运算符与delete运算符 1.3链表的基本操作 1.链表的创建 (1)头插法 (2)尾插法 2.查找 3.插入 4.删除 5.总结(小tip) 1.4静态链表(类似于数组) 1.1结点的建立 typedef str…

基于Java语言实现Creo二次开发的环境搭建

# 安装JAVA JDK 通常我们下载JDK在Oracle(甲骨文公司)官网下载即可&#xff1a;Java Downloads | Oracle 双击下载的软件后进行安装。安装过程可以选择自己想安装的位置&#xff08;安装的路径中最好不要存在中文和空格&#xff09; 这里由于我们是先安装Java编译及运行环境默…

上传PDF、DOC文件到SAP HCM系统中案例

背景:公司最近在上电子签系统&#xff0c;以实现劳动合同、保密协议等文件的去纸质化&#xff0c;保存为电子档文件&#xff0c;而企业的信息化的中心是SAP ERP&#xff0c;于是领导要求将签好的电子文件存储到HCM中。 题主写了如下代码实现需求&#xff1a; FUNCTION ZHR_SA…