数据结构之《二叉树》(中)

news2024/11/13 20:28:46

在数据结构之《二叉树》(上)中学习了树的相关概念,还了解的树中的二叉树的顺序结构和链式结构,在本篇中我们将重点学习二叉树中的堆的相关概念与性质,同时试着实现堆中的相关方法,一起加油吧!


1.实现顺序结构二叉树

在实现顺序结构的二叉树中通常把堆使用顺序结构的数组来存储,因此我们先要了解堆的概念与结构

1.1 堆的概念与结构

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

以上的堆的概念简单来说就是堆是完全二叉树,在大堆中根结点为最大的元素,在此之后的每个孩子结点都要小于或者等于它的父结点;在小堆中根结点为最小的元素,在此之后的每个孩子结点都要大于或者等于的父结点

因此通过堆的概念的了解需要知道堆有以下的性质:
• 堆中某个结点的值总是不大于或不小于其父结点的值
• 堆总是一棵完全二叉树

例如以下图示就是大堆,并且将其结点的数据存储到数组当中

以下图示就是小堆,并且将其结点的数据存储到数组当中

1.2二叉树的性质 

在了解的堆的相关概念和结构后,之后我们要来实现堆,因此在此之前还要再了解二叉树的相关性质

💡 二叉树性质
• 对于具有 n 个结点的完全二叉树,如果按照从上至下从左至右的数组顺序对所有结点从0 开始编号,则对于序号为 i 的结点有:
1. 若 i>0 ,i 位置结点的双亲序号: \frac{i-1}{2} ; i=0 , i 为根结点编号,无双亲结点
2. 若 2i+1<n ,左孩⼦序号: 2i+1 , 2i+1>=n 否则无左孩子
3. 若 2i+2<n ,右孩⼦序号: 2i+2 , 2i+2>=n 否则无右孩子

对以上的性质来通过一个示例来加深理解

以下就是根据二叉树的性质来求出各节点的孩子结点以及父结点的图示

1.3堆的实现

在了解了堆相关的性质与结构后接下来就可以来试着实现堆
在实现堆的程序中还是将代码分为三个文件

1.堆结构的定义 

//堆结构的定义
typedef int HDataType;
typedef struct Heap
{
	HDataType* arr;
	int size;//有效数据个数
	int capacity;//空间大小
}Heap;

在定义堆的结构中,使用一个结构体来定义堆的结构,里面的成员变量和顺序表中相同arr为数组首元素的指针,size为数组中有效元素的个数,capacity为数组空间的大小

2.堆的初始化

在堆的初始化函数的实现中先要在Heap.h内完成初始化函数的声明

//初始化堆
void HeapInit(Heap* php);

将该函数命名为HeapInit,函数的参数为结构体指针

在完成了函数的声明后就是在Heap.c内完成函数的定义

//初始化堆
void HeapInit(Heap* php)
{
	assert(php);
	php->arr = NULL;
	php->size = php->capacity = 0;
}

3.堆的插入

 在堆的插入函数的实现中先要在Heap.h内完成插入函数的声明

//堆的插入
void HeapPush(Heap* php,HDataType x);

将该函数命名为HeapPush函数参数有两个,第一个为结构体指针,第二个为要插入的数据

在完成了函数的声明后就是在Heap.c内完成函数的定义

在堆的插入当中我们要实现的是在堆的末尾插入数据,也就是在数组的size-1位置插入新的数据,并且在插入之后由于堆的特性还要将堆中各元素的位置进行调整,使得其变为一个小堆或者大堆

因此在插入函数中我们先要实现向上调整的函数

3.1 向上调整法 

要实现堆中的向上调整结点的函数首先要来分析在调整过程中需要实现哪些步骤,我们来看下面这个堆的示例

在这个小堆中我们先在堆的末尾插入数据为10的结点,在这之后要将该二叉树重新调整为小堆需要哪些操作呢?

首先就是要将新插入的结点10与它的父结点做对比,如果比父结点大就和父结点交换之后再和父结点的父结点比较重复以上操作直到最后父结点为0时就停止;在此过程中如果比父结点小就不进行交换

所以以上的二叉树要调整为小堆就要经过以下的步骤 

在完全向上调整实例的分析后接下来就可以来实现向上调整的代码 

先再Heap.h内对向上调整函数进行声明,通过以上的分析就可以推测出函数的参数有两个,一个为数组的首元素指针,另一个为数组的有效元素个数 

//向上调整法
void AdjustUp(HDataType* arr, int child);

在以下的代码当中parent就来表示父结点的数组下标,child就来表示孩子节点的下标,因此在知道孩子结点的下标时要求父结点的下标就可以用到前面提到的二叉树的性质,父结点的下标等于其孩子结点下标减一再除以二

//向上调整法
void AdjustUp(HDataType* arr, int child)
{
	int parent = (child - 1) / 2;//求父结点下标
	while (child>0)
	{
         //小堆时下面的if判断部分就用<
         //大堆时下面的if判断部分就用>
		if (arr[child] < arr[parent])//若父结点大于孩子结点就交换
		{
			Swap(&arr[child], & arr[parent]);
			child = parent;
			parent= (child - 1) / 2;
		}
		else
		{
			break;//若不符合以上if的条件就退出循环
		}
	}
}

例如以上的示例中在以下代码中parent和child的变化就如以下图所示

在以上函数中的Swap是来实现两个数的交换,以下是函数的定义

void Swap(HDataType* p1, HDataType* p2)
{
	HDataType* tmp = *p1;
	*p1 = *p2;
	*p2 = tmp;
}

3.2 插入函数代码实现 

在完成了向上调整的代码后接下来就可以来完成堆插入函数的实现

在插入函数中由于php为结构体指针,因此php不能为空,所以要将php进行assert断言
并且在插入之前也要判断数组空间是否满了,满了就要对空间进行调整,在此的调整代码和顺序表中相同
之后再将数据尾插到数组当中,再进行向上调整,最后切记要将size+1

//堆的插入
void HeapPush(Heap* php, HDataType x)
{
	assert(php);
	if (php->size == php->capacity)//对数组空间进行调整
	{
		int newcapacity = php->capacity == 0 ? 4 : 2 * php->capacity;
		Heap* tmp = (Heap*)realloc(php->arr,sizeof(HDataType) * newcapacity);
		if (tmp == NULL)
		{
			perror("malloc file");
			exit(1);
		}
		php->arr = tmp;
		php->capacity = newcapacity;
	}
	php->arr[php->size] = x;//进行尾插
	AdjustUp(php->arr, php->size);//进行向上调整
	php->size++;
}

4.堆的删除 

在堆的删除函数的实现中先要在Heap.h内完成删除函数的声明

//堆的删除
void HeapPop(Heap* php);

将该函数命名为HeapPop函数的参数是结构体指针

在完成了函数的声明后就是在Heap.c内完成函数的定义

在堆的删除中是将跟结点给删除,在删除过程中是将根结点和最后一个结点交换,之后将数组的有效个数减一这样就将原来的根结点给删除了,之后再进行各结点的调整使其重新变为一个堆

因此在插入函数中我们先要实现向下调整的函数

4.1向下调整法

要实现堆中的向下调整结点的函数首先要来分析在调整过程中需要实现哪些步骤,我们来看下面这个堆的示例

在这个小堆中我们如果已经跟结点删除,在这之后要将该二叉树重新调整为小堆需要哪些操作呢?

 

首先就是要将新的的跟结点70与它的两个孩子结点中小的做对比,如果比该孩子结点大就和该孩子结点交换之后再和该孩子结点的两的孩子结点中小的比较重复以上操作直到最后开始的跟结点为叶子节点时就停止;如果在这过程中比孩子结点中小的那个还小就不进行交换

所以以上的二叉树要调整为小堆就要经过以下的步骤 

在完全向下调整实例的分析后接下来就可以来实现向下调整的代码 

 先再Heap.h内对向上调整函数进行声明,通过以上的分析就可以推测出函数的参数有三个,一个为数组的首元素指针,另一个为数组的有效元素个数 ,最后一个是要向下调整的元素的数组下标

//向下调整法
void AdjustDown(HDataType* arr, int n, int parent);

之后就是在Heap.c内完成函数的定义
在以下的代码当中parent就来表示父结点的数组下标,child就来表示孩子节点的下标,因此在知道父结点的下标时要求孩子结点的下标就可以用到前面提到的二叉树的性质,孩子结点的下标等于其父结点下标乘二再加一或二

while循环中当孩子节点下标大于数组有效个数也就是父节点为叶子节点时就退出循环,因此进入while条件为child<n

//向下调整法
void AdjustDown(HDataType* arr, int n, int parent)
{
	int child = 2 * parent + 1;//孩子节点的下标
	while (child<n)
	{
      //如果为小堆 以下的if判断部分就用>
      //如果为大堆 以下的if判断部分就用<
		if (child+1<n && arr[child] > arr[child + 1])
		{
			child++;//若为小堆就找孩子节点中小的,大堆就找孩子节点中大的
		}
		if (arr[parent] > arr[child])//若孩子节点小于父节点就交换
		{
			Swap(&arr[parent], &arr[child]);
			parent = child;
			child= 2 * parent + 1;
		}
		else
		{
			break;//若不符合以上if的条件就退出循环
		}
	}
}

例如以上的示例中在以下代码中parent和child的变化就如以下图所示

 4.2删除函数代码实现

在插入函数中由于php为结构体指针,因此php不能为空,所以要将php进行assert断言
在删除函数中因为要删除的是堆的根结点,所以先将堆的根结点和堆的最后一个节点进行交换,之后再将size-1,这样就可以让数组当中的有效个数减一,使得原来的根结点被删除,之后再将该二叉树使用向下调整重新调整为堆

//堆的删除
void HeapPop(Heap* php)
{
	assert(php && php->size);
	php->arr[0] = php->arr[php->size - 1];
	--php->size;
	AdjustDown(php->arr, php->size, 0);
}

5.取堆顶的元素 

在堆的取堆顶的元素函数的实现中先要在Heap.h内完成取堆顶的元素函数的声明

//取堆顶的元素
HDataType HeapTop(Heap* php);

将该函数命名为HeapTop函数的参数是结构体指针,函数的返回值是堆跟结点内的数据

在完成了函数的声明后就是在Heap.c内完成函数的定义

//取堆顶的元素
HDataType HeapTop(Heap* php)
{
	assert(php);
	return php->arr[0];
}

6.堆的数据个数 

在求堆的数据个数函数的实现中先要在Heap.h内完成堆的数据个数函数的声明

//堆的数据个数
int HeapSize(Heap* php);

将该函数命名为HeapSize函数的参数是结构体指针,函数的返回值是堆的结点结点个数

在完成了函数的声明后就是在Heap.c内完成函数的定义
因为堆是用数组来实现的,所以堆中的结点个数就为数组的有效元素个数

//堆的数据个数
int HeapSize(Heap* php)
{
	assert(php);
	return php->size;
}

7.堆的判空

在判断堆是否为空函数的实现中先要在Heap.h内完成判断堆是否为空函数的声明

//堆的判空
bool HeapEmpty(Heap* php);

将该函数命名为HeapEmpty函数的参数是结构体指针,函数的返回类型是布尔类型

在完成了函数的声明后就是在Heap.c内完成函数的定义
在该函数中当当堆为空时就返回true,不为空时返回false

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

8.堆的销毁

在堆的销毁函数的实现中先要在Heap.h内完成堆的销毁函数的声明

//销毁堆
void HeapDestory(Heap* php);

将该函数命名为HeapDestory函数的参数是结构体指针

在完成了函数的声明后就是在Heap.c内完成函数的定义

//销毁堆
void HeapDestory(Heap* php)
{
	assert(php);
	if (php->arr)
	{
		free(php->arr);
	}
	php->arr = NULL;
	php->size = php->capacity = 0;
}

 9.堆实现完整代码

Heap.h
#define  _CRT_SECURE_NO_WARNINGS 1
#pragma once
#include<stdio.h>
#include<stdlib.h>
#include<assert.h>
#include<stdbool.h>

//堆结构的定义
typedef int HDataType;
typedef struct Heap
{
	HDataType* arr;
	int size;//有效数据个数
	int capacity;//空间大小
}Heap;

//初始化堆
void HeapInit(Heap* php);
//销毁堆
void HeapDestory(Heap* php);
//堆的插入
void HeapPush(Heap* php,HDataType x);
//堆的删除
void HeapPop(Heap* php);
//取堆顶的元素
HDataType HeapTop(Heap* php);
//堆的数据个数
int HeapSize(Heap* php);
//堆的判空
bool HeapEmpty(Heap* php);
//向上调整法
void AdjustUp(HDataType* arr, int child);
//向下调整法
void AdjustDown(HDataType* arr, int n, int parent);
Heap.c
#include"Heap.h"

void Swap(HDataType* p1, HDataType* p2)
{
	HDataType* tmp = *p1;
	*p1 = *p2;
	*p2 = tmp;
}


//向上调整法
void AdjustUp(HDataType* arr, int child)
{
	int parent = (child - 1) / 2;
	while (child>0)
	{
		//小堆 <
		//大堆 >
		if (arr[child] < arr[parent])
		{
			Swap(&arr[child], & arr[parent]);
			child = parent;
			parent= (child - 1) / 2;
		}
		else
		{
			break;
		}
	}

}

//向下调整法
void AdjustDown(HDataType* arr, int n, int parent)
{
	int child = 2 * parent + 1;
	while (child<n)
	{
		//小堆 >
		//大堆 <
		if (child+1<n && arr[child] > arr[child + 1])
		{
			child++;
		}
		if (arr[parent] > arr[child])
		{
			Swap(&arr[parent], &arr[child]);
			parent = child;
			child= 2 * parent + 1;
		}
		else
		{
			break;
		}
	}
}



//初始化堆
void HeapInit(Heap* php)
{
	assert(php);
	php->arr = NULL;
	php->size = php->capacity = 0;
}



//销毁堆
void HeapDestory(Heap* php)
{
	assert(php);
	if (php->arr)
	{
		free(php->arr);
	}
	php->arr = NULL;
	php->size = php->capacity = 0;
}


//堆的插入
void HeapPush(Heap* php, HDataType x)
{
	assert(php);
	if (php->size == php->capacity)
	{
		int newcapacity = php->capacity == 0 ? 4 : 2 * php->capacity;
		Heap* tmp = (Heap*)realloc(php->arr,sizeof(HDataType) * newcapacity);
		if (tmp == NULL)
		{
			perror("malloc file");
			exit(1);
		}
		php->arr = tmp;
		php->capacity = newcapacity;
	}
	php->arr[php->size] = x;
	AdjustUp(php->arr, php->size);
	php->size++;
}



//堆的删除
void HeapPop(Heap* php)
{
	assert(php && php->size);
	php->arr[0] = php->arr[php->size - 1];
	--php->size;
	AdjustDown(php->arr, php->size, 0);
}


//取堆顶的元素
HDataType HeapTop(Heap* php)
{
	assert(php);
	return php->arr[0];
}


//堆的数据个数
int HeapSize(Heap* php)
{
	assert(php);
	return php->size;
}


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

 2.堆的应用

1.堆排序

在之前的C语言的学习当中我们实现了冒泡排序,但在算法的复杂度中得出了冒泡排序的时间复杂度为O(n^2),因此其实冒泡排序的效率是不高的。接下来我们就要来学习一种使用堆来实现排序的算法。

在此之前通过学习堆的相关概念知道了在小堆中的根结点是堆中最小的,那么在小堆中只要一直取堆的根结点就可以得到升序的数据,以下就是使用这种方法来实现的堆排序

void HeapSort(int* a, int n)
{
    Heap hp;
    for(int i = 0; i < n; i++)
    {
        HeapPush(&hp,a[i]);
    }
    int i = 0;
    while (!HeapEmpty(&hp))
    {
        a[i++] = HeapTop(&hp);
        HeapPop(&hp);
    }
    HeapDestroy(&hp);
}

但是在以上的这种算法中需要将数组中的数据先要先存储在堆当中才能在之后得到堆顶的数据,因此以上这种方法的空间复杂度就为O(n),那么有什么方法能在不申请新的空间下来实现堆排序呢?接下来就来看以下的这种基于原来数组建堆的方法

//堆排序算法
void HeapSort(int* arr, int n)
{
	for (int i = 0; i < n; i++)
    {
	    AdjustUp(arr, i);
    }

	int end = n - 1;
	while (end > 0)
	{
		Swap(&arr[end], &arr[0]);
		AdjustDown(arr, end, 0);
		end--;
	}
	for (int i = 0; i < n; i++)
	{
		printf("%d ", arr[i]);
	}
}

在以上这种堆排序中先用向上调整法来将数组通过循环的将数组数组调整为堆,根据之前向上调整的代码在此的建的是小堆。之后的while循环中实现的是将小堆中的跟结点和尾结点进行交换这时数组中最小的元素就排在了数组的末尾之后再进行向下排序就可重新变为小堆,一直重复以上的操作就可以将数组的元素从小到大依次移动到数组末尾,最终原数组就变为升序的了。

那么以上除了使用向上调整建堆外,其实使用向下调整法也可以建堆,以下是使用向下调整法建堆的代码

//堆排序算法
void HeapSort(int* arr, int n)
{
	for (int i = (n - 1 - 1) / 2; i >= 0; i--)
	{
		AdjustDown(arr, n, i);
	}

	int end = n - 1;
	while (end > 0)
	{
		Swap(&arr[end], &arr[0]);
		AdjustDown(arr, end, 0);
		end--;
	}
	for (int i = 0; i < n; i++)
	{
		printf("%d ", arr[i]);
	}
}

这两种那一这种效率更好呢,这就需要来分析向上建堆和向下建堆的时间复杂度

先来看向上调整法

因为堆是完全二叉树,而满二叉树也是完全二叉树,此处为了简化使用满二叉树来证明(时间复杂度本来看的就是近似值,多几个结点不影响最终结果)

以下是各层的结点在向上调整过程中各层结点在调整过程中最坏的情况下移动的层数

需要移动结点总的移动步数为:每层结点个数 * 向上调整次数(第⼀层调整次数为0)

由此可得:
💡 向上调整算法建堆时间复杂度为: O(n ∗ log2 n)

接下来看向下调整法

以下是各层的结点在向下调整过程中各层结点在调整过程中最坏的情况下移动的层数

则需要移动结点总的移动步数为:每层结点个数 * 向下调整次数 

💡 向下调整算法建堆时间复杂度为: O(n) 

通过以上的分析后可以得出在堆排序中使用向下调整法建堆更好

堆排序第二个循环中的向下调整与建堆中的向上调整算法时间复杂度计算一致。因此堆排序的时间复杂度为 O(n + n ∗ log n) ,即 O(n log n)

 

 

2.TOP-K问题

TOP-K问题:即求数据结合中前K个最大的元素或者最小的元素,一般情况下数据量都比较大。
比如:专业前10名、世界500强、富豪榜、游戏中前100的活跃玩家等。
对于Top-K问题,能想到的最简单直接的方式就是排序,但是:如果数据量非常大,排序就不太可取了(可能数据都不能一下⼦全部加载到内存中)。最佳的方式就是用堆来解决,基本思路如下:

(1)用数据集合中前K个元素来建堆
      前k个最大的元素,则建小堆
      前k个最小的元素,则建大堆
(2)用剩余的N-K个元素依次与堆顶元素来比较,不满足则替换堆顶元素
将剩余N-K个元素依次与堆顶元素比完之后,堆中剩余的K个元素就是所求的前K个最小或者大的元素

以下这段代码可以实现将大量的整型数据输入到data.txt的文件当中

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);
}

 接下来就来实现解决TOP-K问题的代码,以下是实现的是得出数据结合中前K个最大的元素

void topk()
{
	
	int k=0;
	printf("k:");
	scanf("%d", &k);
	//读取文件中的前k个数据
	FILE* pf = fopen("data.txt", "r");
	if (pf == NULL)
	{
		perror("fopen fail");
		exit(1);
	}
	int* pa = (int*)malloc(k * sizeof(int));
	if (pa == NULL)
	{
		perror("malloc fail");
		exit(2);
	}

	for (int i = 0; i < k; i++)
	{
		fscanf(pf, "%d", &pa[i]);
	}

	//使用前k个数据建堆

	for (int i = (k - 1 - 1) / 2; i >= 0; i--)
	{
		AdjustDown(pa, k, i);
	}
	

	int tmp=0;
	//循环将k个数据之后的数据堆中最小的元素比较,若比这个元素大就交换
	while (fscanf(pf, "%d", &tmp) != EOF)
	{
		if (tmp > pa[0])
		{
			pa[0] = tmp;
			AdjustDown(pa, k, 0);
		}
	}

	for (int i = 0; i < k; i++)
	{
		printf("%d ", pa[i]);
	}


	fclose(pf);
	pf = NULL;

}


 

 

以上就是二叉树(中)的全部内容了,接下来在二叉树(下)将继续学习二叉树的知识,在下一篇中我们将重点学习链式结构的二叉树的相关知识,未完待续…… 

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

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

相关文章

详细测评下搬瓦工香港CN2 GIA VPS

搬瓦工香港VPS分移动CMI和电信CN2 GIA两个大类&#xff0c;一个属于骨干网&#xff0c;一个属于轻负载。搬瓦工的香港CN2 GIA根据测试来看实际上是CN2 GIABGP&#xff0c;并非三网纯CN2 GIA。详细测评数据如下&#xff1a; 用FIO再给测试一下硬盘I/O&#xff0c;可以仔细看看数…

全网最适合入门的面向对象编程教程:31 Python的内置数据类型-对象Object和类型Type

全网最适合入门的面向对象编程教程&#xff1a;31 Python 的内置数据类型-对象 Object 和类型 Type 摘要&#xff1a; Python 中的对象和类型是一个非常重要的概念。在 Python 中,一切都是对象,包括数字、字符串、列表等,每个对象都有自己的类型。 原文链接&#xff1a; Fre…

WebSocket 协议介绍

前言 一.通用协议设计 参考链接 /* --------------------------------------------------------------- | 魔数 2byte | 协议版本号 1byte | 序列化算法 1byte | 报文类型 1byte | --------------------------------------------------------------- | 状态 1byte | …

前端HTML+CSS查漏补缺——仿制百度搜索首页的一些思考

在像素模仿百度搜索首页的时候&#xff0c;在实现的时候&#xff0c;遇到了一些值得记录的点。 在这个过程中&#xff0c;也顺便看了看百度的源码&#xff0c;感觉很有意思。 对了&#xff0c;QQ截屏里面获取到的颜色&#xff0c;是不大正确的&#xff0c;会有点误差。 这是我…

TypeError: ‘float’ object is not iterable 深度解析

TypeError: ‘float’ object is not iterable 深度解析与实战指南 在Python编程中&#xff0c;TypeError: float object is not iterable是一个常见的错误&#xff0c;通常发生在尝试对浮点数&#xff08;float&#xff09;进行迭代操作时。这个错误表明代码中存在类型使用不…

Study--Oracle-08-ORACLE数据备份与恢复(一)

一、ORACLE数据保护方案 1、oracle数据保护方案 2、数据库物理保护方案 oracle数据库备份可以备份到本地集群存储&#xff0c;也可以备份到云存储。 3、数据库逻辑数据保护方案 二、ORACLE数据体系 1、ORACLE 数据库的存储结构 2、oracle物理和逻辑存储结构 3、数据库进程 4…

OpenCV||超简略的Numpy小tip

一、基本类型 二、数组属性 三、数组迭代&#xff08;了解&#xff09; import numpy as np # 创建一个数组 a np.arange(6).reshape(2, 3) # 使用np.nditer遍历数组 for x in np.nditer(a): print(x) np.nditer有多个参数&#xff0c;用于控制迭代器的行为&#xff…

一层5x1神经网络绘制训练100轮后权重变化的图像

要完成这个任务&#xff0c;我们可以使用Python中的PyTorch库来建立一个简单的神经网络&#xff0c;网络结构只有一个输入层和一个输出层&#xff0c;输入层有5个节点&#xff0c;输出层有1个节点。训练过程中&#xff0c;我们将记录权重的变化&#xff0c;并在训练100轮后绘制…

显示学习5(基于树莓派Pico) -- 彩色LCD的驱动

和这篇也算是姊妹篇&#xff0c;只是一个侧重SPI协议&#xff0c;一个侧重显示驱动。 总线学习3--SPI-CSDN博客 驱动来自&#xff1a;https://github.com/boochow/MicroPython-ST7735 所以这里主要还是学习。 代码Init def __init__( self, spi, aDC, aReset, aCS) :"&…

数据结构(5.4_2)——树和森林的遍历

树的先根遍历(深度优先遍历) 若树非空&#xff0c;先访问根结点&#xff0c;再依次对每棵子树进行先根遍历 树的先根遍历序列和这棵树相应二叉树的先序序列相同。 伪代码&#xff1a; //树的先根遍历 void PreOrder(TreeNode* R) {if (R ! NULL) {visit(R);//访问根结点w…

【WRF安装第四期(Ubuntu)】搭建WRF编译所需系统-WRF和WPS模型的安装

WRF安装第四期&#xff1a;搭建WRF编译所需系统-WRF和WPS模型的安装 1 WRF的编译安装&#xff08;Building WRF&#xff09;1.1 进入Build_WRF文件夹1.2 下载WRFV4.01.3 解压WRF安装包1.4 安装WRF选择#1&#xff1a;34选择#2&#xff1a;32 1.5 检查WRF是否安装成功1.5.1 WRF安…

ai文案生成器,文案自动生成好简单

随着科技的不断进步&#xff0c;AI在各个领域中扮演着越来越重要的角色。其中&#xff0c;ai文案生成器的出现给广告和市场营销行业带来了一场革命。曾经需要耗费大量时间和精力的文案创作过程&#xff0c;如今可以通过ai文案生成器轻松自动完成。这一创新技术的出现&#xff0…

什么是药物临床试验?

药物临床试验是指在人体上进行的新药试验研究&#xff0c;旨在确定新药的疗效、安全性、药代动力学和药效学。临床试验不仅帮助确认药物是否对特定疾病或症状有效&#xff0c;还帮助识别和评估药物的副作用和风险。 药物临床试验&#xff08;Clinical Trial&#xff0c;CT&…

数据结构:带索引的双链表IDL

IDLindexed double list 如图&#xff0c;下方是一个双链表&#xff0c;上方是索引。索引储存为结构体数组&#xff0c;结构体内包括一个指针&#xff0c;和长度。 假设索引只有一个&#xff0c;这时&#xff0c;它应该指向双链表的中间&#xff0c;这样才能提高搜索效率。称…

深入探索可擦除可编程只读存储器(EPROM)DS2502P+TR 1K位只添加存储器

DS2502PT&R产品描述&#xff1a; DS2502PT&R 为1K位只添加存储器&#xff0c;用于识别并存储产品的相关信息。产品批号或特殊的产品信息可以通过最少的接口访问—例如&#xff0c;微控制器的一个端口引脚。DS2502PT&R 具有一个工厂光刻注册码&#xff0c;其中包括…

UE5 大鹅的点击移动 第三人称

文章目录 一、创建动画蓝图二、创建 Location 地标三、Character 和 PlayerControl 的控制四、实现效果 一、创建动画蓝图 这里以 UE5 从零开始制作跟随的大鹅-CSDN博客 创建的动态资产创建动画蓝图&#xff1b;需要用到的资产列表有&#xff1a;大鹅的骨骼网格体&#xff0c;…

【大模型从入门到精通4】openAI API 分类

这里写目录标题 分类理解 SYSTEM 和 USER 在 AI 对话中的角色System MessageUser Message工作原理示例分类示例更多分类示例理论问题理论 分类 理解 SYSTEM 和 USER 在 AI 对话中的角色 在分类任务中&#xff0c;通常需要向模型提供一个需要将其分类到预定义类别中的文本场景…

【数据结构与算法】堆顶删除

堆顶的删除 一.堆顶出列的原理二.堆顶出列的实现1.覆盖最大元素并出列2.向下调整成为堆 三.堆排序四,总结 一.堆顶出列的原理 还记得我们刚开始说的嘛,如果我想要拿出最大的,那么下一个最大的会花落谁家. 那么就需要用到堆顶出列的原理了. 然后我们再对顶节点,进行向下调整就可…

9-springCloud集成nacos config

本文介绍spring cloud集成nacos config的过程。 0、环境 jdk 1.8maven 3.8.1Idea 2021.1nacos 2.0.3 1、项目结构 根项目nacos-config-sample下有两个module&#xff0c;这两个module分别是两个springboot项目&#xff0c;都从nacos中获取连接mysql的连接参数。我们开工。 …

被遗忘的哑终端 —— 键盘键位演变的启发者

注&#xff1a;机翻&#xff0c;未校对。 The Forgotten World of Dumb Terminals 被遗忘的哑终端世界 A quick journey through the lost age of “glass teletypes.” 快速穿越失落的“玻璃电传打字机”时代。 From the earliest days of digital computers, researchers o…