【数据结构初阶】之堆(C语言实现)

数据结构初阶之堆(C语言实现)

  • 🌏 堆的概念
  • 🌏 堆的模拟实现
    • 🐓 堆的结构和方法接口
    • 🐓 堆的方法的模拟实现
      • 🙊 堆的初始化
      • 🙊 堆的构建
      • 🙊 堆的插入
      • 🙊 向上调整
      • 🙊 堆的删除
      • 🙊 向下调整
      • 🙊 堆的数据的个数
      • 🙊 堆的判空
      • 🙊 堆顶元素
      • 🙊 堆的销毁
      • 🙊 堆排序
        • 🌺 版本1 创建堆,通过push、pop进行操作 (异地操作)
        • 🌺 版本2 向上调整建堆(原地操作)
        • 🌺 版本3----向下调整建堆(原地操作)
          • 🍀 向下调整建堆的时间复杂度分析
      • 🙊 topK问题
        • 🌺 topK问题的分析
        • 🌺 topK问题的代码实现
        • 🌺 top问题的时间复杂度和空间复杂度分析
  • 🌏 测试程序
    • 🐓 打印堆
    • 🐓 验证是否是一个堆
    • 🐓 测试程序

前言:在二叉树基础篇我们提到了二叉树的顺序实现,今天让我们来学习一下特殊的二叉树———堆的相关知识。

📃博客主页: 小镇敲码人
💞热门专栏:数据结构与算法
🚀 欢迎关注:👍点赞 👂🏽留言 😍收藏
🌏 任尔江湖满血骨,我自踏雪寻梅香。 万千浮云遮碧月,独傲天下百坚强。 男儿应有龙腾志,盖世一意转洪荒。 莫使此生无痕度,终归人间一捧黄。🍎🍎🍎
❤️ 什么?你问我答案,少年你看,下一个十年又来了 💞 💞 💞

🌏 堆的概念

堆是一种完全二叉树,但是其节点值满足一些特定的规则,又分为小堆和大堆,小堆是任意根节点的值都小于等于它子树的节点值,大堆是任意根节点的值都大于等于它子树的值。

小堆:

在这里插入图片描述

大堆:

在这里插入图片描述

注意:不管是大堆还是小堆,其同一层的节点大小可任意。

🌏 堆的模拟实现

🐓 堆的结构和方法接口

在二叉树部分我们曾经提到,堆是一种完全二叉树,所以其使用顺序存储是不会浪费空间的,而顺序存储就是我们常说的动态顺序表。

#define  _CRT_SECURE_NO_WARNINGS 1
#include<stdbool.h>
#include<stdio.h>
#include<stdlib.h>
#include<assert.h>
#include<string.h>

typedef int HPDataType;
typedef struct Heap
{
	HPDataType* _a;
	int _size;
	int _capacity;
}Heap;

//堆的初始化
void HeapInit(Heap* hp);
// 堆的构建(直接给一个数组)
void HeapCreate(Heap* hp, HPDataType* a, int n);
// 堆的销毁
void HeapDestory(Heap* hp);
// 堆的插入
void HeapPush(Heap* hp, HPDataType x);
// 堆的删除
void HeapPop(Heap* hp);
// 取堆顶的数据
//HPDataType HeapTop(Heap* hp);
// 堆的数据个数
int HeapSize(Heap* hp);
// 堆的判空
int HeapEmpty(Heap* hp);
//向上调整建堆
void AdjustUp(HPDataType* a, int child);
//向下调整
void AdjustDown(HPDataType* a, int parent, int n);
//两数交换
void Swap(HPDataType* hp1, HPDataType* hp2);
//打印堆
void HeapPrint(Heap* hp);
//堆排序
void Heapsort(HPDataType* a, int n);

🐓 堆的方法的模拟实现

下面我们的方法我们都默认建一个小堆。

🙊 堆的初始化

堆的初始化很简单,我们将其对应的指针置空,元素个数和空间大小置0即可。

//堆的初始化
void HeapInit(Heap* hp)
{
	assert(hp);//hp不为空

	hp->_a = NULL;//指针置为空
	hp->_capacity = hp->_size = 0;//元素和空间大小置为0
}

🙊 堆的构建

给你一个数组,你应该如何建一个小堆呢?我们来画图分析:

// 堆的构建
void HeapCreate(Heap* hp, HPDataType* a, int n)
{
	assert(hp);//断言,防止hp为空

	hp->_a = (HPDataType*)malloc(sizeof(HPDataType) * n);//为存节点的数组申请空间
	hp->_size = hp->_capacity = n;//更新空间大小和元素大小

	memcpy(hp->_a, a, sizeof(HPDataType) * n);//将数组里的值copy到我们的数据域里面

	//向上调整建一个小堆
	for (int i = 1; i < n; i++)
	{
		AdjustUp(hp->_a,i);
	}
}

图解代码:
在这里插入图片描述

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

在这里插入图片描述

🙊 堆的插入

堆的插入应该如何实现呢?我们直接插入到线性表尾部就行,由于插入前是堆,所以我们走一个向上调整就可以将其调整为堆。

// 堆的插入
void HeapPush(Heap* hp, HPDataType x)
{
	assert(hp);//断言,防止hp为空

	if (hp->_size == hp->_capacity)//扩容
	{
		hp->_capacity = hp->_capacity == 0 ? 4 : hp->_capacity * 2;
		HPDataType* tmp = (HPDataType*)realloc(hp->_a,sizeof(HPDataType) * hp->_capacity);
		if (tmp == NULL)
		{
			perror("realloc failed\n");
			exit(-1);
		}
		hp->_a = tmp;
	}
	hp->_a[hp->_size] = x;//插入
	hp->_size++;

	//向上调整
	AdjustUp(hp->_a,hp->_size - 1);//在插入位置走一个向上调整
}

🙊 向上调整

上面已经分析过了。

//向上调整建小堆
void AdjustUp(HPDataType* a, int child)
{
	assert(a);//a不为空

	int parent = (child - 1) / 2;//找到child的父亲节点,和其比较看是否需要调整
	while (child > 0)
	{
		if (a[child] < a[parent])//建一个小堆,如果孩子节点比父亲节点的值小,需要调整
		{
			Swap(&a[child], &a[parent]);//交换
			child = parent;//将父亲节点的值给孩子,继续在这条路径上调整
			parent = (parent - 1) / 2;//更新父亲节点的下标
		}
		else
		{
			break;//孩子节点的值已经大于等于父亲节点的值了,不需要再继续调整
		}
	}
}

🙊 堆的删除

堆的删除我们是删除堆顶的数据,因为这个数据最值得去删,什么意思呢?举个例子1.因为删除最后一个节点不会改变堆的结构没什么意义,2.堆顶数据要么是最大值,要么是最小值,很特殊。

那我们应该如何实现堆的删除呢?我们还是画图来分析:

在这里插入图片描述
代码实现:

// 堆的删除
void HeapPop(Heap* hp)
{
	assert(hp);//hp不为空
	assert(hp->_size > 0);//堆的元素个数要大于0
    
	Swap(&hp->_a[0], &hp->_a[hp->_size - 1]);//交换堆顶元素和最后一个元素的值
	hp->_size--;//_size--

	//向下调整
	AdjustDown(hp->_a, 0, hp->_size);//根节点左树和右树为小堆,可以向下调整
}

🙊 向下调整

这个我们上面也已经分析了,直接给代码:

//向下调整(小堆)
void AdjustDown(HPDataType* a, int parent, int n)
{
	assert(a);//a不为空
	int child = parent * 2 + 1;//先假设最小的孩子是左孩子
	while (child < n)
	{
		//找出最小的孩子
		if (child+1 < n && a[child] > a[child + 1])//如果右孩子存在且右孩子的值比左孩子更小
		{
			child = child + 1;
		}

		if (a[child] < a[parent])//最小的孩子比parent的值小,就交换
		{
			Swap(&a[child], &a[parent]);
			parent = child;//更新parent
			child = parent * 2 + 1;//更新child,也假设最小的孩子是左孩子
		}
		else//最小的孩子比父亲节点的值大,已经不需要调整,是小堆了。
		{
			break;
		}
	}
}

🙊 堆的数据的个数

直接返回_size的值。

// 堆的数据个数
int HeapSize(Heap* hp)
{
	assert(hp);//hp不为空

	return hp->_size;
}

🙊 堆的判空

如果_size不为0,堆就不为空。

// 堆的判空
int HeapEmpty(Heap* hp)
{
	assert(hp);

	if (hp->_size == 0)//为空返回1
		return 1;
	else//不为空返回0
		return 0;
}

🙊 堆顶元素

堆顶元素在下标为0的位置。

// 取堆顶的数据
HPDataType HeapTop(Heap* hp)
{
	assert(hp);
	assert(hp->_size > 0);

	return hp->_a[0];
}

🙊 堆的销毁

堆的销毁实际上主要要做的就是回收在堆上开的空间,将相应的指针置为空,空间和元素大小置为0。

// 堆的销毁
void HeapDestory(Heap* hp)
{
	assert(hp);//hp不为空
	assert(hp->_size > 0);//元素个数要大于0

	free(hp->_a);//释放空间
	hp->_a = NULL;//将动态数组的指针置为空
	hp->_capacity = hp->_size = 0;//将空间和元素大小置为0
}

🙊 堆排序

由于小堆和大堆的堆顶元素都是最小或者最大的,所以我们pop堆顶元素,然后再剩下的元素调整,就能得到一个有序的序列。

🌺 版本1 创建堆,通过push、pop进行操作 (异地操作)

那我们该如何实现这个堆排序呢?下面的代码可以吗为什么?

void Heapsort(HPDataType* a, int n)
{
	Heap hp1;//假设我们建一个小堆
	HeapInit(&hp1);
	for (int i = 0; i < n; ++i)
	{
		HeapPush(&hp1, a[i]);//插入对应元素
	}

	for (int i = 0; i < n; ++i)
	{
		a[i] = HeapTop(&hp1);//将最小的元素依次给数组
		HeapPop(&hp1);//pop堆顶元素
	}
}

效果演示:

在这里插入图片描述

这种方法是不太好的,因为先暂且不谈时间复杂度的问题,我们为了去给数组排一个序,而建了一个堆,这个空间开销是没必要的,其实我们可以对数组原地建堆。

🌺 版本2 向上调整建堆(原地操作)
//堆排序,向上调整
void Heapsort2(HPDataType* a, int n)
{
	assert(a);


	for (int i = 1; i < n; i++)//我们向上调整,原地建一个小堆
	{
		AdjustUp(a,i);
	}

	for (int end = n - 1; end >= 0; --end)
	{
		Swap(&a[0], &a[end]);//将堆顶元素放到堆最后面去
		AdjustDown(a, 0, end);//此时的end就代表我们的元素个数
	}
}

原地建堆,如果你想排降序,就需要建一个小堆,因为小堆的堆顶元素是最小的,我们原地操作,就可以将最小的和最后一个元素交换,这样没有破坏原来的结构,走一个向下调整就可以恢复堆结构。

在这里插入图片描述

时间复杂度:NlogN,向上调整建堆:NlogN,向下调整调整一次logN(一共调整了N次),所以总体的时间复杂度也是O(N*logN)。

🌺 版本3----向下调整建堆(原地操作)

但是这样就结束了吗,我们发现如果使用上面这种方法,既要写一个向上调整,也要写一个向下调整,我只是想排个序,需要这么复杂吗,难道就不能直接向下调整建堆吗?这样我就不用写两个了呀,调整那块肯定要用向下调整的,不然就破坏堆的结构了。

下面我们来介绍向下调整建堆

在这里插入图片描述
代码实现:

//堆排序,向下调整
void Heapsort3(HPDataType* a, int n)
{
	assert(a);


	for (int i = (n-1-1)/2; i >= 0; i--)//我们向上调整,原地建一个小堆
	{
		AdjustDown(a,i,n);
	}

	for (int end = n - 1; end >= 0; --end)
	{
		Swap(&a[0], &a[end]);//将堆顶元素放到堆最后面去
		AdjustDown(a, 0, end);//此时的end就代表我们的元素个数
	}
}

运行结果:

在这里插入图片描述

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

在这里插入图片描述

整体这种堆排序的时间复杂度也是O(N*logN),建堆的消耗是O(N),但是每一次调整的消耗还是logN,调整了次,量级还是和第二种一样,但是向下调整建堆只需要写一个向下调整,且向下调整建堆比向上调整建堆要更快。

🙊 topK问题

topK问题指的是让我们求第K大,第K小等问题,思考一下,在一个乱序的数组里面,如果我们想要这样来做,我们常规的解决办法是什么?排序!!!这样来做的时间复杂度是N*logN,我们只是求第K大或者第K小的数,有必要把这些数全部排一遍顺序吗,答案是不用,下面让我们来学习一下使用堆这种数据结构来解决经典的topK问题。

🌺 topK问题的分析

下面我们画图来分析一下topk问题具体的解决之道。

在这里插入图片描述

🌺 topK问题的代码实现

我们使用C语言的文件操作,造了100w个小于100w的数据(随机值),然后我们求前5个大的数,我们这样来验证,把文件里任意5个数改为大于100w的数,如果最后打印出来是我们改的5个数,证明我们的topk问题得到解决。

在这里插入图片描述

可以看到我们改了最后5个数,修改之后把创建数据的程序注释掉,防止它重新生成,我们的修改就没意义了。

代码实现:

void CreateNDate()
{
	// 造数据
	int n = 1000000;
	srand(time(0));
	const char* file = "data.txt";
	FILE* fin = fopen(file, "w");
	if (fin == NULL)
	{
		perror("fopen error");
		return;
	}

	for (size_t i = 0; i < n; ++i)
	{
		int x = rand() % 1000000;//造随机数据,可以让我们的测试程序得到更好的检查
		fprintf(fin, "%d\n", x);//将数据放入文件
	}

	fclose(fin);
}

void PrintTopK(int k)
{
	const char* file = "data.txt";//定义文件
	FILE* flout = fopen(file, "r");
	if (flout == NULL)
	{
		perror("fopen failed");
		exit(-1);
	}

	//开一个数组存放k个数据,开一个小堆
	HPDataType* minheap = (HPDataType*)malloc(sizeof(HPDataType) * k);
	if (minheap == NULL)
	{
		perror("malloc failed");
		exit(-1);
	}
	for (int i = 0; i < k; i++)//将k个数放入数组中
	{
		fscanf(flout, "%d", &minheap[i]);
	}

	//向下调整建堆
	for (int i = (k - 1 - 1) / 2; i >= 0; i--)
	{
		AdjustDown(minheap, i, k);
    }
	//遍历,如果大于小堆堆顶的数据的话,就和它换,并向下调整
	int x = 0;
	while (fscanf(flout, "%d", &x) != EOF)
	{
		if (x > minheap[0])
		{
			Swap(&x, &minheap[0]);
			AdjustDown(minheap, 0,k);
		}
	}
	for (int i = 0; i < k; i++)//打印前k大的数
	{
		printf("%d ", minheap[i]);
	}
	fclose(flout);//关闭文件
}
int main()
{
	CreateNDate();//造10w个数据
	PrintTopK(5);
	return 0;
}

运行结果:

在这里插入图片描述

可以看到预期结果和我们修改的数是一致的,说明我们的代码应该没啥问题。

🌺 top问题的时间复杂度和空间复杂度分析

在这里插入图片描述

🌏 测试程序

下面我们来测试一下我们写的上述方法的正确性如何。

🐓 打印堆

我们可能需要把堆打印出来看结果。

//打印堆
void HeapPrint(Heap* hp)
{
	assert(hp);
	assert(hp->_size > 0);

	for (int i = 0; i < hp->_size; i++)
	{
		printf("%d ", hp->_a[i]);
	}
	printf("\n");
}

🐓 验证是否是一个堆

我们应该如何验证我们的数据是否是一个小堆呢?其实很简单,数组是顺序存储的,我们的数据一定是完全二叉树,这个不用验证,关键是验证我们的数据是否都满足小堆的定义,即所有的根节点的值都比它的左子树的和右子树的根节点的值要小,我们可以走一个前序遍历来验证。

//验证是否是一个小堆
int is_Heap(HPDataType* a, int n,int root)
{
	if (root >= n)
		return 1;
	int leftchild = root * 2 + 1;//左孩子的下标
	int rightchild = root * 2 + 2;//右孩子的下标
	if (leftchild < n && a[root] > a[leftchild])//左孩子存在且小于根,返回0,并打印相关信息
	{
		printf("%d\n位置不满足小堆", root);
		return 0;
	}
	if (rightchild < n && a[root] > a[rightchild])//右孩子存在且小于根,返回0,并打印相关信息
	{
		printf("%d\n位置不满足小堆", root);
		return 0;
	}
	return is_Heap(a, n, leftchild) && is_Heap(a, n, rightchild);//左树和右树都满足小堆就返回1
}

🐓 测试程序

void Test2()
{
	Heap hp1;//建堆
	HeapInit(&hp1);//初始化堆
	//造10w个数据进堆
	int N = 100000;
	srand(time(NULL));//通过时间来初始化随机数种子
	for (int i = 0; i < N; ++i)
	{
		HeapPush(&hp1, rand() % (10000000));//将数据存入堆里面
	}
	int i = is_Heap(hp1._a,N,0);//验证是否为堆
	if (i)//打印提示信息
		printf("是堆\n");
	else
		printf("不是堆\n");

	
	printf("堆的元素个数为%d\n", HeapSize(&hp1));//验证元素个数方法是否正确
	printf("堆顶元素为:%d\n", HeapTop(&hp1));//验证堆顶元素是否正确
	HeapPop(&hp1);//验证pop函数是否正确
	printf("堆的元素个数为%d\n", HeapSize(&hp1));
	printf("堆顶元素为:%d\n", HeapTop(&hp1));
	hp1._a[3] = -1;//破坏堆的结构
	i = is_Heap(hp1._a, N, 0);
	if (i)
		printf("是堆\n");
	else
		printf("不是堆\n");
	HeapDestory(&hp1);
	if (hp1._a == NULL)
		printf("销毁成功\n");
}

运行结果:

在这里插入图片描述

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

sentinel热点参数流控

1、概念 热点参数限流会统计传入参数中的热点参数&#xff0c;并根据配置的限流阈值与模式&#xff0c;对包含热点参数的资源调用进行限流。热点参数限流可以看做是一种特殊的流量控制&#xff0c;仅对包含热点参数的资源调用生效。 2、示例 2.1、目的 对于如下的/get接口的参…

【正点原子FreeRTOS学习笔记】————(13)队列集

这里写目录标题 一、队列集简介&#xff08;了解&#xff09;二、队列集相关API函数介绍&#xff08;熟悉&#xff09;三、队列集操作实验&#xff08;掌握&#xff09; 一、队列集简介&#xff08;了解&#xff09; 一个队列只允许任务间传递的消息为同一种数据类型&#xff…

鸿蒙开发之了解ArkTS

鸿蒙开发者官网 &#xff1a; https://developer.huawei.com/consumer/cn/ 开发鸿蒙要用的软件是 DevEco Studio ArkTS建立在JS和TS的基础之上&#xff0c;扩展了声明式UI开发范式和状态管理&#xff0c;提供更简洁和自然的开发方式。 ArkTS引入了渲染引擎的增强&#xff0c…

centos7 linux下yum安装redis

安装redis 1.检查是否有redis yum 源 yum install redis2.下载fedora的epel仓库 yum install epel-release3.安装redis数据库 yum install redis4.安装完毕后&#xff0c;使用下面的命令启动redis服务 # 启动redis service redis start# 停止redis service redis stop# 查…

UOS、Linux下的redis的详细部署流程(适用于内网)

提示&#xff1a;适用于Linux以及UOS等内外网系统服务器部署。 文章目录 一.上传离线包二.部署基本环境三.解压并安装redis四.后台运行redis五.uos系统可能遇到的问题六.总结 一.上传离线包 1.自己去Redis官网下载适配自己部署系统的redis安装包。 2.通过文件传输工具&#xf…

精酿啤酒屋:畅饮与文化的交汇点

在城市的繁华之中&#xff0c;Fendi Club啤酒屋犹如一颗璀璨的明珠&#xff0c;吸引着无数寻求文化与享受的人们。它不仅仅是一个提供美酒佳肴的地方&#xff0c;更是一个文化和社交的交汇点。 走进Fendi Club&#xff0c;你会被它与众不同的环境所吸引。暗色调的装饰风格&…

Python机器学习赋能GIS:地质灾害风险评价的新方法论

地质灾害是指全球地壳自然地质演化过程中&#xff0c;由于地球内动力、外动力或者人为地质动力作用下导致的自然地质和人类的自然灾害突发事件。由于降水、地震等自然作用下&#xff0c;地质灾害在世界范围内频繁发生。我国除滑坡灾害外&#xff0c;还包括崩塌、泥石流、地面沉…

NO11 蓝桥杯单片机之DS18B20数字温度计

DS18B20数字温度计这个模块和以往单片机学习的模块可能不同&#xff0c;这里还要知道其头文件&#xff08;.h&#xff09;和.c文件代码的理解。 具体这个温度计是怎么实现检测温度的&#xff0c;呃呃呃呃呃这可能就要去查阅专业资料&#xff0c;涉及的知识体系应该很庞大&…

ABNDP: Co-optimizing Data Access and Load Balance in Near-Data Processing——论文泛读

ASPLOS 2023 Paper 论文阅读笔记整理 问题 近数据处理&#xff08;NDP&#xff09;是一种很有前途的体系结构范式&#xff0c;可以解决数据密集型应用程序的内存墙挑战。基于3D堆叠存储器的典型NDP系统包含大量并行处理单元&#xff0c;每个并行处理单元都可以访问其本地存储…

YOLOv9改进策略:block优化 | SEAM提升小目标遮挡物性能

&#x1f4a1;&#x1f4a1;&#x1f4a1;本文改进内容&#xff1a;SEAM提升小目标遮挡物性能&#xff0c;在多个数据集得到很好的验证 改进结构图如下&#xff1a; YOLOv9魔术师专栏 ☁️☁️☁️☁️☁️☁️☁️☁️☁️☁️☁️☁️☁️☁️☁️☁️☁️☁️ ☁️☁️☁️…

Github实现push触发自动构建(包括提交代码到另一个仓库中和发布npm)

将指定内容自动更新到另一个代码仓库中 登录https://github.com&#xff0c;创建Personal access token&#xff1a; &#xff08;1&#xff09;github右上角&#xff0c;点击头像&#xff0c;进入Settings > Developer settings > Personal access tokens 下面的Tokens …

一名00后程序员初次使用低代码开发平台的感受

前言 大家好&#xff0c;我是小代&#xff0c;一名00后的Java后端开发工程师。我热爱编程&#xff0c;希望通过写博客来分享我的学习经验和心得。最近&#xff0c;我加入了一家专注于低代码开发平台的公司&#xff0c;这里的技术挑战和新颖性让我决定开始我的博客之旅。 工作…

3.1 SQL概述

SQL&#xff08;Structured Query Language&#xff09; 结构化查询语言&#xff0c;是关系数据库的标准语言 SQL是一个通用的、功能极强的关系数据库语言 功能&#xff1a;查询&#xff0c;数据库模式创建&#xff0c;数据库数据的插入与修改&#xff0c;数据库完整性、安全…

影响欧美货币对因素?fpmarkets提醒如何避免陷阱

欧元兑美元汇率不仅取决于美国经济数据&#xff0c;还取决于欧洲央行的报告。 美国经济数据不仅包括美联储利率和美国就业报告&#xff0c;还有美国石油储备、汽油储备和页岩油钻井平台数量的报告。fpmarkets发现更谨慎的投资者还会分析失业数据、平均时薪变化这些更细致的数据…

1.使用eNSP绘制拓扑图实训

1&#xff0e;实训目的 掌握使用eNSP绘制拓扑图的方法。 2&#xff0e;应用环境 某企业需要提升自己的网络环境&#xff0c;为了保证项目顺利的实施&#xff0c;需要使用eNSP事先绘制拓扑图。 3&#xff0e;实训设备 安装有eNSP的计算机。 4&#xff0e;实训内容 按照设…

矿场设备远程监控解决方案

矿场设备远程监控解决方案 在现代工业智能化进程中&#xff0c;针对矿场设备远程监控的解决方案显得尤为重要。这一方案旨在通过集成先进的信息技术与物联网技术&#xff0c;实现对矿场各类设备的实时、精准、高效的远程监控和管理&#xff0c;从而优化生产效率&#xff0c;降…

WHM中启用FTP功能

WHM面板上FTP功能默认是禁用的&#xff0c;我们在创建完cPanel账户之后&#xff0c;用到FTP去登录的时候会发现登录受限&#xff0c;这个时候可以去WHM面板上启用&#xff0c;这边以Hostease的独立服务器并且购买了cPanel面板为例&#xff0c;步骤如下&#xff1a; 1.先登陆WH…

what is apache?

Apache 通常指 Apache Software Foundation (ASF) 或 Apache HTTP Server&#xff0c;两者都是计算机软件领域的重要实体。 Apache 软件基金会 (ASF)&#xff1a;Apache 软件基金会是一个开发开源软件项目的非营利组织。它为涵盖软件开发各个方面的广泛项目提供支持&#xff0c…

01背包-动态规划

01背包 易知状态转移方程为&#xff1a; dp[i][j] max(dp[i-1][j],dp[i-1][j-v[i]]w[i]) 代码 N,V map(int,input().split()) v, w [0],[0] # 体积v&#xff0c;价值w for i in range(N):a list(map(int,input().split()))v.append(a[0]) # 体积viw.append(a[1]) # 价值w…

【牛客】【刷题节】美团2024届秋招笔试第一场编程真题

1.小美的外卖订单【简单题】 题意理解&#xff1a; 这道题是简单题&#xff0c;主要是一个逻辑实现和判断的问题。但是简单题一般喜欢加一点小障碍&#xff0c;所以读题的时候就要比较注意一些约束条件。就比如这道题&#xff1a;过了15/20个测试用例&#xff0c;出现error, 当…