八大排序算法之堆排序的实现+经典TopK问题

news2025/1/16 2:40:34

目录

一.堆元素的上下调整接口

1.前言

2.堆元素向上调整算法接口

3.堆元素向下调整算法接口

二.堆排序的实现

1.空间复杂度为O(N)的堆排序(以排升序为例)

思路分析:

代码实现:

排序测试:

​时空复杂度分析:

2. 空间复杂度为O(1)的堆排序(以排降序为例)

将数组arr调整成堆的思路:

将数组arr调整成堆的时间复杂度分析: ​

在数组arr数组被调整成堆的基础上完成排序的思路

堆排序代码实现:

排序时空复杂度分析:

三.用堆数据结构解决TopK问题

1. 问题描述:

 2.问题分析与求解

 


一.堆元素的上下调整接口

1.前言

完全二叉树的物理结构和逻辑结构:

关于堆和堆元素上下调整算法接口设计原理分析参见青菜的博客http://t.csdn.cn/MKzyticon-default.png?t=N176http://t.csdn.cn/MKzyt青菜友情提示:想要深刻理解堆排序,必须掌握堆的构建

注意:接下来给出的两个接口针对小根堆元素调整算法接口,若需要用到大根堆数据结构,只需在小根堆的元素调整算法接口中子父结点值比较符号换一下方向即可用于实现大根堆.

2.堆元素向上调整算法接口

函数首部:

void AdjustUp(HPDataType* arry, size_t child)  //child表示孩子结点的编号

HPDataType是typedef定义的数据类型,arry是指向堆区数组的指针,child是待调整的结点在完全二叉树中的编号(物理上是其数组下标)

  •  算法调用场景: 

接口实现:

//元素交换接口
void Swap(HPDataType* e1, HPDataType* e2)
{
	assert(e1 && e2);
	HPDataType tem = *e1;
	*e1 = *e2;
	*e2 = tem;
}



//小堆元素的向上调整接口
void AdjustUp(HPDataType* arry, size_t child)  //child表示待调整的结点的编号
{
	assert(arry);
	size_t parent = (child - 1) / 2;           //找到child结点的父结点
	while (child > 0)						   //child减小到0时则调整结束(说明待调整结点被调整到了根结点位置)
	{
		if (arry[child] < arry[parent])        //父结点大于子结点,则子结点需要上调以保持小堆的结构
		{
			Swap(arry + child, arry+parent);
			child = parent;				//将原父结点作为新的子结点继续迭代过程
			parent = (child - 1) / 2;	//继续向上找另外一个父结点
		}
		else
		{
			break;						//父结点不大于子结点,则堆结构任然成立,无需调整
		}
	}
}
  • 循环的结束分两种情况:
  1. child减小到0时,说明待调整结点调整到了根结点的位置(小根堆数据结构恢复)
  2. 某次父子结点比较中,父结点的值若大于子结点,则说明小根堆数据结构恢复,break跳出循环即可
  • 调用该接口的前提是:待调整的结点上层结构(包括待调整结点的所在层,但不包括待调整结点本身)满足小根堆的数据结构,比如:否则的话堆元素的调整将失去意义(因为只有在满足上述前提的情况下,每次调用完该接口,待调整的结点的上层结构保持小根堆的数据结构,并且以待调整结点为叶结点的上层结构会成为一个堆)
  • 大根堆的元素向上调整算法接口:
  • 若要将接口改为大根堆元素向上调整算法接口,只需将上图中的红圈中的小于号改为大于号即可

3.堆元素向下调整算法接口

函数首部:

void AdjustDown(HPDataType* arry,size_t size,size_t parent)

HPDataType是typedef定义的数据类型,arry是指向堆区数组首地址的指针,size是堆的元素总个数,parent是待调整的结点在完全二叉树中的编号(物理上是其数组下标)

  •  算法调用场景: 

接口实现:

//元素交换接口
void Swap(HPDataType* e1, HPDataType* e2)
{
	assert(e1 && e2);
	HPDataType tem = *e1;
	*e1 = *e2;
	*e2 = tem;
}

//小堆元素的向下调整接口
void AdjustDown(HPDataType* arry,size_t size,size_t parent)
{
	assert(arry);
	size_t child = 2 * parent + 1;   //确定父结点的左孩子的编号
	while (child < size)			 //child增加到大于或等于size时则调整结束
	{
		if (child + 1 < size && arry[child + 1] < arry[child]) //确定左右孩子中较小的孩子结点
		{
			++child;
		}
		if ( arry[child] < arry[parent])//父结点大于子结点,则子结点需要上调以保持小堆的结构
		{
			Swap(arry + parent, arry + child);
			parent = child;				//将原子结点作为新的父结点继续迭代过程
			child = 2 * parent + 1;		//继续向下找另外一个子结点
		}
		else
		{
			break;						//父结点不大于子结点,则堆结构任然成立,无需调整
		}
	}
}
  •  算法需要注意的一些边界条件:
  1. child >= size说明被调整元素已经被交换到了叶结点的位置,小根堆数据结构恢复,终止循环
  2. 接口中,我们只设计了一个child变量来表示当前父结点的孩子结点编号,因此我们需要先确定左右孩子中哪一个结点值较小,令child等于较小的孩子结点的编号:
    if (child + 1 < size && arry[child + 1] > arry[child]) //确定左右孩子中较小的孩子结点
    {
    	++child;
    }

    child + 1<size判断语句是为了确定当前父结点的右孩子是否存在

  • 调用该接口的前提是:待调整的结点位置的左右子树都满足小根堆的数据结构,比如:否则的话堆元素的调整将失去意义(因为只有在满足上述前提的情况下,每次调用完该接口后,待调整的结点位置的左右子树保持小根堆的数据结构,并且以待调整结点为根结点的子树会成为一个堆)

  • 大根堆的元素向下调整算法接口:

  • 若要实现大根堆的元素向下调整算法接口,我们只需将上图红圈中的两个小于号改为大于号即可 

堆元素上下调整算法接口的实现原理分析参见:http://t.csdn.cn/MKzyticon-default.png?t=N176http://t.csdn.cn/MKzyt

二.堆排序的实现

有了堆元素的上下调整算法接口后,我们便可以利用堆的数据结构来实现高效的排序算法.

现在我们给出一个一百个元素的数组(每个元素随机附一个值):

typedef int HPDataType;
int main()
{
	int arr[100] = { 0 };
	srand((unsigned int)time(NULL));
	for (int i = 0; i < 100; i++)
	{
		arr[i] = rand() % 10000;        //数组每个元素赋上一个随机值
	}
	
	return 0;
}

堆排序函数接口:

void HeapSort(int * arr,int size);

 arr是指向待排序数组首地址的指针,size是待排序的数组的元素个数

1.空间复杂度为O(N)的堆排序(以排升序为例)

思路分析:

  • 实现堆排序的其中一种非常暴力的思路是:
  1. 在HeapSort接口中动态开辟一个和待排序数组空间大小相同Heap数组作为
  2. 然后将待排序数组的元素逐个尾插到Heap数组中同时调用堆元素向上调整算法调整堆尾元素的位置来建堆(排升序则建立小根堆)
  3. 建堆过程完成后,再逐个取出堆顶数据(按照堆顶元素删除的方式取出,具体参见堆的实现http://t.csdn.cn/vhbJf)(堆顶数据为堆中的最小元素)从待排序数组首地址开始覆盖待排序数组的空间即可完成排序

排序算法图解:

  • 先将arr中的元素逐个尾插到Heap数组中建堆
  • 逐个Heap数组的堆顶元素利用堆顶元素删除操作放回到arr数组中,完成升序排序(其原理在于小根堆堆顶元素永远是堆中的最小元素)(堆顶元素删除操作指的是:先将堆顶元素与堆尾元素交换,维护堆尾的下标指针减一(堆元素个数减一),再将堆顶元素向下调整恢复小根堆数据结构):

代码实现:

//元素交换接口
void Swap(HPDataType* e1, HPDataType* e2)
{
	assert(e1 && e2);
	HPDataType tem = *e1;
	*e1 = *e2;
	*e2 = tem;
}



//小堆元素的向上调整接口
void AdjustUp(HPDataType* arry, size_t child)  //child表示待调整的结点的编号
{
	assert(arry);
	size_t parent = (child - 1) / 2;           //找到child结点的父结点
	while (child > 0)						   //child减小到0时则调整结束(说明待调整结点被调整到了根结点位置)
	{
		if (arry[child] < arry[parent])        //父结点大于子结点,则子结点需要上调以保持小堆的结构
		{
			Swap(arry + child, arry+parent);
			child = parent;				//将原父结点作为新的子结点继续迭代过程
			parent = (child - 1) / 2;	//继续向上找另外一个父结点
		}
		else
		{
			break;						//父结点不大于子结点,则堆结构任然成立,无需调整
		}
	}
}

//小堆元素的向下调整接口
void AdjustDown(HPDataType* arry,size_t size,size_t parent)
{
	assert(arry);
	size_t child = 2 * parent + 1;   //确定父结点的左孩子的编号
	while (child < size)			 //child增加到大于或等于size时则调整结束
	{
		if (child + 1 < size && arry[child + 1] < arry[child]) //确定左右孩子中较小的孩子结点
		{
			++child;
		}
		if ( arry[child] < arry[parent])//父结点大于子结点,则子结点需要上调以保持小堆的结构
		{
			Swap(arry + parent, arry + child);
			parent = child;				//将原子结点作为新的父结点继续迭代过程
			child = 2 * parent + 1;		//继续向下找另外一个子结点
		}
		else
		{
			break;						//父结点不大于子结点,则堆结构任然成立,无需调整
		}
	}
}


void HeapSort(int* arr, int size)
{
	assert(arr);
	int* Heap = (int*)malloc(size * sizeof(int));
	assert(Heap);
	int ptrarr = 0;		//维护arr数组的下标指针
	int ptrheap = 0;	//维护Heap数组的下标指针
	//逐个尾插元素建堆
	while (ptrarr < size)
	{
		Heap[ptrheap] = arr[ptrarr]; //将arr数组中的元素逐个尾插到Heap数组中
		AdjustUp(Heap, ptrheap);     //每尾插一个元素就将该元素向上调整保持小堆的数据结构
		ptrheap++;
		ptrarr++;

	}
	//逐个将堆顶的元素放回arr数组(同时进行删堆操作)
	ptrarr = 0;
	int HeapSize = size;
	while (ptrarr < size)
	{
		Swap(&Heap[0], &Heap[HeapSize - 1]);  //交换堆顶和堆尾的元素
		arr[ptrarr] = Heap[HeapSize-1];		  //将原堆顶元素插入arr数组中
		HeapSize--;                           //堆元素个数减一(完成堆数据弹出)
		ptrarr++;                             //维护arr的下标指针+1
		AdjustDown(Heap, HeapSize, 0);        //将交换到堆顶的数据向下调整恢复堆的数据结构
	}
}

排序测试:

int main()
{
	int arr[100] = { 0 };
	srand((unsigned int)time(NULL));
	for (int i = 0; i < 100; i++)
	{
		arr[i] = rand() % 10000;        //数组每个元素赋上一个随机值
	}

	HeapSort(arr, 100);
	for (int i = 0; i < 100; ++i)
	{
		printf("%d ", arr[i]);
	}
	
	return 0;
}

时空复杂度分析:

  • 由于尾插建堆堆顶删堆时间复杂度都是O(NlogN),因此排序的时间复杂度为O(NlogN)
  • 显然,在HeapSort接口中多开辟了一个Heap数组,排序的空间复杂度为O(N)
  • 关于建堆和删堆的时间复杂度证明参见青菜的博客:http://t.csdn.cn/MKzyticon-default.png?t=N176http://t.csdn.cn/MKzyt
  • 该种堆排序代码量很大,数据并发量也很大,而且空间复杂度较高,接下来我们来实现一种最优良的堆排序算法

2. 空间复杂度为O(1)的堆排序(以排降序为例)

前面的堆排序算法中引入了Heap数组来建堆,浪费了很多空间。

实际上,我们可以在待排序数组上原地完成堆的构建(即将数组arr调整成堆).

将数组arr调整成堆的思路:

  • 现有一个乱序数组arr,逻辑上我们将其看成一颗完全二叉树:
  • 接下来我们尝试用堆的元素向下调整算法接口将arr调整成小根堆 
  1. 调用堆元素向下调整接口的前提是:待调整的结点位置的左右子树都满足小根堆的数据结构(因为在满足这个前提的情况下,我们每次调用完该接口待调整的结点位置的左右子树保持小根堆的数据结构,并且以待调整结点为根结点的子树会成为一个堆)
  2. 由上述前提可知,如果从堆顶(或中间任意一个位置的结点)元素开始调整堆是没有意义的,所以我们只能从堆尾的子结构开始调堆:
  3. 通过上图的分析,我们可以通过堆尾元素找到第一个要被向下调整的结点,然后从第一个要被向下调整的结点开始依次往前向下调整其他结点直到完成对树的根结点的向下调整之后,整颗完全二叉树就会被调整成堆:
  4. 调堆小动画:
  • 实现将arr数组调整成小根堆的代码:
    void HeapSort(int* arr, int size)
    {
    	assert(arr);
    	int parent = (size - 1 - 1) / 2;	//找到第一个要被调整向下调整的元素
    	for (; parent >= 0; --parent)
    	{
    		AdjustDown(arr, size, parent);  //逐个元素向下调整完成堆的构建
    	}
    }

将数组arr调整成堆的时间复杂度分析: 

因此假设arr数组中有N个元素,将数组arr调整成堆的时间复杂度为:O(N)

在数组arr数组被调整成堆的基础上完成排序的思路

  • 数组arr被调整成小根堆后,我们只需逐个删除堆顶元素就可以完成所有数的降序排序
  • 堆元素删除操作指的是:先将堆顶元素与堆尾元素交换,维护堆尾的下标指针减一(堆元素个数减一),再将堆顶元素向下调整恢复小根堆数据结构)
  • 逐个删除堆顶元素完成降序排序的过程图解:
  •  由上述算法设计思路可知:为了完成堆排序我们只需额外设计一个堆元素向下调整接口

堆排序代码实现:

//元素交换接口
void Swap(HPDataType* e1, HPDataType* e2)
{
	assert(e1 && e2);
	HPDataType tem = *e1;
	*e1 = *e2;
	*e2 = tem;
}

//小堆元素的向下调整接口
void AdjustDown(HPDataType* arry,size_t size,size_t parent)
{
	assert(arry);
	size_t child = 2 * parent + 1;   //确定父结点的左孩子的编号
	while (child < size)			 //child增加到大于或等于size时则调整结束
	{
		if (child + 1 < size && arry[child + 1] < arry[child]) //确定左右孩子中较小的孩子结点
		{
			++child;
		}
		if ( arry[child] < arry[parent])//父结点大于子结点,则子结点需要上调以保持小堆的结构
		{
			Swap(arry + parent, arry + child);
			parent = child;				//将原子结点作为新的父结点继续迭代过程
			child = 2 * parent + 1;		//继续向下找另外一个子结点
		}
		else
		{
			break;						//父结点不大于子结点,则堆结构任然成立,无需调整
		}
	}
}

void HeapSort(int* arr, int size)
{
	assert(arr);
	int parent = (size - 1 - 1) / 2;	//找到第一个要被调整向下调整的元素
	for (; parent >= 0; --parent)
	{
		AdjustDown(arr, size, parent);  //逐个元素向下调整完成堆的构建
	}

	while (size > 0)					//逐个删除堆顶元素完成降序排序,我们将size作为堆尾指针
	{
		Swap(&arr[0], &arr[size - 1]);  //交换堆尾与堆顶元素
		size--;							//堆尾指针减一,堆元素个数减一
		AdjustDown(arr, size, 0);       //将堆顶元素向下调整恢复小根堆数据结构
	}
}

排序接口测试: 

int main()
{
	int arr[100] = { 0 };
	srand((unsigned int)time(NULL));
	for (int i = 0; i < 100; i++)
	{
		arr[i] = rand() % 10000;      //数组每个元素赋上一个随机值
	}

	HeapSort(arr, 100);
	for (int i = 0; i < 100; ++i)
	{
		printf("%d ", arr[i]);
	}
	
	return 0;
}

排序时空复杂度分析:

  • 逐个删除堆顶元素直到将堆删空的时间复杂度为O(NlogN),证明分析参见青菜的博客:http://t.csdn.cn/vhbJficon-default.png?t=N176http://t.csdn.cn/vhbJf
  • 已知将arr数组调整成堆的时间复杂度为O(N),因此堆排序整体的时间复杂度为O(NlogN)
  • 同时易知,堆排序算法的空间复杂度为O(1)
  • 可见堆排序是一个非常高效的排序算法(至少比冒泡厉害多了)

三.用堆数据结构解决TopK问题

TopK问题指的是,从N个元素数组中,选出K个最值.(K<=N)

Leetcode上面有相关题型.

面试题 17.14. 最小K个数 - 力扣(Leetcode) 

1. 问题描述:

设计一个算法,找出数组中最小的k个数。以任意顺序返回这k个数均可。(数组元素个数为arrSize)

(k<=arrSize)

示例:

输入: arr = [1,3,5,7,2,4,6,8], k = 4
输出: [1,2,3,4]

题解接口: 

int* smallestK(int* arr, int arrSize, int k, int* returnSize)
{

}

arrSize为题设数组的元素个数,k为要找出的最小数的个数,returnSize是结果数组元素个数

 2.问题分析与求解

  • 本题如果直接对arr数组进行排序理论上是可以解决的,但是时间效率略低(O(NlogN)),有种杀鸡用牛刀的感觉
  • 我们可以考虑利用堆数据结构实现本题的最优解之一:
  1. 首先创建一个k*sizeof(int)字节大小数组Heap用于存储堆
  2. 然后将arr中前k个元素尾插到Heap中建堆
  3. 然后将arr中后(arrSize-k)个元素逐个与Heap堆顶的元素比较,若arr中后(arrSize-k)个元素中的某元素小于Heap堆顶的元素,则将其与Heap堆顶元素交换,再将其进行向下调整保持堆的数据结构(元素交换入堆)
  4. 完成arr中后(arrSize-k)个元素Heap堆顶遍历比较后,堆中最后剩下的就是arr数组中最小的k个元素

算法图解: 

算法的合理性证明:

  • 由于大根堆堆顶元素是堆中的最大元素,因此在arr中后(arrSize-k)个元素Heap堆顶遍历比较的过程中没有入堆的元素一定都大于堆中的k个元素,因此最终堆中的k个元素一定是arr数组中最小的k个元素

题解代码:

void Swap(int* e1 ,int* e2)
{
    int tem = *e1;
    *e1 = *e2;
    *e2 = tem;
}

//大堆元素的向上调整接口
void AdjustUp(int * arry, size_t child)     //child表示待调整结点的编号
{
	assert(arry);
	size_t parent = (child - 1) / 2;
	while (child > 0)						//child减小到0时则调整结束
	{
		if (arry[child] > arry[parent])     //父结点小于子结点,则子结点需要上调以保持大堆的结构
		{
			Swap(arry + child, arry+parent);
			child = parent;				    //将原父结点作为新的子结点继续迭代过程
			parent = (child - 1) / 2;	    //继续向上找另外一个父结点
		}
		else
		{
			break;						    //父结点不小于子结点,则堆结构任然成立,无需调整
		}
	}
}


//大堆元素的向下调整接口
void AdjustDown(int * arry,size_t size,size_t parent)
{
	assert(arry);
	size_t child = 2 * parent + 1;   //确定父结点的左孩子的编号
	while (child < size)			 //child增加到大于或等于size时则调整结束
	{
		if (child + 1 < size && arry[child + 1] > arry[child]) //确定左右孩子中较大的孩子结点
		{
			++child;
		}
		if ( arry[child] > arry[parent])//父结点小于子结点,则子结点需要上调以保持大堆的结构
		{
			Swap(arry + parent, arry + child);
			parent = child;				//将原子结点作为新的父结点继续迭代过程
			child = 2 * parent + 1;		//继续向下找另外一个子结点
		}
		else
		{
			break;						//父结点不小于子结点,则堆结构任然成立,无需调整
		}
	}
}

int* smallestK(int* arr, int arrSize, int k, int* returnSize)
{
    if(0==k)
    {
        *returnSize =0;
        return NULL;
    }
    int * Heap = (int*)malloc(k*sizeof(int));
    *returnSize = k;                     //创建一个空间大小为k的数组用于存储堆
    int ptrHeap =0;                      //维护堆尾的指针
    while(ptrHeap<k)                     //将arr数组前k个元素尾插到Heap中完成建堆
    {
        Heap[ptrHeap]=arr[ptrHeap];
        AdjustUp(Heap,ptrHeap);    
        ptrHeap++;
    }


    int ptrarr = k;             //用于遍历arr中后(arrSize-k)个元素的下标指针
    while(ptrarr < arrSize)     //将arr中后(arrSize-k)个元素逐个与Heap堆顶的元素进行比较
    {
        //如果找到arr中后(arrSize-k)个元素中比堆顶元素小的元素则将该元素替换入堆
        //并通过堆元素向下调整接口保持大根堆的数据结构
        if(Heap[0]>arr[ptrarr])
        {
            Swap(&Heap[0],&arr[ptrarr]);
            AdjustDown(Heap,k,0);
        }
        ptrarr++;
    }
    return Heap;                  //返回Heap数组作为及结果
}

算法时空复杂度分析:

设数组arr元素个数为N

  • 建立Heap数组堆的时间复杂度为O(klogk)
  • arr后(N-k)个元素与heap堆顶元素比较并入堆的时间复杂度为O((N-k)logk)(在最坏的情况下,arr后(N-k)个元素每个都进行了交换入堆并且被调整到了堆的叶子结点位置)
  • 因此算法的总体时间复杂度为O(Nlogk)
  • 易知算法的空间复杂度为O(k)

TopK问题的求解思想有着十分重要的实际意义

比如在硬盘中有十亿个数据,我们想选出其中的100个最小值,那么利用上面的算法思想我们就可以在极少的内存消耗,极高的时间效率下完成这个工作.

 

 

 

 

 

 

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

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

相关文章

IGKBoard(imx6ull)-SPI接口编程-回环测试

文章目录1- 使能imx6ull开发板SPI驱动2- 回环测试imx6ull开发板物理连接3- 编程SPI回环测试4- 代码重难点分析&#xff08;1&#xff09;spi_device结构体&#xff08;2&#xff09;spi_ioc_transfer结构体&#xff08;3&#xff09;ioctl函数对于SIP不了解的可以参考这篇文章&…

GVRP-LNP-VCMP讲解

目录 GVRP讲解 动态创建Vlan并将端口加入Vlan GVRP消息类型 GVRP工作原理 LNP讲解 动态修改接口链路类型 VCMP讲解 动态创建Vlan 相关概念 Vlan同步 VCMP与GVRP的区别 GVRP讲解 动态创建Vlan并将端口加入Vlan GVRP&#xff08;GARR Vlan Registration Protocol&#xf…

28个案例问题分析---01---redis没有及时更新问题--Redis

redis没有及时更新问题一&#xff1a;背景介绍二&#xff1a;前期准备pom依赖连接Redis工具类连接mysql工具类三&#xff1a;过程使用redis缓存&#xff0c;缓存用户年龄业务对应流程图使用redis缓存用户年龄对应代码四&#xff1a;总结一&#xff1a;背景介绍 业务中使用redis…

【机器学习面试】百面机器学习笔记和问题总结+扩展面试题

第1章 特征工程 1、为什么需要对数值类型的特征做归一化&#xff1f; &#xff08;1&#xff09;消除量纲&#xff0c;将所有特征统一到一个大致相同的区间范围&#xff0c;使不同指标之间具由可比性&#xff1b; &#xff08;2&#xff09;可以加快梯度下降收敛的速度&#…

powershell-dns-txt远程加载

2022-10-30 参考原文&#xff1a; 远程下载的通用替代方案 &#xff5c; 红队攻防 https://mp.weixin.qq.com/s/9MAKZZfNB5YFT7jgln5lXQ实现过程 dns环境&#xff1a;kali bind9&#xff08;docker版&#xff09;&#xff0c;ip&#xff1a;192.168.161.128 靶机&#xff…

AD封装转Allego Cadence

AD封装转Allego CadenceAD封装转Allego Cadence软件版本转换步骤导出AD文件在PADS导入AD在cadence导入PADS在cadence导出library修改焊盘替换焊盘AD封装转Allego Cadence 有时候在网上下载的封装是AD格式的&#xff0c;但内容实在太多&#xff0c;为了快速便捷获得cadence格式…

梯度提升算法决策过程的逐步可视化

梯度提升算法是最常用的集成机器学习技术之一&#xff0c;该模型使用弱决策树序列来构建强学习器。这也是XGBoost和LightGBM模型的理论基础&#xff0c;所以在这篇文章中&#xff0c;我们将从头开始构建一个梯度增强模型并将其可视化。 梯度提升算法介绍 梯度提升算法&#x…

【VC 7/8】vCenter Server 基于文件的备份和还原Ⅰ——基于文件的备份和还原的注意事项和限制

目录1.1 协议1.2 还原后配置说明1.3 Storage DRS1.4 分布式电源管理1.5 分布式虚拟交换机1.6 内容库1.7 虚拟机生命周期操作1.8 vSphere High Availability1.9 基于存储策略的管理1.10 其它注意事项虚拟存储区域网络修补关联博文[图片来源]&#xff1a;https://www.vmignite.co…

ARM uboot 源码分析9 - uboot的硬件驱动部分

一、uboot 与 linux 驱动 1、uboot 本身是裸机程序 (1) 裸机本来是没有驱动的概念的&#xff08;狭义的驱动的概念就是&#xff0c;操作系统中用来具体操控硬件的那部分代码叫驱动&#xff09; (2) 裸机程序中是直接操控硬件的&#xff0c;操作系统中必须通过驱动来操控硬件…

Java 8 新特性之Stream流(二)关键

继续探索流API的高级功能之前&#xff0c;我们先从接口级别全面了解一下流API&#xff0c;这个对于我们来说是至关重要的。下面是一张流API关键知识点的UML图。 流API UML 流API定义的几个接口&#xff0c;都是在java.util.stream包中的.其中上图中的BaseStream接口是最基础的…

每日记录自己的Android项目(二)—Viewbinding,WebView,Navigation

今日想法今天是想把做一个跳转页面的时候调到H5页面去&#xff0c;但是这个页面我用app来承载&#xff0c;不要调到浏览器去。所以用到了下方三个东西。Viewbindingbuild.gradle配置首先在app模块的build.gradle里添加一下代码默认情况下&#xff0c;每一个布局xml文件都会生成…

【Linux学习】基础IO——理解缓冲区 | 理解文件系统

&#x1f431;作者&#xff1a;一只大喵咪1201 &#x1f431;专栏&#xff1a;《Linux学习》 &#x1f525;格言&#xff1a;你只管努力&#xff0c;剩下的交给时间&#xff01; 基础IO☕理解缓冲区&#x1f9c3;缓冲区的共识&#x1f9c3;缓冲区的位置&#x1f9c3;缓冲区的刷…

Spring Boot+Vue前后端分离项目练习03之网盘项目文件夹创建及文件查询接口开发

1.集成Swagger 3接口文档 在前后端分离的项目中&#xff0c;接口文档的存在十分重要。swagger 是一个自动生成接口文档的工具&#xff0c;在需求变更十分频繁的情况下&#xff0c;手写接口文档是效率十分低下&#xff0c;这时swagger自动生生文档的的作用就体现出来了&#xf…

【uni-app教程】UniAPP 常用组件和 常用 API 简介# 知心姐姐聊天案例

五、UniAPP 常用组件简介 uni-app 为开发者提供了一系列基础组件&#xff0c;类似 HTML 里的基础标签元素&#xff0c;但 uni-app 的组件与 HTML 不同&#xff0c;而是与小程序相同&#xff0c;更适合手机端使用。 虽然不推荐使用 HTML 标签&#xff0c;但实际上如果开发者写了…

华为机试题:HJ105 记负均正II(python)

文章目录&#xff08;1&#xff09;题目描述&#xff08;2&#xff09;Python3实现&#xff08;3&#xff09;知识点详解1、input()&#xff1a;获取控制台&#xff08;任意形式&#xff09;的输入。输出均为字符串类型。1.1、input() 与 list(input()) 的区别、及其相互转换方…

【Kubernetes】第十七篇 - ECS 服务停机和环境修复

一&#xff0c;前言 上一篇&#xff0c;介绍了 Secret 镜像的使用&#xff1b; 三台服务每天大概 15 块钱的支出&#xff0c;用一个月也是不少钱&#xff1b; 闲时可以停掉&#xff0c;这样每天只有 4 块钱支出&#xff0c;剩下一大笔&#xff1b; ECS 服务停机后公网 IP 会…

移除元素(每日一题)

目录 一、题目描述 二、题目分析 2.1 方法一 2.1.1 思路 2.1.2 代码 2.2 方法二 2.2.1 思路 2.2.2 代码 一、题目描述 题目链接&#xff1a;27. 移除元素 - 力扣&#xff08;LeetCode&#xff09; 给你一个数组 nums 和一个值 val&#xff0c;你需要 原地 移除所有数…

【Maven】P1 Maven 基础知识

Maven 基础知识Maven基础仓库坐标快速坐标生成网站国内镜像仓库前言 本节&#xff1a;Maven第一节内容&#xff0c;记录maven是什么&#xff0c;解决了什么问题&#xff0c;进而推出他的作用&#xff1b;然后介绍maven中两个重要概念&#xff0c;仓库与坐标。 下一节&#xff1…

TIA博途中使用SCL语言实现选择排序算法并封装成FC全局库

TIA博途中使用SCL语言实现选择排序算法并封装成FC全局库 选择排序算法包括升序和降序2种: 升序排列: 第一轮从数据源中找到最小值排在第一位,第二轮从剩下的数据中寻找最小值排在第二位,依次类推,直到所有数据完成遍历;降序排列: 第一轮从数据源中找到最大值排在第一位,…

centOS 编译strongswan

安装编译环境 yum groupinstall "Development Tools" 编译strongswan Download strongSwan: wget https://download.strongswan.org/strongswan-x.x.x.tar.bz2 Unpack the tarball and navigate into the directory: tar xjf strongswan-x.x.x.tar.bz2; cd strong…