数据结构10:堆和堆排序

news2024/9/21 4:23:12

文章目录

  • 树的概念及结构
    • 树的概念
    • 树的相关概念
    • 树的表示
    • 树在实际中的应用
      • 表示文件系统的目录树结构
  • 二叉树概念及结构
    • 概念
    • 特殊的二叉树
    • 二叉树的性质
    • 二叉树的存储结构
    • 顺序存储
    • 链式存储
  • 二叉树的顺序结构及实现
    • 二叉树的顺序结构
    • 堆的概念及结构
  • 堆的实现
    • 堆的插入
    • 堆的删除
    • 堆的创建
      • 向上调整法
      • 向下调整法
    • 堆的销毁
    • 取堆顶的数据
    • 堆的数据个数
    • 堆的判空
  • 堆的应用
    • 堆排序
    • TOP-k问题
  • 堆的实现参考代码
    • 头文件
      • Heap.h
    • 实现文件
      • Heap.c
    • 测试文件
      • test.c

树的概念及结构

树的概念

树是一种非线性的数据结构,它是由n(n>=0)个有限结点组成一个具有层次关系的集合。把它叫做树是因为它看起来像一棵倒挂的树,也就是说它是根朝上,而叶朝下的。

有一个特殊的结点,称为根结点,根节点没有前驱结点。
除根节点外,其余结点被分成M(M>0)个互不相交的集合T1、T2、……、Tm,其中每一个集合Ti(1<= i <= m)又是一棵结构与树类似的子树。每棵子树的根结点有且只有一个前驱,可以有0个或多个后继。因此,树是递归定义的

二叉树
注意:树形结构中,子树之间不能有交集,否则就不是树形结构
树与非树

树的相关概念

在这里插入图片描述
节点的度:一个节点含有的子树的个数称为该节点的度; 如上图:A的为6
叶节点或终端节点:度为0的节点称为叶节点; 如上图:B、C、H、I…等节点为叶节点
非终端节点或分支节点:度不为0的节点; 如上图:D、E、F、G…等节点为分支节点
双亲节点或父节点:若一个节点含有子节点,则这个节点称为其子节点的父节点; 如上图:A是B的父节点
孩子节点或子节点:一个节点含有的子树的根节点称为该节点的子节点; 如上图:B是A的孩子节点
兄弟节点:具有相同父节点的节点互称为兄弟节点; 如上图:B、C是兄弟节点
树的度:一棵树中,最大的节点的度称为树的度; 如上图:树的度为6
节点的层次:从根开始定义起,根为第1层,根的子节点为第2层,以此类推;
树的高度或深度:树中节点的最大层次; 如上图:树的高度为4
堂兄弟节点:双亲在同一层的节点互为堂兄弟;如上图:H、I互为兄弟节点
节点的祖先:从根到该节点所经分支上的所有节点;如上图:A是所有节点的祖先
子孙:以某节点为根的子树中任一节点都称为该节点的子孙。如上图:所有节点都是A的子孙
森林:由m(m>0)棵互不相交的树的集合称为森林;

树的表示

树结构相对线性表就比较复杂了,要存储表示起来就比较麻烦了,既要保存值域,也要保存结点和结点之间的关系,实际中树有很多种表示方式如:双亲表示法,孩子表示法、孩子双亲表示法以及孩子兄弟表示法等。我们这里就简单的了解其中最常用的孩子兄弟表示法

typedef int DataType;
struct Node
{
	struct Node* _firstChild1;    //第一个孩子结点
	struct Node* _pNextBrother;   //下一个兄弟节点
	DataType _data;               //数据域
};

在这里插入图片描述

树在实际中的应用

表示文件系统的目录树结构

在这里插入图片描述

二叉树概念及结构

概念

一棵二叉树是结点的一个有限集合,该集合:

  1. 或者为空
  2. 或者由一个根节点加上两棵别称为左子树和右子树的二叉树组成

在这里插入图片描述
从上图可以上出:

  1. 二叉树不存在度大于2的结点
  2. 二叉树的子树有左右之分,次序不能颠倒,因此二叉树是有序树

注意:对于任意的二叉树都是由以下几种情况复合而成的:
在这里插入图片描述

特殊的二叉树

  1. 满二叉树:一个二叉树,如果每一个层的结点数都达到最大值,则这个二叉树就是满二叉树。也就是说,如果一个二叉树的层数为K,且结点总数是(2k-1),则它就是满二叉树。
  2. 完全二叉树:完全二叉树是效率很高的数据结构,完全二叉树是由满二叉树而引出来的。对于深度为K的,有n个结点的二叉树,当且仅当其每一个结点都与深度为K的满二叉树中编号从1至n的结点一一对应时称之为完全二叉树。 要注意的是满二叉树是一种特殊的完全二叉树

在这里插入图片描述

二叉树的性质

  1. 若规定根节点的层数为1,则一棵非空二叉树的第i层上最多有2(i-1)个结点.
  2. 若规定根节点的层数为1,则深度为h的二叉树的最大结点数是2h-1.
  3. 对任何一棵二叉树, 如果度为0其叶结点个数为n0,度为2的分支结点个数为 n2,则有:n0=n2+1.
  4. 若规定根节点的层数为1,具有n个结点的满二叉树的深度,h=log2(n+1)
  5. 对于具有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,则无右孩子

二叉树的存储结构

二叉树一般可以使用两种结构存储,一种顺序结构,一种链式结构

顺序存储

顺序结构存储就是使用数组来存储,一般使用数组只适合表示完全二叉树,因为不是完全二叉树会有空间的浪费。而现实中使用中只有才会使用数组来存储。二叉树顺序存储在物理上是一个数组,在逻辑上是一颗二叉树
在这里插入图片描述

链式存储

二叉树的链式存储结构是指,用链表来表示一棵二叉树,即用指针来指示元素的逻辑关系。 通常的方法是链表中每个结点由三个域组成,数据域和左右指针域,左右指针分别用来给出该结点左孩子和右孩子所在的链结点的存储地址 。链式结构又分为二叉链和三叉链。
在这里插入图片描述
在这里插入图片描述

typedef int BTDataType;

// 二叉链
struct BinaryTreeNode
{
	struct BinTreeNode* _pLeft;  // 指向当前节点左孩子
	struct BinTreeNode* _pRight; // 指向当前节点右孩子
	BTDataType _data;            // 当前节点值域
}

 // 三叉链
struct BinaryTreeNode
{
	struct BinTreeNode* _pParent; // 指向当前节点的双亲
	struct BinTreeNode* _pLeft;   // 指向当前节点左孩子
	struct BinTreeNode* _pRight;  // 指向当前节点右孩子
	BTDataType _data; 			  // 当前节点值域
}

二叉树的顺序结构及实现

二叉树的顺序结构

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

在这里插入图片描述

堆的概念及结构

堆分为大堆(或称大根堆)和小堆(或称小根堆),大堆就是堆中的每一个结点都满足:父节点大于或等于他的两个子节点;小堆就是堆中的每一个结点都满足:父节点小于或等于他的两个子节点。

堆的性质:

  1. 堆中某个节点的值总是不大于或不小于其父节点的值
  2. 堆总是一棵完全二叉树

堆的实现

堆的插入

首先来看如何在堆中插入数据。假设我们现在有一个大堆,其结构如下,我们想在堆中插入一个数据,数据大小为48,该如何操作呢?
在这里插入图片描述
首先我们使用了数组来存储堆的数据,那么我们的插入操作最方便的就是尾插,所以我们首先将48尾插到数组的最后,那么插入之后的逻辑结构表示为:
在这里插入图片描述
很显然,我们插入数据后破坏了堆,我们接下来要调整48的位置,使其恢复成堆。我们可以将48和他的父节点进行比较,如果48比父节点大,则将他们调换位置,这样他们两个结点就满足了堆的结构
在这里插入图片描述
然后将48继续和他新的父节点进行比较,如果48比父节点大,则将他们调换位置
在这里插入图片描述

可以保证的是,所有和48交换过的结点是仍可以满足堆的结构的,因为48只是在寻找合适的位置插入,并没有改变他们之间的相对位置(如经过上述两次操作后的45和25,45是25的父节点,满足大堆的要求,48并没有将25改变为45的父节点),而且经过如此循环的操作,直到48小于等于他的父节点或48已经成为了根节点,则停止循环。这样我们就将48成功插入到了堆中。

这种将结点逐步向上调整寻找位置的方式称为:堆的向上调整。

代码实现如下:

// 调整堆的大小
void CheckCapacity(Heap* hp)
{
	assert(hp);

	if (hp->size == hp->capacity)
	{
		hp->capacity *= 2;
		HeapDataType* new = (HeapDataType*)realloc(new, sizeof(HeapDataType) * hp->capacity);
		if (new == NULL)
		{
			perror("malloc fail");
			return;
		}
		hp->a = new;
	}
}

//交换数据
void Swap(HeapDataType* x, HeapDataType* y)
{
	HeapDataType tmp = *x;
	*x = *y;
	*y = tmp;
}

// 堆的向上调整
void AdjustUp(HeapDataType* a, int child)
{
	assert(a);

	int parent = (child - 1) / 2;
	while (child)
	{
		if (a[child] > a[parent])
		{
			Swap(&a[child], &a[parent]);
			child = parent;
			parent = (child - 1) / 2;
		}
		else
		{
			break;
		}
	}
}

// 堆的插入
void HeapPush(Heap* hp, HeapDataType x)
{
	assert(hp);

	CheckCapacity(hp);

	hp->a[hp->size] = x;
	AdjustUp(hp->a, hp->size);
	hp->size++;
}

堆的删除

堆的删除,首先我们要确定删除的是那个元素,如果删除的是数组最后的元素,直接hp->size--就可以了,而且并没有什么实际意义,所以我们讲的堆的删除,一般是指删除堆顶元素。

想实现堆的删除,我们首先将堆顶元素和数组的最后一个元素交换位置,然后hp->szie--,使最后的50成为无效数据,就实现了删除的操作。
在这里插入图片描述
但很显然,我们这样也破坏了堆的结构,所以接下来我们要调整15的位置,恢复堆的结构。

首先让15的两个子结点(如果都存在)比较大小,选出大的子结点,和他们的父节点,也就是15,比较大小,如果父节点的值小于子节点的值,就将父节点和子节点的值交换
在这里插入图片描述
然后重复上述操作,即可将15置于合适的位置。
在这里插入图片描述
这种将结点逐步向下调整寻找位置的方式称为:堆的向下调整。

代码实现如下:

// 堆的向下调整
void AdjustDown(HeapDataType* a, int n, int parent)
{
	assert(a);
	assert(!HeapEmpty(a));

	int child = parent * 2 + 1;
	while (child < n)
	{
		if (child + 1 < n && a[child + 1] > a[child])
		{
			child++;
		}

		if (a[parent] < a[child])
		{
			Swap(&a[parent], &a[child]);
			parent = child;
			child = child * 2 + 1;
		}
		else
		{
			break;
		}
	}
}

堆的创建

向上调整法

有了以上的向上调整函数以后,我们很容易就可以想到创建堆的方式:从数组的第二个元素开始,一直到最后一个元素,挨个使用向上调调整法,同时使ph->size++,这样可以将数组调整为堆。

代码实现如下:

void HeapCreate(Heap* hp, HeapDataType* a, int n)
{
	assert(hp);

	//将数组a中的数据调整为堆
	for (int i = 1; i < n; i++)
	{
		AdjustUp(a, i);
	}

	//判断创建数组的大小
	hp->capacity = 4;
	while (hp->capacity < n)
	{
		hp->capacity *= 2;
	}

	//动态内存开辟
	HeapDataType* new = (HeapDataType*)malloc(sizeof(HeapDataType) * hp->capacity);
	if (new == NULL)
	{
		perror("malloc fail");
		return;
	}
	hp->a = new;
	hp->size = n;

	//将给定数组中的值拷贝的hp->a中
	for (int i = 0; i < n; i++)
	{
		hp->a[i] = a[i];
	}
}

向下调整法

除了最容易想到的向上调整法,还可以使用向下调整法创建堆。对于向上调整法和向下调整法,他们的应用前提都是在原始数据已经构成堆的前提下,将新数据插入堆,所以向下插入法并不能直接对数组的第一个元素使用,因为其余元素并不构成堆。那么我们可以从后向前使用向下调整法,因为叶结点是堆(只有一个结点,左右子树都是NULL),所以我们要找到从后向前除了叶结点之外的第一个结点。我们知道数组的元素个数为n,那么数组中最后一个元素的下标为n - 1,它的父结点的下标为(n - 1 - 1) / 2,只要从该结点向前对每个结点依次向下调整,即可把数组调整为堆。
代码实现如下:

void HeapCreate(Heap* hp, HeapDataType* a, int n)
{
	assert(hp);

	//将数组a中的数据调整为堆
	for (int i = (n - 1 - 1) / 2; i >= 0; i--)
	{
		AdjustDown(a, n, i);
	}
	
	//判断创建数组的大小
	hp->capacity = 4;
	while (hp->capacity < n)
	{
		hp->capacity *= 2;
	}

	//动态内存开辟
	HeapDataType* new = (HeapDataType*)malloc(sizeof(HeapDataType) * hp->capacity);
	if (new == NULL)
	{
		perror("malloc fail");
		return;
	}
	hp->a = new;
	hp->size = n;

	//将给定数组中的值拷贝的hp->a中
	for (int i = 0; i < n; i++)
	{
		hp->a[i] = a[i];
	}
}

堆的销毁

堆的销毁是要销毁动态开辟的空间,也就是hp->a,然后将其置空,将hp->sizehp->capacity赋值为0。
代码实现如下:

void HeapDestroy(Heap* hp)
{
    assert(hp);
    
    free(hp->a);
	hp->a = NULL;
	hp->capacity = 0;
	hp->size = 0;
}

取堆顶的数据

HeapDataType HeapTop(Heap* hp)
{
	assert(hp);
	assert(!HeapEmpty(hp));

	return hp->a[0];
}

堆的数据个数

int HeapSize(Heap* hp)
{
	assert(hp);

	return hp->size;
}

堆的判空

bool HeapEmpty(Heap* hp)
{
	assert(hp);

	return hp->size == 0;
}

堆的应用

堆排序

如果想排升序,就创建大堆,然后将首元素和尾元素交换,同时hp->size--,然后重复上述过程,即可将堆中元素排为升序。
代码如下:

void HeapSort(int* a, int n)
{
	assert(a);

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

	int end = n - 1;
	while (end)
	{
		Swap(&(a[0]), &(a[end]));
		end--;
		AdjustDown(a, end, 0);
	}
}

int main()
{
	int arr[10] = { 2,4,7,1,4,6,3,7,9,0 };


	HeapSort(arr, 10);

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

	return 0;
}

TOP-k问题

如果要非常多数据中选出top-k个数据,这时对所有数据排序就显得不太现实,这时我们可以应用堆来选出top-k个数据。
如果想找出最大的k个元素,可以对数组的前k个元素建小堆,然后其他元素分别与堆顶元素比较,如果比堆顶元素大,则交换两个元素,并向下调整堆顶元素,然后判断下一个元素。
代码如下:

//Top-k
void top_k(int* a, int n, int k)
{
	assert(a);

	//前k个元素建堆
	for (int i = (k - 1 - 1) / 2; i >= 0; i--)
	{
		//这里的向下调整要更换为建小堆
		AdjustDown(a, k, i);
	}

	for (int i = k; i < n; i++)
	{
		if (a[i] > a[0])
		{
			Swap(&a[i], &a[0]);
			AdjustDown(a, k, 0);
		}
	}
}

int main()
{
    int n = 10000;
    int* a = (int*)malloc(sizeof(int) * n);
    srand(time(0));
    for (size_t i = 0; i < n; ++i)
    {
        a[i] = rand() % 1000000;
    }
    a[5] = 1000000 + 1;
    a[1231] = 1000000 + 2;
    a[531] = 1000000 + 3;
    a[5121] = 1000000 + 4;
    a[115] = 1000000 + 5;
    a[2335] = 1000000 + 6;
    a[9999] = 1000000 + 7;
    a[76] = 1000000 + 8;
    a[423] = 1000000 + 9;
    a[3144] = 1000000 + 10;

    top_k(a, 10000, 10);

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

    free(a);
    a = NULL;

    return 0;
}

堆的实现参考代码

头文件

Heap.h

#pragma once

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

typedef int HeapDataType;

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

// 堆的构建
void HeapCreate(Heap* hp, HeapDataType* a, int n);
// 堆的销毁
void HeapDestory(Heap* hp);
// 堆的插入
void HeapPush(Heap* hp, HeapDataType x);
// 堆的删除
void HeapPop(Heap* hp);
// 取堆顶的数据
HeapDataType HeapTop(Heap* hp);
// 堆的数据个数
int HeapSize(Heap* hp);
// 堆的判空
bool HeapEmpty(Heap* hp);
// 堆的向上调整
void AdjustUp(HeapDataType* a, int child);
// 堆的向下调整
void AdjustDown(HeapDataType* a, int n, int parent);

实现文件

Heap.c

#define _CRT_SECURE_NO_WARNINGS 1

#include"Heap.h"

// 堆的构建
void HeapCreate(Heap* hp, HeapDataType* a, int n)
{
	assert(hp);

	//将数组a中的数据调整为堆
	//1. 向下调整法
	for (int i = (n - 1 - 1) / 2; i >= 0; i--)
	{
		AdjustDown(a, n, i);
	}
	//2. 向上调整法
	//for (int i = 1; i < n; i++)
	//{
	//	AdjustUp(a, i);
	//}

	//判断创建数组的大小
	hp->capacity = 4;
	while (hp->capacity < n)
	{
		hp->capacity *= 2;
	}

	//动态内存开辟
	HeapDataType* new = (HeapDataType*)malloc(sizeof(HeapDataType) * hp->capacity);
	if (new == NULL)
	{
		perror("malloc fail");
		return;
	}
	hp->a = new;
	hp->size = n;

	//将给定数组中的值拷贝的hp->a中
	for (int i = 0; i < n; i++)
	{
		hp->a[i] = a[i];
	}
}

//交换数据
void Swap(HeapDataType* x, HeapDataType* y)
{
	HeapDataType tmp = *x;
	*x = *y;
	*y = tmp;
}

// 堆的向上调整
void AdjustUp(HeapDataType* a, int child)
{
	assert(a);

	int parent = (child - 1) / 2;
	while (child > 0)
	{
		if (a[child] > a[parent])
		{
			Swap(&a[child], &a[parent]);
			child = parent;
			parent = (child - 1) / 2;
		}
		else
		{
			break;
		}
	}
}

// 堆的向下调整
void AdjustDown(HeapDataType* a, int n, int parent)
{
	assert(a);
	assert(!HeapEmpty(a));

	int child = parent * 2 + 1;
	while (child < n)
	{
		if (child + 1 < n && a[child + 1] > a[child])
		{
			child++;
		}

		if (a[parent] < a[child])
		{
			Swap(&a[parent], &a[child]);
			parent = child;
			child = child * 2 + 1;
		}
		else
		{
			break;
		}
	}
}

// 堆的销毁
void HeapDestory(Heap* hp)
{
	assert(hp);

	free(hp->a);
	hp->a = NULL;
	hp->capacity = 0;
	hp->size = 0;
}

// 调整堆的大小
void CheckCapacity(Heap* hp)
{
	assert(hp);

	if (hp->size == hp->capacity)
	{
		hp->capacity *= 2;
		HeapDataType* new = (HeapDataType*)realloc(hp->a, sizeof(HeapDataType) * hp->capacity);
		if (new == NULL)
		{
			perror("malloc fail");
			return;
		}
		hp->a = new;
	}
}

// 堆的插入
void HeapPush(Heap* hp, HeapDataType x)
{
	assert(hp);

	CheckCapacity(hp);

	hp->a[hp->size] = x;
	AdjustUp(hp->a, hp->size);
	hp->size++;
}

// 堆的删除
void HeapPop(Heap* hp)
{
	assert(hp);
	assert(!HeapEmpty(hp));

	Swap(&(hp->a[0]), &(hp->a[hp->size - 1]));
	hp->size--;
	AdjustDown(hp->a, hp->size, 0);
}

// 取堆顶的数据
HeapDataType HeapTop(Heap* hp)
{
	assert(hp);
	assert(!HeapEmpty(hp));

	return hp->a[0];
}

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

	return hp->size;
}

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

	return hp->size == 0;
}

测试文件

test.c

#define _CRT_SECURE_NO_WARNINGS 1

#include"Heap.h"

int main()
{
	Heap a;

	int arr[10] = { 9,2,4,6,1,5,3,8,0,7 };

	HeapCreate(&a, arr, 10);

	while (!HeapEmpty(&a))
	{
		int tmp = HeapTop(&a);
		printf("%d ", tmp);
		HeapPop(&a);
	}



	HeapDestory(&a);
	return 0;
}

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

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

相关文章

【大数据】TiDB: A Raft-based HTAP Database

文章目录 数据库知识介绍数据库系统的ACID特性分布式系统和CAP理论关系型数据库与非关系型数据库关系型数据库非关系型数据库 OldSQL、NoSQL、NewSQLOldSQLNoSQLNewSQL OLTP、OLAP、HTAP 前言&#xff1a;为什么选择TiDB学习&#xff1f;pingCAP介绍TiDB介绍TiDB的影响力TiDB概…

Wireshark TS | 再谈应用传输缓慢问题

问题背景 来自于朋友分享的一个案例&#xff0c;某某客户反馈电脑应用软件使用时打开很慢&#xff0c;并提供了一个慢时所捕获的数据包文件以及服务端 IP。以前也说过&#xff0c;所谓的慢有很多种现象&#xff0c;也会有很多原因引起&#xff0c;在没有更多输入条件的情况下&…

Redis集合[持续更新]

Redis&#xff08;全称&#xff1a;Remote Dictionary Server 远程字典服务&#xff09;是一个开源的使用 ANSI C 语言编写、支持网络、可基于内存亦可持久化的日志型、Key-Value 数据库&#xff0c;并提供多种语言的 API。 数据结构 1. string 字符串 字符串类型是 Redis 最…

云知识库怎么搭建才适合中小企业?用这几个工具很轻松

当我们想到知识库时&#xff0c;可能会联想到庞大的公司和复杂的系统&#xff0c;但实际上&#xff0c;随着技术的发展&#xff0c;中小企业也可以利用各种工具来建立自己的云知识库。这样不仅能够提升企业的知识管理效率&#xff0c;还能优化客户服务流程。这篇文章会介绍三款…

【QT+QGIS跨平台编译】182:【QGIS+Qt跨平台编译】(一套代码、一套框架,跨平台编译)

文章目录 一、qgis模块介绍二、QGIS下载三、文件分析四、pro文件五、编译实践一、qgis模块介绍 qgis模块作为QGIS启动模块,集成底层所有的模块。 二、QGIS下载 QGIS网址: QGIS Source Download 获取qgis.tar.bz2文件。 三、文件分析 解压缩qgis.tar.bz2文件,进入到qgis\…

编写Spark独立应用程序

执行本文之前&#xff0c;先搭建好spark的开发环境&#xff0c;我目前只搭建了standalone模式&#xff0c;参考链接 &#xff1a; Spark Standalone模式部署-CSDN博客 1. 安装sbt 1&#xff09;下载sbt 网址&#xff1a;https://www.scala-sbt.org/download.html &#xff0c…

# Win10 打不开【本地组策略编辑器】解决方案

Win10 打不开【本地组策略编辑器】解决方案 段子手168 问题描述&#xff1a; 当在 WIN R 打开【运行】输入&#xff1a;gpedit.msc 打开【本地组策略编辑器】时&#xff0c;出现错误时&#xff0c; 或者在【计算机管理】中 没有【本地用户和组】这一项。 可以试一下以下方…

8.MMD ray渲染主流常用插件介绍

导入一个场景&#xff0c;做好基础操作 导入控制器、天空盒、材质 1. AutoLuminous4 自发光的插件 ![[Pasted image 20240421103420.png]] 拖进去以后可以让场景中的自发光材质发光 也可以让不发光的材质强行发光 打开MME&#xff0c;找到AL_EmitterRT 展开场景&#x…

解线性方程组——直接解法:LU分解、PLU分解(类似列主元消去法) | 北太天元

L: lower triangular 下三角 U: upper triangular 上三角 LU 分解&#xff0c;顾名思义&#xff0c;为 把一个 矩阵 分成 一个下三角矩阵 乘上一个上三角矩阵的形式。 Example 为什么可以这样 几个基本的初等行变换&#xff0c;可以自己验算一下&#xff0c;等式的左边与右边…

spring高级篇(二)

1、Aware和InitializingBean Aware和InitializingBean都与Bean的生命周期管理相关。 Aware接口: 概念: Aware接口是Spring框架中的一个标记接口&#xff0c;它表示一个类能够感知到&#xff08;aware of&#xff09;Spring容器的存在及其特定的环境。Spring框架提供了多个Awar…

一周学会Django5 Python Web开发-Django5模型数据修改

锋哥原创的Python Web开发 Django5视频教程&#xff1a; 2024版 Django5 Python web开发 视频教程(无废话版) 玩命更新中~_哔哩哔哩_bilibili2024版 Django5 Python web开发 视频教程(无废话版) 玩命更新中~共计47条视频&#xff0c;包括&#xff1a;2024版 Django5 Python we…

小图标还不会设计!

ICON图标设计 hello&#xff0c;我是小索奇 image-20230805225451447 你有好奇过这样的图标如何设计的吗&#xff1f; 其实非常简单&#xff0c;仅需要一行代码即可完成&#xff0c;本篇文章就带伙伴们使用&#xff0c;每天看一篇&#xff0c;简单易懂&#xff0c;日久技长~…

5 款免费数据恢复工具,用于恢复误删数据

数据恢复工具是通过组装幸存的片段、从剩余的片段重建或使用备份来重新获得对因存储损坏、人为错误或意外中断而丢失的文件的访问权限的行为。它是将丢失、损坏、意外擦除或以其他方式无法访问的数据恢复到服务器、计算机、手机或存储设备的过程。 在大多数情况下&#xff0c;数…

kaggle 泰坦尼克使用xgboost 得分0.73684

流程 导入所要使用的包引入kaggle的数据集csv文件查看数据集有无空值填充这些空值提取特征分离训练集和测试集调用模型 导入需要的包 import pandas as pd import numpy as np import matplotlib.pyplot as plt import seaborn as sns import warnings warnings.filterwarni…

人人都能玩赚数字人操作员 数字人直播搭建/多路开播/选品技巧/0-1开播流程

课程目录 01 数字人工业化直播车间打造 02 数字人直播规则及防封技巧 03 数字人直播间搭建步骤流程 04 数字人直播行业应用盘点 05 数字人直播多平台多路开播 06 数字人高成交循环话术运营 07 数字人直播选品及组品技I5 08 数字人直播0-1流程 09 工业化直播0-1流程 网…

Axure中的样式

样式 首先说一下Axure里面的原点位置 如下图&#xff1a; 还有一个办法是我们选中我们的按钮&#xff0c;如上图&#xff0c;然后打开右边的样式&#xff0c;可以看按钮的x&#xff0c;y属性&#xff0c;类似于游戏中unity软件的x&#xff0c;y属性&#xff0c;类似于html中…

【JVM系列】关于静态块、静态属性、构造块、构造方法的执行顺序

&#x1f49d;&#x1f49d;&#x1f49d;欢迎来到我的博客&#xff0c;很高兴能够在这里和您见面&#xff01;希望您在这里可以感受到一份轻松愉快的氛围&#xff0c;不仅可以获得有趣的内容和知识&#xff0c;也可以畅所欲言、分享您的想法和见解。 推荐:kwan 的首页,持续学…

数字时代的智慧演奏

数字化时代&#xff0c;工业不再是孤独的机器运转&#xff0c;而是演绎着一场智能与数据的华丽交响。无数智能节点的联动&#xff0c;数据的涌动&#xff0c;成为工业的新活力&#xff0c;同时也是创新的源泉。 工业互联网将每个机器、设备连接在一起&#xff0c;打破了原本独立…

【数据结构练习题】堆——top-k问题

♥♥♥♥♥个人主页♥♥♥♥♥ ♥♥♥♥♥数据结构练习题总结专栏♥♥♥♥♥ ♥♥♥♥♥上一章&#xff1a;【数据结构练习题】二叉树(1)——1.相同的树2.另一颗树的子树3.翻转二叉树4.平衡二叉树5.对称二叉树♥♥♥♥♥ 文章目录 1.top-k问题1.1问题描述1.2思路分析1.3绘图分析…

simulink中怎么使用solve函数?

&#x1f3c6;本文收录于「Bug调优」专栏&#xff0c;主要记录项目实战过程中的Bug之前因后果及提供真实有效的解决方案&#xff0c;希望能够助你一臂之力&#xff0c;帮你早日登顶实现财富自由&#x1f680;&#xff1b;同时&#xff0c;欢迎大家关注&&收藏&&…