【数据结构】第十站:堆与堆排序

news2025/2/28 12:17:26

目录

一、二叉树的顺序结构

二、堆的概念及结构

三、堆的实现

1.堆的创建

2.堆的各接口实现

四、堆排序

1.堆排序的基本思想

2.堆排序的实现

3.堆排序时间复杂度

四、TOP-K问题

五、堆的完整代码


一、二叉树的顺序结构

二叉树有顺序结构和链式结构,分别使用顺序表和链表来实现

普通的二叉树是不适合用数组来存储的,因为可能会存在大量的空间浪费。而完全二叉树更适合使用顺序结构存储。 现实中我们通常把堆 ( 一种二叉树 ) 使用顺序结构的数组来存储,需要注意的是这里的堆和操作系统
虚拟进程地址空间中的堆是两回事,一个是数据结构,一个是操作系统中管理内存的一块区域分段。
如下图所示,是二叉树的顺序结构的逻辑结构与物理结构。逻辑结构就是我们想象出来的,而物理结构就是实实在在在内存中是如何存储的。

 不难发现,将逻辑结构转化为物理结构就是只需要按照一层一层的往数组中插入即可

并且这样转化好之后,他们的父子之间的下标是有规律的

parent=(child-1)/2

leftchild=parent*2+1

rightchild=parent*2+2

 但是这种顺序存储是适合满二叉树或者完全二叉树的,如果不是的话,会有很多空的元素,这样就存在一定的浪费了。

 由此可见,堆只适合存储完全二叉树

二、堆的概念及结构

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

上面的概念或许比较抽象,简单来说就是堆是一颗完全二叉树,在这颗二叉树中,如果每一个父节点都大于或等于子节点,那么称作最大堆,如果每一个父节点都小于或等于子节点,那么称作最小堆。

三、堆的实现

1.堆的创建

由于堆是一颗完全二叉树,并且存储在顺序表中,所以它的定义就很简单了

typedef int HPDateType;

typedef struct Heap
{
	HPDateType* a;
	int size;
	int capacity;
}Heap;

2.堆的各接口实现

1.堆的初始化

//堆的初始化
void HeapInit(Heap* php)
{
	assert(php);
	php->a = (HPDateType*)malloc(sizeof(HPDateType) * 4);
	if (php->a == NULL)
	{
		perror("malloc fail");
		return;
	}
	php->size = 0;
	php->capacity = 4;
}

如上代码所示,与顺序表的初始化是一样的操作

2.堆的插入

void Swap(HPDateType* a, HPDateType* b)
{
	HPDateType tmp = *a;
	*a = *b;
	*b = tmp;
}
void AdjustUp(HPDateType* a, int child)
{
	assert(a);
	int parent = (child - 1) / 2;
	while (child > 0)
	{
		if (a[parent] < a[child])
		{
			Swap(&a[parent], &a[child]);
			child = parent;
			parent = (child - 1) / 2;
		}
		else
		{
			break;
		}
	}
}
//堆的插入
void HeapPush(Heap* php, HPDateType x)
{
	assert(php);
	if (php->size == php->capacity)
	{
		HPDateType* tmp = (HPDateType*)realloc(php->a, sizeof(HPDateType) * php->capacity * 2);
		if (tmp == NULL)
		{
			perror("realloc fail");
			return;
		}
		php->a = tmp;
		php->capacity *= 2;
	}
	php->a[php->size++] = x;

	AdjustUp(php->a, php->size - 1);
}

如上代码所示,对于堆的插入,首先是现在顺序表中进行尾插,之后,我们要将这个刚刚插入的数据进行调整,假设我们需要的是大堆,那么也就是说,如果刚插入节点比父节点大,那么就需要进行交换,然后一层一层网上调整,直到遇到一个祖先比他大。一旦当刚插入的数据到达了最顶端,那么也可以停止了。

3.堆的删除

void AdjustDown(HPDateType* a, int n, int parent)
{
	assert(a);
	int child = parent * 2 + 1;
	while (child < n)
	{
		if ((child + 1 < n) && (a[child] < a[child + 1]))
		{
			child++;
		}
		if (a[child] > a[parent])
		{
			Swap(&a[child], &a[parent]);
			parent = child;
			child = parent * 2 + 1;
		}
		else
		{
			break;
		}
	}
}
//堆的删除
void HeapPop(Heap* php)
{
	assert(php);
	assert(!HeapEmpty(php));
	Swap(&(php->a[0]), &(php->a[php->size-1]));
	php->size--;

	AdjustDown(php->a, php->size, 0);
}

如上代码所示,对于堆而言它一般删除的是堆顶的元素,因为对于其他的元素没有意义,不仅破坏了堆的结构,扰乱了父子关系,而且效率低下。

所以我们一般删除堆顶元素,为了删除堆顶元素,我们可以交换堆顶的尾元素,然后删除掉最后一个元素即可。此时我们堆顶的元素需要往下调整,即如果孩子节点大比父亲节点大的化,则交换,一直循环往复。原来的元素调整到叶子节点的时候,就可以停止了。要注意,每一次向下调整需要与最大的孩子进行交换,我们可以假设左孩子最大, 然后进行比较从而得出大孩子的下标。

在向下调整中,我们需要传三个参数,一个是数组,一个是堆顶下标,一个是数组的元素个数。要传数组的元素个数是为了方便判断叶子节点的个数。

与向上调整不同的是,向上调整不需要传数组元素个数,因为它并不关心数组有多少元素,只需要确保孩子到达堆顶即可。而堆顶的下标很显然就是0。

也就是说,是目标的不同而导致的参数有所变化,向上是从某个位置到0下标处,向下某个是从某个位置到叶子节点处。叶子节点的位置需要根据size来判断

4.取堆顶的数据

//取堆顶的数据
HPDateType HeapTop(Heap* php)
{
	assert(php);
	return php->a[0];
}

这个代码很简单,返回堆顶元素即可

5.判断堆是否为空

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

与顺序表和栈判断为空方法一样

6.判断堆的数据个数

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

同样的,与栈的方法一样

7.堆的销毁

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

由于堆是使用顺序表实现的,所以其实就是顺序表或栈的销毁

8.利用一个数组来初始化堆

//利用数组初始化堆
void HeapInitArray(Heap* php, int* a, int n)
{
	assert(php);
	php->a = (HPDateType*)malloc(sizeof(HPDateType) * n);
	if (php->a == NULL)
	{
		perror("malloc fail");
		return;
	}
	php->size = n;
	php->capacity = n;

	int i = 0;
	for (i = (n - 1 - 1) / 2; i >= 0; i--)
	{
		AdjustDown(php->a, php->size, i);
	}
}

有时候我们也会遇到这样的接口,多给了一个数组,目的是讲这个数组中的数据给建成堆。我们直接使用向下调整法即可。向下调整在下文中详细给出。

四、堆排序

1.堆排序的基本思想

堆排序的思想是这样的,首先先将传入的数组的元素给建成堆,建堆的思想可以使用向上调整法,也可以使用向下调整法。然后当堆建成以后,如果我们要排的是升序,那么我们需要建大堆,这是因为大堆的堆顶元素就是最大的元素,为了不破坏堆,我们将堆顶元素和最后一个元素进行交换,这样一来,最后一个元素就是最大的元素了,然后将前n-1个元素看作一个新的数组,先将第一个元素向下调整,然后继续让堆顶元素和最后一个元素交换。这样就实现了堆排序。

注意:

1.排升序一定要建大堆,排降序建小堆。

如果升序建了小堆的话,那么第一个元素确实是最小的元素,但是后面的n-1个元素的堆关系全部混乱了。我们需要重新建堆,时间复杂度大幅度提升。

2.在建堆的时候,如果采用向上调整法建堆,由于向上调整法必须保证原来的数组就是一个大堆或小堆,所以那么我们是从第二个元素开始,每一个元素依次向上调整即可,类似于堆的插入,这样我们可以保证我们前面的元素时刻为大堆或者小堆

3.在建堆的时候,如果采用向下调整法建堆,由于向下调整法需要保证,这个节点的左右子树都是大堆或者小堆,所以,我们就得从后面的元素开始建堆,由于叶子节点本身可以视作一个堆,所以我们从最后一个父节点开始建堆,然后一步一步往前面的父节点进行调整,这样可以保证每一个节点的左右子树都是大堆或小堆。

2.堆排序的实现

//堆排序
void HeapSort(int* a, int n)
{
	//向上调整建堆
	int i = 0;
	//for (i = 1; i < n; i++)
	//{
	//	AdjustUp(a, i);
	//}
	//向下调整建堆
	for (i = (n - 1 - 1) / 2; i >= 0; i--)
	{
		AdjustDown(a, n, i);
	}
	int end = n - 1;
	while (end > 0)
	{
		Swap(&a[0], &a[end]);
		AdjustDown(a, end, 0);
		end--;
	}
}

如上代码所示,是堆排序的代码,建堆的时候,我们将向下调整和向上调整建堆的方法都给出,当堆建好以后,我们开始让堆顶元素与最后一个元素交换,然后向下调整,然后继续从前n-1个元素的数组中,让堆顶元素继续与最后最后一个元素交换,向下调整,如此循环往复即可。

3.堆排序时间复杂度

先说结论:向上调整建堆的时间复杂度为O(N*logN),向下调整建堆的时间复杂度为O(N),在选大数排序的过程时间复杂度也是O(N*logN)

总时间复杂度为O(N*logN)

向下调整建堆的时间复杂度分析

我们直接讨论最坏情况

如上图所示的满二叉树。我们不难得知每一层的节点个数

向上调整建堆的时间复杂度

事实上,我们也可以直观的看出来

向上调整建堆,由于二叉树下层的节点个数多,而下层向上层调整的次数也多。所以最终的次数必然是较多的

向下调整建堆,由于二叉树的下层节点个数多,但是下层的向下调整次数少,上层的个数少,次数多。所以向下调整建堆是优于向上调整建堆的

3.利用堆排序的时间复杂度计算

对于这一段代码,我们是这样思考的

先看最坏情况,最后一层的最坏情况是:由2^(h-1)次方个节点,每个节点交换后,又需要向下调整到最后一层也就是h-1次

不难发现,最后一层的时间复杂度已经达到了O(N*logN)

四、TOP-K问题

TOP-K 问题:即求数据结合中前 K 个最大的元素或者最小的元素,一般情况下数据量都比较大
比如:专业前 10 名、世界 500 强、富豪榜、游戏中前 100 的活跃玩家等。
对于 Top-K 问题,能想到的最简单直接的方式就是排序,或者说建大堆,取出前k个堆顶元素即可,但是:如果数据量非常大,排序和建大堆就不太可取了 ( 可能 数据都不能一下子全部加载到内存中 ) 。最佳的方式就是用堆来解决,基本思路如下:
1.前k个数据建立一个小堆
2.遍历剩余的数据,如果存在一个数据比堆顶元素大,那么让这个大的元素替换掉堆顶元素。最后向下调整,再次变成小堆。这样就可以解决问题。最终堆里面的数据就是前k个最大的元素
如下代码是随机生成100000个数据,然后求出前k个的最大值的代码
void CreatDate()
{
	int n = 100000;
	srand(time(0));
	const char* file = "date.txt";
	FILE* pf = fopen(file, "w");
	if (pf == NULL)
	{
		perror("fopen");
		return;
	}
	while (n--)
	{
		int x = rand() % 10000;
		fprintf(pf, "%d\n", x);
	}
	fclose(pf);

}
void PrintTopK(const char* file,int k)
{
	int* topk = (int*)malloc(sizeof(int) * k);
	if (topk == NULL)
	{
		perror("malloc\n");
		return;
	}
	FILE* pf = fopen(file, "r");
	if (pf == NULL)
	{
		perror("fopen\n");
		return;
	}
	for (int i = 0; i < k; i++)
	{
		fscanf(pf, "%d", &topk[i]);
	}
	for (int i = (k - 1 - 1) / 2; i >= 0; i--)
	{
		AdjustDown(topk, k, i);
	}
	int x = 0;
	int ret = fscanf(pf, "%d", &x);
	while (ret != EOF)
	{
		if (x > topk[0])
		{
			Swap(&x, &topk[0]);
			AdjustDown(topk, k, 0);
		}

		ret = fscanf(pf, "%d", &x);

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

五、堆的完整代码

Heap.h

#pragma once

#include<stdio.h>
#include<malloc.h>
#include<assert.h>
#include<stdbool.h>
#include<time.h>
#include<stdlib.h>

typedef int HPDateType;

typedef struct Heap
{
	HPDateType* a;
	int size;
	int capacity;
}Heap;

//堆的初始化
void HeapInit(Heap* php);
//利用数组初始化堆
void HeapInitArray(Heap* php, int* a, int n);
//堆的插入
void HeapPush(Heap* php, HPDateType x);
//堆的删除
void HeapPop(Heap* php);
//取堆顶的数据
HPDateType HeapTop(Heap* php);
//堆是否为空
bool HeapEmpty(Heap* php);
//堆的数据个数
int HeapSize(Heap* php);
// 堆的销毁
void HeapDestory(Heap* php);

//交换两个元素
void Swap(HPDateType* a, HPDateType* b);
//向上调整
void AdjustUp(HPDateType* a, int child);
//向下调整
void AdjustDown(HPDateType* a, int n, int parent);
//堆排序
void HeapSort(int* a, int n);

Heap.c

#define _CRT_SECURE_NO_WARNINGS 1
#include"Heap.h"

//堆的初始化
void HeapInit(Heap* php)
{
	assert(php);
	php->a = (HPDateType*)malloc(sizeof(HPDateType) * 4);
	if (php->a == NULL)
	{
		perror("malloc fail");
		return;
	}
	php->size = 0;
	php->capacity = 4;
}
void Swap(HPDateType* a, HPDateType* b)
{
	HPDateType tmp = *a;
	*a = *b;
	*b = tmp;
}
//向上调整
void AdjustUp(HPDateType* a, int child)
{
	assert(a);
	int parent = (child - 1) / 2;
	while (child > 0)
	{
		if (a[parent] < a[child])
		{
			Swap(&a[parent], &a[child]);
			child = parent;
			parent = (child - 1) / 2;
		}
		else
		{
			break;
		}
	}
}
//堆的插入
void HeapPush(Heap* php, HPDateType x)
{
	assert(php);
	if (php->size == php->capacity)
	{
		HPDateType* tmp = (HPDateType*)realloc(php->a, sizeof(HPDateType) * php->capacity * 2);
		if (tmp == NULL)
		{
			perror("realloc fail");
			return;
		}
		php->a = tmp;
		php->capacity *= 2;
	}
	php->a[php->size++] = x;

	AdjustUp(php->a, php->size - 1);
}
//向下调整
void AdjustDown(HPDateType* a, int n, int parent)
{
	assert(a);
	int child = parent * 2 + 1;
	while (child < n)
	{
		if ((child + 1 < n) && (a[child] > a[child + 1]))
		{
			child++;
		}
		if (a[child] < a[parent])
		{
			Swap(&a[child], &a[parent]);
			parent = child;
			child = parent * 2 + 1;
		}
		else
		{
			break;
		}
	}
}
//堆的删除
void HeapPop(Heap* php)
{
	assert(php);
	assert(!HeapEmpty(php));
	Swap(&(php->a[0]), &(php->a[php->size-1]));
	php->size--;

	AdjustDown(php->a, php->size, 0);
}
//取堆顶的数据
HPDateType HeapTop(Heap* php)
{
	assert(php);
	return php->a[0];
}
//堆是否为空
bool HeapEmpty(Heap* php)
{
	assert(php);
	return php->size == 0;
}
//堆的数据个数
int HeapSize(Heap* php)
{
	assert(php);
	return php->size;
}
// 堆的销毁
void HeapDestory(Heap* php)
{
	assert(php);
	free(php->a);
	php->a = NULL;
	php->size = 0;
	php->capacity = 0;
}

//堆排序
void HeapSort(int* a, int n)
{
	//向上调整建堆
	int i = 0;
	//for (i = 1; i < n; i++)
	//{
	//	AdjustUp(a, i);
	//}
	//向下调整建堆
	for (i = (n - 1 - 1) / 2; i >= 0; i--)
	{
		AdjustDown(a, n, i);
	}
	int end = n - 1;
	while (end > 0)
	{
		Swap(&a[0], &a[end]);
		AdjustDown(a, end, 0);
		end--;
	}
}
//利用数组初始化堆
void HeapInitArray(Heap* php, int* a, int n)
{
	assert(php);
	php->a = (HPDateType*)malloc(sizeof(HPDateType) * n);
	if (php->a == NULL)
	{
		perror("malloc fail");
		return;
	}
	php->size = n;
	php->capacity = n;

	int i = 0;
	for (i = (n - 1 - 1) / 2; i >= 0; i--)
	{
		AdjustDown(php->a, php->size, i);
	}
}

Test.c

#define _CRT_SECURE_NO_WARNINGS 1
#include"Heap.h"

void TestHeap1()
{
	Heap hp;
	HeapInit(&hp);
	HeapPush(&hp, 5);
	HeapPush(&hp, 10);
	HeapPush(&hp, 60);
	HeapPush(&hp, 100);
	HeapPush(&hp, 99);
	HeapPush(&hp, 12);
	HeapPush(&hp, 16);
	while (!HeapEmpty(&hp))
	{
		printf("%d ", HeapTop(&hp));
		HeapPop(&hp);
	}
	printf("\n");
	HeapDestory(&hp);
}



void TestHeapSort()
{
	int a[] = { 1,2,5,4,100,22,165,12,167,123,111,0 };
	int sz = sizeof(a) / sizeof(a[0]);
	HeapSort(a, sz);
}

void CreatDate()
{
	int n = 100000;
	srand(time(0));
	const char* file = "date.txt";
	FILE* pf = fopen(file, "w");
	if (pf == NULL)
	{
		perror("fopen");
		return;
	}
	while (n--)
	{
		int x = rand() % 10000;
		fprintf(pf, "%d\n", x);
	}
	fclose(pf);

}
void PrintTopK(const char* file,int k)
{
	int* topk = (int*)malloc(sizeof(int) * k);
	if (topk == NULL)
	{
		perror("malloc\n");
		return;
	}
	FILE* pf = fopen(file, "r");
	if (pf == NULL)
	{
		perror("fopen\n");
		return;
	}
	for (int i = 0; i < k; i++)
	{
		fscanf(pf, "%d", &topk[i]);
	}
	for (int i = (k - 1 - 1) / 2; i >= 0; i--)
	{
		AdjustDown(topk, k, i);
	}
	int x = 0;
	int ret = fscanf(pf, "%d", &x);
	while (ret != EOF)
	{
		if (x > topk[0])
		{
			Swap(&x, &topk[0]);
			AdjustDown(topk, k, 0);
		}

		ret = fscanf(pf, "%d", &x);

	}
	for (int i = 0; i < k; i++)
	{
		printf("%d ", topk[i]);
	}
	free(topk);
	fclose(pf);
}
int main()
{
	//TestHeap1();

	//TestHeapSort();
	//CreatDate();
	PrintTopK("date.txt", 10);
	return 0;
}

本期内容就到这里了

如果对你有帮助的话,不要忘记点赞加收藏哦!!!

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

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

相关文章

Android之adb安装busybox使用wget、telnet等服务

一、adb里面安装busybox BusyBox 是一个集成了三百多个最常用Linux命令和工具的软件。BusyBox 包含了一些简单的工具&#xff0c;例如ls、cat和echo等等&#xff0c;还包含了一些更大、更复杂的工具&#xff0c;例grep、find、mount以及telnet。 1、下载busybox busybox—bi…

有哪些通过PMP认证考试的心得值得分享?

回顾这100多天来艰辛的备考经历&#xff0c;感慨颇多 一&#xff0c;对于pmp的认知 百度百科&#xff1a;PMP&#xff08;Project Management Professional&#xff09;指项目管理专业人士&#xff08;人事&#xff09;资格认证。美国项目管理协会&#xff08;PMI&#xff09;举…

使用 Floyd Warshall 检测负循环

我们得到了一个有向图。我们需要计算图形是否有负循环。负循环是循环的总和为负的循环。 在图形的各种应用中都可以找到负权重。例如,如果我们沿着这条路走,我们可能会得到一些好处,而不是为一条路付出代价。 例子:

PVE相关的各种一键脚本(一键安装PVE)(一键开设KVM虚拟化的NAT服务器-自带内外网端口转发)

PVE 原始仓库&#xff1a;https://github.com/spiritLHLS/pve 前言 建议debian在使用前尽量使用最新的系统 非debian11可使用 debian一键升级 来升级系统 当然不使用最新的debian系统也没问题&#xff0c;只不过得不到官方支持 请确保使用前机器可以重装系统&#xff0c;…

RK3568平台开发系列讲解(驱动基础篇)自动创建设备节点

🚀返回专栏总目录 文章目录 一、自动创建设备节点1.1、创建和删除类函数1.2、创建设备函数二、创建类函数三、创建设备函数沉淀、分享、成长,让自己和他人都能有所收获!😄 📢自动创建设备节点分为两个步骤: 步骤一:使用 class_create 函数创建一个类。步骤二:使用 d…

C++算法初级7——二分查找

C算法初级7——二分查找 文章目录C算法初级7——二分查找在升序的数组上进行二分查找总结应用范围应用二分查找的原理&#xff1a;每次排除掉一半答案&#xff0c;使可能的答案区间快速缩小。 二分查找的时间复杂度&#xff1a;O(log n)&#xff0c;因为每次询问会使可行区间的…

【MyBatis Plus】001 -- MyBatis-Plus快速入门(介绍、QuickStart)

目录 1、了解MyBatis-Plus 1.1 MyBatis-Plus介绍 1.2 代码及文档 1.3 特性 1.4 架构 1.5 作者 2、快速开始 2.1 创建数据库以及表 2.2 创建工程 2.3 MyBatis MP 2.3.1 创建子module 2.3.2 MyBatis实现查询User&#xff08;无Service方法&#xff0c;直接通过Mapper实现查询&am…

海外虚拟主机空间:如何使用CDN加速提升用户体验?

随着互联网的迅速发展和全球化的趋势&#xff0c;越来越多的企业和个人选择海外虚拟主机空间。然而&#xff0c;由于服务器的地理位置和网络延迟等原因&#xff0c;这些网站在国内访问时可能会遇到较慢的加载速度和不稳定的用户体验。为了解决这一问题&#xff0c;使用CDN加速是…

Web漏洞-文件包含漏洞超详细全解(附实例)

目录 一、导图 二、文件包含漏洞 1.脚本代码 2.原理演示 3.漏洞成因 4.检测方法 5.类型分类 三、本地文件包含漏洞的利用 <无限制本地文件包含> <有限制本地文件包含> 四、远程文件包含漏洞的利用 <无限制远程文件包含> <有限制远程文件包含…

开心档之C++ 多线程

C 多线程 目录 C 多线程 创建线程 终止线程 实例 实例 实例 向线程传递参数 实例 连接和分离线程 实例 std::thread 实例 多线程是多任务处理的一种特殊形式&#xff0c;多任务处理允许让电脑同时运行两个或两个以上的程序。一般情况下&#xff0c;两种类型的多任务…

NumPy 初学者指南中文第三版:11~14

原文&#xff1a;NumPy: Beginner’s Guide - Third Edition 协议&#xff1a;CC BY-NC-SA 4.0 译者&#xff1a;飞龙 十一、玩转 Pygame 本章适用于希望使用 NumPy 和 Pygame 快速轻松创建游戏的开发人员。 基本的游戏开发经验会有所帮助&#xff0c;但这不是必需的。 您将学…

LinuxGUI自动化测试框架搭建(八)-安装LinuxGUI自动化测试工具Dogtail

(八)-安装LinuxGUI自动化测试工具Dogtail 1 Dogtail简介2 Dogtail技术原理3 Dogtail安装4 Dogtail的sniff组件1 Dogtail简介 官网:Dogtail官网文档; Linux平台能够支持Accessibility去获取元素控件的工具,主要有Dogtail和LDTP两个工具;dogtail 用 Python 编写,是python …

SpingBoot——SB整合MB的web项目模板

这里是我以后用到的项目都要先创建的模板 第一步 新建一个springboot项目&#xff0c;这里jdk版本和java版本根据需求选择 第二步 ——选择springboot版本和他提供的可以选择安装的依赖 这里因为是开发web项目&#xff0c;所以选择一个spring web 同时因为还要用到mysql&am…

分享4个不可或缺的 VSCode 插件,让 Tailwind CSS开发更简单

本文将为大家分享我在使用 Tailwind 进行开发时常用的四个 VSCode 扩展程序&#xff0c;这些扩展程序都包含在 VSCode 的 TailwindCSS Kit 扩展程序包中。1.Tailwind CSS IntelliSenseTailwind CSS IntelliSense 是一款功能强大的工具&#xff0c;可以帮助开发者更快、更高效地…

python-day1

第001天&#xff1a;初识python 本博客主要涉及到以下几个部分 1、配置镜像源 2、变量名及其命名规范 3、input函数和数据类型 4、指令和程序 5、运算符 6、练习 1、配置镜像源 此处我配置的是豆瓣源&#xff0c;操作步骤如下&#xff1a; 1、进入D:XXX\Scripts文件夹&#xff…

NumPy 初学者指南中文第三版:1~5

原文&#xff1a;NumPy: Beginner’s Guide - Third Edition 协议&#xff1a;CC BY-NC-SA 4.0 译者&#xff1a;飞龙 一、NumPy 快速入门 让我们开始吧。 我们将在不同的操作系统上安装 NumPy 和相关软件&#xff0c;并看一些使用 NumPy 的简单代码。 本章简要介绍了 IPython…

数据结构和算法学习记录——初识二叉树(定义、五种基本形态、几种特殊的二叉树、二叉树的重要性质、初识基本操作函数)

目录 二叉树的定义 二叉树具体的五种基本形态 1.空树 2.只有一个节点 3.有左子树&#xff0c;但右子树为空 4.有右子树&#xff0c;但左子树为空 5.左右两子树都不为空 特殊二叉树 斜二叉树 满二叉树 完全二叉树 二叉树的几个重要性质 初识二叉树的几个操作函数 …

线性代数代码实现(七)求解线性方程组(C++)

前言&#xff1a; 上次博客&#xff0c;我写了一篇关于定义矩阵除法并且代码的文章。矩阵除法或许用处不大&#xff0c;不过在那一篇文章中&#xff0c;我认为比较好的一点是告诉了大家一种计算方法&#xff0c;即&#xff1a;若矩阵 已知且可逆&#xff0c;矩阵 已知&#x…

2021蓝桥杯真题大写 C语言/C++

题目描述 给定一个只包含大写字母和小写字母的字符串&#xff0c;请将其中所有的小写字母转换成大写字母后将字符串输出。 输入描述 输入一行包含一个字符串。 输出描述 输出转换成大写后的字符串。 输入输出样例 示例 输入 LanQiao 输出 LANQIAO 评测用例规模与约定 对于…

[架构之路-158]-《软考-系统分析师》-10-系统分析-1-5-逻辑设计、逻辑模型(系统分析师的主要职责之一)

目录 前言&#xff1a;什么是系统 科学内涵 常见的系统 第 10章 现有系 统 分 析 1 0 . 1 系统分析概述 1 . 系统分析的任务 2 . 系统分析的难点 3 . 对系统分析师的要求 1 0 . 2 详细调查 10.2.1 详细调查的原则 10.2.2 详细调査的内容 》对企业的实际运营和业务进…