数据结构初阶---二叉树---堆

一、树

1.树的概念

树是一种非线性的数据结构,由n(n≥0)个有限结点组成的一个有层次关系的集合。形状类似一棵倒挂的树,根朝上,分支向下。

根结点没有前驱结点,可以有n(n≥0)个后继结点。

其余结点被分为M个互不相交的集合,每一个集合都是一棵子树,每棵子树的根节点均有1个前驱结点,n(n≥0)个后继结点。因此树是递归定义的。

注:除根外的结点被分为互不相交的集合,如果相交,就不是树,而是图了。

子树之间存在交集,不属于树形结构,某结点存在多个父结点即它的前驱结点>1,也不属于树形结构。

2.与树有关的概念

节点的度:一个节点含有的子树的个数称为该节点的度; 
叶节点(终端节点):度为0的节点称为叶节点;(重要)
分支节点(非终端节点):度不为0的节点;
父节点(双亲结点):若一个节点含有子节点,则这个节点称为其子节点的父节点;(重要)
子节点(孩子节点):一个节点含有的子树的根节点称为该节点的子节点;(重要)
兄弟节点:具有相同父节点的节点互称为兄弟节点;
树的度:一棵树中,最大的节点的度称为树的度;
节点的层次:从根开始定义起,根为第1层,根的子节点为第2层,以此类推;(重要)
树的高度或深度:树中节点的最大层次;(重要)
堂兄弟节点:双亲在同一层的节点互为堂兄弟;
节点的祖先:从根到该节点所经分支上的所有节点;(重要)
子孙:以某节点为根的子树中任一节点都称为该节点的子孙。                                                          森林:由m(m>0)棵互不相交的树的集合称为森林;

注:关于树的层次,一般而言是从1开始,即根所在层次为1,如果题目给定从0开始,那就从0开始,否则默认从1开始层次,因为对于无结点树而言,层次刚好是0,从0开始那么无结点树层次就为-1。

3.树的表示

如果我们使用数组来表示树,那么我们一般采用数组ArrayChild来存储孩子结点

#define N 6 //假设已知树的度为6
struct TreeNode
{
    TreeNodeTypeData val;
    struct TreeNode* ArrayChild[N];
};

但是,如果这样来表示树,那么相当于每一个分支结点,都有N个数组空间(N是树的度的值),但是树的分支结点不一定都有N个子结点,这样的表示就太过于浪费空间了。

如果用顺序表来表示树型结构,

struct TreeNode
{
    TreeNodeTypeData val;
    SeqList Childlist;
};

那么每个孩子结点都是一个顺序表,但是需要我们去构建一个顺序表,在C语言阶段也是非常费心费力的。

左孩子右兄弟表示法

树形结构的最优表示方法是左孩子右兄弟表示法

树形结构中创建两个指针,左孩子指针LeftChild与右兄弟指针RightBrother

struct TreeNode
{
    TreeNodeTypeData val;
    struct TreeNode* LeftChild;
    struct TreeNode* RightBrother;
};

我们通过每一次的根节点访问左孩子,就可以通过左孩子结点访问右兄弟指针找到该层次的其他结点;如果想访问下一层次,那么现在的左孩子结点访问它的左孩子结点,通过它的左孩子结点访问右兄弟指针能够找到其他所有结点。

上图中左孩子右兄弟表示形式里,未标识出来的左孩子LeftChild指针都应该指向NULL。

由此我们只需要访问上一层次某一棵子树的根节点的左孩子结点就可以遍历该层次属于该子树的所有结点。

如A的左孩子为B,A结点访问成员LeftChild得到B结点,B结点访问成员RightBrother就能够访问到同层次属于A孩子的C与D结点。如B的左孩子是E,那么通过B访问到E结点就可以遍历E、F结点。

4.树的实际运用

Linux树状目录结构。

Windows文件目录多盘形式---森林。

二、二叉树

1.概念

二叉树的结点的度均≤2(即每个结点的子结点不超过2个),同时二叉树区分左右子树(即区分左右结点),是有序树。

二叉树是特殊的树。

2.特殊的二叉树

①满二叉树

二叉树的所有结点的度都是2,那么就是满二叉树。对于满二叉树而言,每一层的结点数都达到了最大值。

假设满二叉树的深度为h,结点数为N,那么存在关系:2^h - 1 = N <==> h = log(N+1) (以2为底)

反过来说,如果一个二叉树的层数为K,它的结点数为2^K-1,那么这个二叉树就是满二叉树。

②完全二叉树

完全二叉树是效率很高的数据结构,完全二叉树是由满二叉树而引出来的。

对于深度为K的,有n个结点的二叉树,当且仅当其每一个结点都与深度为K的满二叉树中编号从1至n的结点一一对应时称之为完全二叉树。满二叉树是一种特殊的完全二叉树。

通俗来讲,除去最后一层的其它层结点数达到最大值,最后一层结点数不确定但是从左到右一定有序的二叉树就是完全二叉树。

对于完全二叉树来说,由于最后一层的结点有序,但是不确定最后一层结点个数,因此总的结点数N是一个范围,最小的时候最后一层只有1个结点,最大的时候是满二叉树。

那么N的范围为:[2^(h-1)-1+1 , 2^(h) -1]  化简--->[2^(h-1) , 2^(h) -1]

注意,对于完全二叉树来说,最后一层的结点一定是有序的!结点一定是从左至右增加的。

3.二叉树的性质

4.二叉树的存储结构

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

①顺序存储

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

顺序存储--->使用数组存储,只适用于完全二叉树。如果用数组存储的方式对任意二叉树,那么由于每个结点的度不同,会有许多数组空间的浪费,因此数组存储只适用于完全二叉树。

将完全二叉树的结点一层一层的存入数组中,

我们会发现,父子结点的下标是存在联系的。

②链式存储

对于不是完全二叉树和满二叉树的树形结构,我们采取链式结构来进行数据存储。

二叉树的链式存储结构是指,用链表来表示一棵二叉树,即用链来指示元素的逻辑关系。通常的方法是链表中每个结点由三个域组成,数据域和左右指针域,左右指针分别用来给出该结点左孩子和右孩子所 在的链结点的存储地址 。链式结构又分为二叉链和三叉链,当前我们学习中一般都是二叉链,后面学到高阶数据结构如红黑树等会用到三叉链。

5.堆

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

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

简单的说,如果一个数组的内容满足堆的性质,那么堆就把这个数组数据看做一棵完全二叉树。

堆只分为小(根)堆和大(根)堆,小堆表示所有的父结点的数据都小于等于其子结点;大堆表示所有的父结点的数据都大于等于其子结点。除此之外都不是堆。

有序数组是不是堆?是,但是堆不一定是有序数组。

跟之前数据结构讲的栈不同于内存中的栈类似,

数据结构的堆是一种管理数据的结构,是完全二叉树;

而操作系统中的堆,指的是一个内存区域,这个区域中的空间可以通过malloc、calloc开辟、realloc扩容使用,最后free释放。

堆的物理结构是数组;逻辑结构是完全二叉树。

堆的意义:

1.堆排序---时间复杂度O(N*logN)

2.top k问题

对于堆的实现,我们想象自己操纵的是完全二叉树,但是我们实际上操纵的是数组。

数组实现堆

结构如下,我们来实现一下大(根)堆:

//堆是由读取规定的---与栈和队列一样,堆顶开始读取然后删除
//大堆---父节点始终大于等于子节点
#define HeapDataType int
typedef struct Heap
{
	HeapDataType* a;
	int size;
	int capacity;
}Heap;

接口函数

①初始化HeapInit

void HeapInit(Heap* php);

//初始化
void HeapInit(Heap* php)
{
	assert(php);
	php->a = NULL;
	php->capacity = 0;
	php->size = 0;
}
②销毁HeapDestroy

void HeapDestroy(Heap* php);

//销毁
void HeapDestroy(Heap* php)
{
	assert(php);
	free(php->a);
	php->a = NULL;
	php->capacity = 0;
	php->size = 0;
}
③插入HeapPush---O(logN)

void HeapPush(Heap* php, HeapDataType x);

在一个堆中插入一个新的数据x,那么我们就必须得将该数据移动到合适的位置,确保插入后新的数组仍然是一个大堆。

首先先写出扩容判断的代码以及插入的代码:

	//检测容量---不用单独开接口,因为只有一个插入
	if (php->size == php->capacity)
	{
		int NewCapacity = php->capacity == 0 ? 4 : php->capacity * 2;
		HeapDataType* tmp = (HeapDataType*)realloc(php->a, sizeof(HeapDataType) * NewCapacity);
		if (tmp == NULL)
		{
			perror("realloc fail");
			exit(-1);
		}
		php->a = tmp;
		php->capacity = NewCapacity;
	}
	//插入数据
	php->a[php->size] = x;
	php->size++;

那么就差最后一步,判断是否需要更换位置:

我们知道,对于大堆而言,父节点的数据始终大于等于子节点的数据,对于目前插入在最后一层的新结点而言,我们需要将其与它的父辈、祖辈,与它的祖先进行比较,如果新结点的数据大于父节点,那么就需要将新结点与父节点交换,然后接着向上比较:

需要交换数据,那么我们封装为一个Swap函数用来交换数据:

//交换函数
void Swap(HeapDataType* p1, HeapDataType* p2)
{
	HeapDataType tmp = *p1;
	*p1 = *p2;
	*p2 = tmp;
}

所以对于插入的值的比较,我们需要通过该结点找到其父结点,根据前面学到的堆的父子结点关系,我们知道,子节点下标child,(child-1)/2就能够得到父节点下标parent

最后的比较要到什么时候终止呢?如果最慢的情况,插入的数据比原根节点都要大,那么就要换到A结点处--->因此循环的终止条件应为child == 0 。那么我们将判断和移动数据的过程封装成一个函数UpAdjust(向上调整)。

向上调整算法UpAdjust

void UpAdjust(Heap* php, int child);

    //向上调整算法
	UpAdjust(php, php->size - 1);

传入的child是插入结点的下标,由于前面插入后size自增1了,所以这个地方传进来的应该是size-1。

//向上调整算法---最坏情况调整深度h次,而完全二叉树的h与logN相关---因此时间复杂度logN
void UpAdjust(Heap* php, int child)
{
	assert(php);
	int parent = (child - 1) / 2;//父节点下标为子节点-1再除2
	while (child > 0)
	{
		if (php->a[parent] < php->a[child])
		{
			Swap(&php->a[parent], &php->a[child]);
			child = parent;
			parent = (parent - 1) / 2;
		}
		else
			break;
	}
}
④删除HeapPop---O(logN)

void HeapPop(Heap* php);

在堆的实现中的删除操作,删除的是堆顶元素---即根结点元素。

但是我们又不能够直接进行覆盖操作,如果直接进行覆盖,一会导致原来的这个父子下标关系改变,摧毁了这个堆;二是数组的头删挪位覆盖时间复杂度为O(N)。

所以就有一种方法:我们先交换堆顶元素与最后一个元素,然后进行尾删--->就删除了堆顶元素。

对于数组结构而言,尾删是非常方便的,size自减1即可。然后这个时候,除了堆顶元素改变,其他位置上的元素均没有变化,其实类似于向上调整---我们实现一个向下调整算法DownAdjust。

//删除堆顶结点---时间复杂度O(logN)
void HeapPop(Heap* php)
{
	assert(php);
	Swap(&php->a[0], &php->a[php->size - 1]);//交换--->尾删原堆顶
	php->size--;//尾删原堆顶
	//向下调整算法---O(logN),要将交换上去的结点向下调整
	DownAdjust(php->a, php->size, 0);
}

DownAdjust向下调整算法内,我们将堆顶元素与它的子节点元素中较大的进行判断,如果堆顶元素大,那么符合大堆,不用进行交换元素,反之,堆顶元素小于较大的子结点元素,那么交换二者,将交换后的处于子节点处的该元素继续与这个位置上它的子结点的较大元素进行比较。

向下调整算法DownAdjust

 void DownAdjust(HeapDataType* a, int size, int parent);

//向下调整---时间复杂度O(logN)
void DownAdjust(HeapDataType* a, int size, int parent)
{
	int child = parent * 2 + 1;
	while (child < size)
	{
		if ( child+1 < size && a[child] < a[child + 1])//小心越界
		{
			child++;
		}
		if (a[child] > a[parent])
		{
			Swap(&a[child], &a[parent]);
			parent = child;
			child = 2 * child + 1;
		}
		else
			break;
	}
}

为了防止if else判断两个子节点谁大的代码冗长,我们选择使用了假设判断法,先将其中的左节点下标给child,然后再if判断如果左节点数据小于右孩子结点,那么将右节点下标给child,实际上就与child自增1没有区别。 极端情况下,当我们判断两者大小时,可能出现child+1的越界访问的问题。 因此需要在if的条件中多加上一条child+1<size

找到更大的孩子节点、然后进行比较,这个整体是一个循环过程,因为交换到堆顶的数据最多需要进行深度h次交换,如果该数据足够小的话。循环进行条件就是chiild<size,即下标不能越界。在发现了该节点处的数据大于等于较大子节点的数据时,就满足了大堆性质,直接跳出循环即可。

⑤获取堆顶元素HeapTop

HeapDataType HeapTop(Heap* php);

//获取堆顶元素
HeapDataType HeapTop(Heap* php)
{
	assert(php);
	assert(php->size);

	return php->a[0];
}

有关于获取堆顶元素的接口,我们一般与删除堆顶元素联合起来使用,大堆的堆顶元素读取然后删除,执行k次,就能够找到这个堆中前k大的数据。

同时如果我们也可以这样获取降序的全部数据--->大堆就能够获取降序,小堆能够获取升序数据。

⑥获取堆有效元素个数HeapSize

int HeapSize(Heap* php);

//获取堆有效元素个数
int HeapSize(Heap* php)
{
	assert(php);

	return php->size;
}
⑦判断堆是否为空HeapEmpty

bool HeapEmpty(Heap* php);

//判断堆是否为空
bool HeapEmpty(Heap* php)
{
	assert(php);

	return php->size == 0;
}

堆的应用

1.堆排序问题

时间复杂度O(N*logN)。

上面通过数组实现了堆,那么给定一个数组,我们当然可以将数组的数据插入的堆结构中,进行向上调整,然后通过获取堆顶数据,删除堆顶向下调整的小套餐来打印排序后的数据,但是这样的操作有两个缺点:①需要一个完整的堆结构②原数组其实没有改变

那么实际上不需要如上操作进行排序,堆排序可以对该数组重新排序。

升序--->①建立大堆②进行排序

大堆父结点大于子节点,为什么会升序?===>这就涉及到伪删除,我们利用到堆接口函数删除堆顶元素的实现,本质是将尾部数据与堆顶数据互换再删除堆顶数据,那么其实此时堆顶数据在末尾,只要我们不删除,就能够实现每一次的伪删除会将最大的数据移动到数组尾部。每一次互换之后我们需要进行向下调整,当然调整不会包括互换后的尾结点数据。

降序--->①建立小堆②进行排序

降序为什么建立小堆同上。

下面演示升序:

①升序演示---建立大堆

建堆有两种方式:我们可以通过将每个数组数据从起始开始进行向上调整;也可以从尾结点的父结点开始进行向下调整。目的都是为了将数组数据排放成堆的形式。

从起始开始向上调整:时间复杂度O(N*logN)

	//数组建堆---
	//方法一:第一个数据成堆,其他数据依次入堆然后向上调整 O(N*logN)
	for (int i=1; i < n; i++)
	{
		UpAdjust(a, i);//数据依次向上调整成大堆
	}

从尾结点的父结点开始向下调整:时间复杂度O(N)

	//方法二:从最后一个结点的父结点开始,倒着对所有非叶子结点进行向下调整 O(N)
	for (int i = (n - 1 - 1) / 2; i >= 0; i--)
	{
		DownAdjust(a, n, i);
	}

起始我们也能看出来时间复杂度差在哪里,对于方式二而言,最后一层结点数最多,但是对于方式二却不用对它们进行任何次数的调整,因此时间复杂度比方式一要小。

②升序演示---伪删除/选数

将堆顶与尾部数据交换,然后传入DownAdjust向下调整的end每次遍历减1。第一次交换后传入的size应该是减1之后的===>即下面代码的end = n-1。

	//伪删除---堆顶数据与尾部交换,size--,向下调整===>那么数组尾部就是最大数据 O(NlogN)
	int end = n - 1;
	while (end > 0)
	{
		Swap(&a[0], &a[end]);
		DownAdjust(a, end, 0);//依次向下调整
		end--;
	}

一个数据在向下调整最多层数h次,N个数据===>N*logN。

选数操作的时间复杂度O(N*logN)。 

推排序升序数组:

//堆排序
//堆排序是将给出的数组数据先排序成堆,然后再进行排序
//而不是将数据依次插入实现好的堆中
void HeapSort()//测试---堆排序
{
	int a[] = { 1,5,3,6,8,2,1,5,8 };
	int n = sizeof(a) / sizeof(a[0]);
	//数组建堆---
	//方法一:第一个数据成堆,其他数据依次入堆然后向上调整 O(N*logN)
	for (int i=1; i < n; i++)
	{
		UpAdjust(a, i);//数据依次向上调整成大堆
	}
	//方法二:从最后一个结点的父结点开始,倒着对所有非叶子结点进行向下调整 O(N)
	for (int i = (n - 1 - 1) / 2; i >= 0; i--)
	{
		DownAdjust(a, n, i);
	}
	//伪删除---堆顶数据与尾部交换,size--,向下调整===>那么数组尾部就是最大数据 O(NlogN)
	int end = n - 1;
	while (end > 0)
	{
		Swap(&a[0], &a[end]);
		DownAdjust(a, end, 0);//依次向下调整
		end--;
	}
	for (int i = 0; i < n; i++)
	{
		printf("%d ", a[i]);
	}
}

2.top k问题
思路一

前面接口函数中提到了top k问题的解决方法,我们可以通过将这些数据以大堆的形式全部插入到堆中,那么再循环k次来删除k次堆顶数据并获取前k个最大的数据。但是如果数据基数N非常大,那么我们需要为这个大堆开辟的空间是非常非常大的,举个例子,N=100亿--->100亿个整型--->约为400亿字节--->约为40G的内存大小,现如今普通人家的电脑内存大小,16-32G,无法完成,而且就算能够完成,也不适用。

思路二---最优解

在top k问题中,如果要在一个非常非常大的N个数据中,找出k个最大的数据,那么我们可以通过这样的办法实现:

创建一个k个数据的小堆,先将最开始的k个数据存入小堆中,然后在剩余的数据依次与小堆堆顶元素比较,如果大于堆顶元素,那么覆盖堆顶元素并向下调整,使其依然满足小堆。那么到最后,小堆中就是最大的k个数据了。这种方法是非常便捷的,时间复杂度为O(N*logk)即O(N),但是空间复杂度只有O(1),而思路一的空间复杂度达到了O(N),而且基本上难以实现。

时间复杂度O(N*logk)==O(N)。

那么通过最优解,就可以从N个数据中找k个最大的数据,假设我们在文件中保存1000000个随机值,然后通过堆来找出k个最大的值:

首先我们在文件中写入N=1000000个随机值:

//TopK问题---在N个数据中找到K个最大的数据
void CreatNData()//在文件中创建1000000个数据
{
	FILE* fin = fopen("data.txt", "w");
	if (fin == NULL)
	{
		perror("fopen fail");
		return;
	}
	int n = 10000000;
	srand(time(0));
	while (n--)
	{
		int x = (rand() + n) % 1000000;//数据都是小于1000000的
		fprintf(fin, "%d\n", x);
	}
	fclose(fin);
}

这里写入的数据模了1000000方便我们后续测试是否成功取出k个最大数据。

我们执行一次CreatNdatra函数,那么就能够创建这个data.txt文件并在其中写入1000000个数据。

由于我们需要取较大数据,那么就需要用小堆===>即原来适用于大堆的向上调整与向下调整算法需要进行细微的修改。

下面是小堆的向上向下调整算法(其实只需调整几个大于小于号)

//小堆的向上向下调整算法
//向上调整算法---最坏情况调整深度h次,而h大概率等于logN相关---因此时间复杂度logN
void UpAdjust_lowheap(HeapDataType* a, int child)
{
	int parent = (child - 1) / 2;//父节点下标为子节点-1再除2
	while (child > 0)
	{
		if (a[parent] > a[child])
		{
			Swap(&a[parent], &a[child]);
			child = parent;
			parent = (parent - 1) / 2;
		}
		else
			break;
	}
}
//向下调整---时间复杂度O(logN)---小堆
void DownAdjust_lowheap(HeapDataType* a, int size, int parent)
{
	int child = parent * 2 + 1;
	while (child < size)
	{
		if (child + 1 < size && a[child] > a[child + 1])//小心越界
		{
			child++;
		}
		if (a[child] < a[parent])
		{
			Swap(&a[child], &a[parent]);
			parent = child;
			child = 2 * child + 1;
		}
		else
			break;
	}
}

写一个函数PrintTopK,解决topk问题:

void PrintTopK(FILE* file, int k);

创建一个minheap数组存储k个数据,VS不支持变长数组,那么我们使用malloc开辟k个数据大小的空间给minheap。

int* minheap = (int*)malloc(sizeof(int) * k);

第一步将该文件中的前k个数据存入小堆进行向上调整;

	//读前k个数据建小堆
	for (int i = 0; i < k; i++)
	{
		fscanf(fout, "%d", &minheap[i]);
		UpAdjust_lowheap(minheap, i);
	}

第二步将文件中剩余的N-k个数据依次遍历与堆顶数据比较,大于等于堆顶数据则替换堆顶数据然后进行向下调整使之依然成小堆。

	//剩下N-K个数据依次遍历与堆顶数据比较,大于等于堆顶数据进入堆
	int x = 0;
	while (fscanf(fout, "%d", &x) != EOF)//先读到x中,若x大于等于堆顶,放入堆顶并向下调整
	{
		if (x >= minheap[0])
		{
			minheap[0] = x;
			DownAdjust_lowheap(minheap, k, 0);
		}
	}

fscanf读取结束返回EOF,那么循环条件可以直接写作如上图所示,将每次遍历的文件中的数据存入x中,x与堆顶数据进行判断,若大于等于堆顶数据,替换堆顶数据并向下调整。

注:scanf函数以及fscanf等函数,默认遇到空格或者换行\n终止。 

最后我们可以打印minheap来观察数据,记得最后需要释放堆中开辟的空间以及关闭文件。

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

前面手动添加了取模1000000,我们可以通过手动修改data.txt中的5个数据使其大于1000000,再次执行程序来观察最大值的变化,从而判断程序是否正确无误。

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

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

相关文章

高等动力学中的正则变换

1 正则变化的定义 在相空间中&#xff0c;如果以时间t为参数的坐标变换 使得满足哈密顿正则方程 的动力变量 和 变成满足新的正则方程 的新的动力变量 和 &#xff0c;则称该变换为正则变换。 该变换可逆的条件&#xff1a;雅可比行列式值不等于零 正则变换的充要条件&#…

在线图片格式转换器 - 加菲工具

图片格式转换器-加菲工具 支持JPG\JPGE\PNG\ICO\BMP之间相互转换。 打开加菲工具或者直接打开 https://www.orcc.online/tools/image 选择“图片格式转换器” 上传图片&#xff0c;选择目标格式 转换完成

python web练习案例:基于表单类的商品管理(修改并删除商品信息)

目录 1、修改商品信息 &#xff08;1&#xff09;修改show.html页面&#xff0c;增加 修改 栏 &#xff08;2&#xff09;创建 update.html 网页&#xff0c;继承 add.html 模板 &#xff08;3&#xff09;定义视图函数 &#xff08;4&#xff09;定义路由 (5) 浏览器查看 …

【容器】k8s学习笔记基础部分(三万字超详细)

概念 应用部署方式演变 在部署应用程序的方式上&#xff0c;主要经历了三个时代&#xff1a; 传统部署&#xff1a;互联网早期&#xff0c;会直接将应用程序部署在物理机上 优点&#xff1a;简单&#xff0c;不需要其它技术的参与 缺点&#xff1a;不能为应用程序定义资源使…

数据结构速成

1. 数据结构与算法 2. 顺序表 3. 链表 4. 栈与队列 5. 串 6. 树与二叉树&#xff08;1&#xff09; 7. 树与二叉树&#xff08;2&#xff09; 8. 图 9. 图的应用 10. 查找 11. 排序&#xff08;1&#xff09; 12. 排序&#xff08;2&#xff09;

算法题(5):求和

审题&#xff1a; 首先我们需要对这个式子先整理一下。 整理后可以知道 我们发现ai乘的那一串数字逐渐减少&#xff0c;而每次减少的就是ai。 思路&#xff1a; 获取数据&#xff1a;用n获取数据个数-》确定获取数据的循环次数 用数组a存储数据 用sum获录入数据之和 用num存储最…

el-table 动态计算合并行

原始表格及代码 <el-table:data"tableData"class"myTable"header-row-class-name"tableHead" ><el-table-column prop"date" label"日期"> </el-table-column><el-table-column prop"name" …

深圳国威HB1910数字IP程控交换机 generate.php 远程命令执行漏洞复现

0x01 产品描述: 深圳国威主营国威模拟、数字、IP 交换机、语音网关、IP 电话机及各种电话机。深圳国威电子有限公司HB1910是一款功能强大的网络通信设备,适用于各种企业通信需求。 0x02 漏洞描述: 深圳国威电子有限公司HB1910数字IP程控交换机generate.php存在远程命令执行…

数据结构(链表)JAVA方法的介绍

注意&#xff1a; 1.我们从上图中可以看出链表LinkedList继承于List接口&#xff1a; 如果不懂List接口的朋友们可以先看我上期作品了解一下List接口 数据结构&#xff08;顺序表&#xff09;JAVA方法的介绍-CSDN博客 2.本期注重讲解Java中LinkedList链表中个方法的使用&am…

UE5中实现Billboard公告板渲染

公告板&#xff08;Billboard&#xff09;通常指永远面向摄像机的面片&#xff0c;游戏中许多技术都基于公告板&#xff0c;例如提示拾取图标、敌人血槽信息等&#xff0c;本文将使用UE5和材质节点制作一个公告板。 Gif效果&#xff1a; 网格效果&#xff1a; 1.思路 通过…

文件包含 0 1学习

漏洞原理 成因 文件包含漏洞是一个最常见的依赖与脚本运行而影响Web应用程序的漏洞.当应用程序使用攻击者控制的变量建立一个可执行代码的路径,允许攻击者控制在运行时执行哪个文件时,就会导致文件包含漏洞.程序开发人员通常会把可重复使用的函数写入单文件中,在使用这些函数…

docker login 出错 Error response from daemon

在自己的Linux服务器尝试登陆docker出错 输入完用户密码之后错误如下&#xff1a; 解决方案 1.打开daemo文件&#xff1a; vim/etc/docker/daemon.json 2.常用的国内Docker 镜像源地址 网易云 Docker 镜像&#xff1a;http://hub-mirror.c.163.com 百度云 Docker 镜像&#x…

专业140+总分410+浙江大学842信号系统与数字电路考研经验浙大电子信息与通信工程,真题,大纲,参考书。

考研落幕&#xff0c;本人本中游211&#xff0c;如愿以偿考入浙江大学&#xff0c;专业课842信号系统与数字电路140&#xff0c;总分410&#xff0c;和考前多次模考预期差距不大&#xff08;建议大家平时做好定期模考测试&#xff0c;直接从实战分数中&#xff0c;找到复习的脉…

【html网页页面012】html+css制作品牌主题宏宝莱网页含视频、留言表单(7页面附效果及源码)

品牌主题宏宝莱网页制作 &#x1f964;1、写在前面&#x1f367;2、涉及知识&#x1f333;3、网页效果完整效果(7页)&#xff1a;代码目录结构&#xff1a;page1、首页page2、衍生品page3、包装设计page4、视频介绍page5、留言板page6、联系我们page7、详情页&#xff08;三层页…

Unity 获取鼠标点击位置物体贴图颜色

实现 Ray ray Camera.main.ScreenPointToRay(Input.mousePosition); if (Physics.Raycast(ray, out RaycastHit hit)) {textureCoord hit.textureCoord;textureCoord.x * textureMat.width;textureCoord.y * textureMat.height;textureColor textureMat.GetPixel(Mathf.Flo…

游戏引擎学习第50天

仓库: https://gitee.com/mrxiao_com/2d_game Minkowski 这个算法有点懵逼 回顾 基本上&#xff0c;现在我们所处的阶段是&#xff0c;回顾最初的代码&#xff0c;我们正在讨论我们希望在引擎中实现的所有功能。我们正在做的版本是初步的、粗略的版本&#xff0c;涵盖我们认…

Mac备忘录表格中换行(`Option` + `Return`(回车键))

在Mac的ARM架构设备上&#xff0c;如果你使用的是Apple的原生“备忘录”应用来创建表格&#xff0c;换行操作可以通过以下步骤来实现&#xff1a; 在单元格中换行&#xff1a; 双击你想要编辑的单元格你可以输入文本&#xff0c;按Option&#xff08;⌥&#xff09; Enter来插…

蜂鸟视图微程序:低代码赋能室内导航应用开发

随着数字化转型的深入&#xff0c;室内导航应用的需求日益增加。然而&#xff0c;传统的开发模式往往成本高、周期长、门槛高&#xff0c;给企业带来诸多挑战。 蜂鸟视图微程序应运而生&#xff0c;通过低代码技术赋能开发者&#xff0c;快速构建高性能室内地图导航应用&#…

#渗透测试#漏洞挖掘#红蓝攻防#SRC漏洞挖掘

免责声明 本教程仅为合法的教学目的而准备&#xff0c;严禁用于任何形式的违法犯罪活动及其他商业行为&#xff0c;在使用本教程前&#xff0c;您应确保该行为符合当地的法律法规&#xff0c;继续阅读即表示您需自行承担所有操作的后果&#xff0c;如有异议&#xff0c;请立即停…

http 502 和 504 的区别

首先看一下概念&#xff1a; 502&#xff1a;作为网关或者代理工作的服务器尝试执行请求时&#xff0c;从上游服务器接收到无效的响应。503&#xff1a;由于临时的服务器维护或者过载&#xff0c;服务器当前无法处理请求。这个状况是临时的&#xff0c;并且将在一段时间以后恢…