[数据结构初阶]二叉树

news2024/11/18 8:51:17

各位读者老爷好,鼠鼠我现在浅浅介绍一些关于二叉树的知识点,在各位老爷茶余饭后的闲暇时光不妨看看,鼠鼠很希望得到各位老爷的指正捏!

开始介绍之前,给各位老爷看一张风景照,有读者老爷知道在哪里吗?

第一个在评论区答出正确答案的老爷鼠鼠会联系你,有惊喜捏! 

目录

1.树

1.1.树的概念 

1.2.树的相关概念

2.二叉树

2.1.二叉树的概念

2.2.特殊的二叉树

2.3.二叉树的存储结构

3.堆

3.1.堆的概念及分类

3.2.堆的顺序存储的实现(二叉树的顺序存储的实现) 

3.2.1.堆(大堆)总览

3.2.2. 定义堆

3.2.3.堆的初始化

3.2.4.堆的插入

3.2.5.堆的删除 

3.2.6.获取堆顶的数据

3.2.7.堆的数据个数

3.2.8.堆的判空

3.2.9.堆的销毁

4.运行结构分析

好的,我们在介绍二叉树之前需要了解树的概念!

1.树

1.1.树的概念 

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

画个图给各位老爷看看:

我们可以看到:

1.有一个特殊的结点(A节点),称为根结点,根节点没有前驱结点。

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

3.因此,树是递归定义的。

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


树形结构除了根节点外,每个节点有且只有一个父节点。如图就不是一个树形结构。

1.2.树的相关概念

介绍下面相关概念鼠鼠以下面树形结构举例:

1.节点的度:一个节点含有的子树的个数称为该节点的度; 如上图:A节点的度为3,B节点的度为2。

2.叶节点或终端节点:度为0的节点称为叶节点; 如上图:G、H、I、J、K和L节点为叶节点。

3.非终端节点或分支节点:度不为0的节点; 如上图:B、C、D、E和F节点为分支节点。

4.双亲节点或父节点:若一个节点含有子节点,则这个节点称为其子节点的父节点; 如上图:A是B的父节点,也是C的父节点,也是D的父节点。

5.孩子节点或子节点:一个节点含有的子树的根节点称为该节点的子节点; 如上图:B、C和D都是A的孩子节点。

6.兄弟节点:具有相同父节点的节点互称为兄弟节点; 如上图:J、K和L互为兄弟节点。

7.树的度:一棵树中,最大的节点的度称为树的度; 如上图:树的度为3。

8.节点的层次:从根开始定义起,根为第1层,根的子节点为第2层,以此类推。

9.树的高度或深度:树中节点的最大层次; 如上图:树的高度为4。

10.堂兄弟节点:双亲节点在同一层的节点互为堂兄弟节点;如上图:F和G互为堂兄弟节点。

11.节点的祖先:从根到该节点所经分支上的所有节点;如上图:A是所有节点的祖先。

12.子孙:以某节点为根的子树中任一节点都称为该节点的子孙。如上图:所有节点都是A的子孙。

13.森林:由m(m>0)棵互不相交的树的集合称为森林。

有了以上铺垫,我们来了解二叉树的概念!

2.二叉树

2.1.二叉树的概念

一棵二叉树是结点的一个有限集合,该集合:
1. 或者为空。
2. 由一个根节点加上两棵别称为左子树和右子树的二叉树组成。

说简单点呢,二叉树是一颗特殊的树,这颗树的度最大为2,就像是对这颗树的节点进行了计划生育,最多只能生两个节点宝宝。 

从图可以看出:

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

3.二叉树的子树也都是二叉树,既然是二叉树就可以为空树。

注意:对于任意的二叉树都是由以下几种情况复合而成的:

2.2.特殊的二叉树

1. 满二叉树:一个二叉树,如果每一个层的结点数都达到最大值,则这个二叉树就是满二叉树。

很容易知道,满二叉树各层的节点个数构成一个以1为首项,以2为公比的等比数列。如果一个二叉树的层数为K,用等比数列的求和公式或者错位相减法就能得到满二叉树节点的和为2^K-1。

那么反过来,如果知道了满二叉树的节点个数为N,那么它的层数就为log2(N+1),约等log2(N)。


2.完全二叉树:完全二叉树是效率很高的数据结构,完全二叉树是由满二叉树而引出来的。对于深度为K的,有n个结点的二叉树,当且仅当其每一个结点都与深度为K的满二叉树中编号从1至n的结点一一对应时称之为完全二叉树。 要注意的是满二叉树是一种特殊的完全二叉树。

说简单易懂的话呢,就是说完全二叉树如果有K层,那么前K-1层的节点数都达到最大值,第K层的节点数不一定达到最大值,但是最后一层的节点从左到右必须连续。

跟上面分析的一样,我们知道完全二叉树的前K-1层的节点数总和为2^(K-1)-1 。最后一层(第K层)的节点个数最少为1个,最多为2^(K-1)个。那么完全二叉树的节点总数范围就是2^(K-1)到2^K-1。

相同的,我们如果知道完全二叉树的节点个数为N,那么它的层数也大概是log2(N)。

2.3.二叉树的存储结构

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


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

也许各位老爷不明白为啥可以将二叉树存储到一个数组中呢?

其实我们将二叉树在逻辑上想象成一颗二叉树,在逻辑上我们将二叉树的节点一层一层按顺序存储到数组中。我们通过以下规律可以控制二叉树捏:

1.左孩子节点下标=父节点下标*2+1;

2.右孩子节点下标=父节点下标*2+1;

3.父节点下标=(左孩子节点下标-1)/2=(右孩子节点下标-1)/2; 

那么为什么只有完全二叉树适合用顺序存储捏?

因为用顺序存储(就是用数组存储)的话我们要保证父节点下标和孩子节点下标满足上面三条规律才能控制好二叉树。而非完全二叉树要想满足上面三条规律的话,我们会有空间浪费,如下图:

如果根据错误对应来存储二叉树节点的话,就不符合上面的三条规律了! 所以顺序存储适合完全二叉树,非完全二叉树也可以存储但不适合!


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

本篇博客不细讲二叉树的链式存储,我们主要搞定顺序存储,链式存储后面博客再介绍!


既然介绍二叉树的顺序存储,而现实中使用中只有堆才会使用数组来存储,鼠鼠就要介绍堆这个概念并实现堆(也就是实现二叉树的顺序存储)捏 !

3.堆

堆就是一颗二叉树,堆一般是将数组数据看做一颗完全二叉树。现实中我们通常把堆(一种二叉树)使用顺序结构的数组来存储,需要注意的是这里的堆和操作系统虚拟进程地址空间中的堆是两回事,本篇博客讲的堆一个是数据结构,另外一个是操作系统中管理内存的一块区域分段。

3.1.堆的概念及分类

简单讲,堆就是一颗二叉树,不过这颗二叉树对数据摆放有要求:

大堆:任意一个父节点数据要大于或等于它的任意孩子节点数据;

小堆:任意一个父节点数据要小于或等于它的任意孩子节点数据;

堆只有大堆和小堆之分,没有中堆这个说法!我们来看一道题,方便理解:

1.下列关键字序列为堆的是:()
A 100,60,70,50,32,65
B 60,70,65,50,32,100
C 65,100,70,32,50,60
D 70,65,100,32,50,60
E 32,50,100,70,65,60
F 50,100,70,65,60,32

答案选A,为什么A是堆,其他不是捏?我们来看:

A选项:

 还原成逻辑结构的完全二叉树的话,很明显是一个大堆!

B选项:

 还原成逻辑结构的完全二叉树的话,很明显不是大堆也不是小堆,比如说60<65,符合小堆要求;但是70>32,又符合大堆要求;所以不是堆!

其他选项分析相同,鼠鼠就不解释了!

3.2.堆的顺序存储的实现(二叉树的顺序存储的实现) 

也许有读者老爷不明白为啥用堆的顺序存储的实现代表二叉树的顺序存储的实现?因为堆的顺序存储的实现是有意义的,它能很好的管理数据而并非单纯的存储数据。如果单纯存储数据我们大可不必用二叉树来存储!至于堆能实现什么功能,再下面的讲解中鼠鼠会浅谈。

因为堆有大堆和小堆之分,鼠鼠我实现大堆的顺序存储为例,小堆的实现相信老爷们可以举一反三自己搞定!

3.2.1.堆(大堆)总览

我们先总体看堆的顺序存储实现完的三个文件,有兴趣的老爷可以放到一个工程下面玩玩:

1.heap.h

#pragma once
#include<stdio.h>
#include<assert.h>
#include<stdlib.h>
#include<stdbool.h>

//大堆


typedef int HeapDataType;

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

//堆的初始化
void HeapInit(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 HeapDestroy(Heap* HP);

2.heap.c 

#define _CRT_SECURE_NO_WARNINGS
#include"heap.h"

void HeapInit(Heap* HP)
{
	assert(HP);
	HP->a = NULL;
	HP->capacity = HP->size = 0;
}

void swap(HeapDataType* parent, HeapDataType* child)
{
	HeapDataType tmp = *parent;
	*parent = *child;
	*child = tmp;
}

void AdjustUp(HeapDataType* a, int childcoordinate)
{
	int parentcoordinate = (childcoordinate - 1) / 2;
	while (childcoordinate > 0)
	{
		if (a[parentcoordinate] < a[childcoordinate])
		{
			swap(&a[parentcoordinate], &a[childcoordinate]);
			childcoordinate = parentcoordinate;
			parentcoordinate = (parentcoordinate - 1) / 2;
		}
		else
		{
			break;
		}
	}
}

void HeapPush(Heap* HP, HeapDataType x)
{
	assert(HP);

	//扩容
	if (HP->capacity == HP->size)
	{
		int newcapacity = HP->capacity == 0 ? 4 : HP->capacity * 2;
		HeapDataType* tmp = (HeapDataType*)realloc(HP->a, sizeof(HeapDataType) * newcapacity);
		if (tmp == NULL)
		{
			perror("realloc fail");
			exit(-1);
		}
		HP->a = tmp;
		HP->capacity = newcapacity;
	}
	HP->a[HP->size++] = x;

	//向上调整
	AdjustUp(HP->a, HP->size - 1);
}

void AdjustDown(HeapDataType* a, int size, int parentcoordinate)
{
	int childcoordinate = parentcoordinate * 2 + 1;
	
	while (childcoordinate < size)
	{
		if (a[childcoordinate] < a[childcoordinate + 1]&&childcoordinate+1<size)
		{
			childcoordinate++;
		}
		if (a[childcoordinate] > a[parentcoordinate])
		{
			swap(&a[childcoordinate], &a[parentcoordinate]);
			parentcoordinate = childcoordinate;
			childcoordinate = childcoordinate * 2 + 1;
		}
		else
		{
			break;
		}
	}
}

void HeapPop(Heap* HP)
{
	assert(HP);
	assert(HP->size > 0);
	swap(&HP->a[0], &HP->a[HP->size - 1]);
	HP->size--;

	//向下调整
	AdjustDown(HP->a, HP->size,0);
}

HeapDataType HeapTop(Heap* HP)
{
	assert(HP->size > 0);
	return HP->a[0];
}

int HeapSize(Heap* HP)
{
	return HP->size;
}

bool HeapEmpty(Heap* HP)
{
	return HP->size == 0;
}

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

3.test.c

#define _CRT_SECURE_NO_WARNINGS
#include"heap.h"

int main()
{
	int a[] = { 100,90,80,110,75,3 };
	Heap p;
	HeapInit(&p);
	for (int i = 0; i < sizeof(a) / sizeof(a[0]); i++)
	{
		HeapPush(&p, a[i]);
	}
	for(int i=0;i<p.size;i++)
	{
		printf("%d ", p.a[i]);
	}
	printf("\n");
	while (!HeapEmpty(&p))
	{
		printf("%d ", HeapTop(&p));
		HeapPop(&p);
	}
	return 0;
}

4.运行结果 

运行结果为啥是这样的,我们一会分析! 

3.2.2. 定义堆
typedef int HeapDataType;

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

这里重命名int为HeapDataType,大大方便后续代码的维护!用a指向后来动态开辟的连续内存,该连续内存用来存储堆的数据,size用来记录堆的数据个数或者指向堆最后一个数据的下一个,capacity用来记录连续内存可放入数据的容量。

这里用一个结构体来定义堆如代码所示,定义的跟顺序表一样,但是我们这里表示的堆对数据的摆放要求是大堆或者小堆,而顺序表就没有这个要求! 

3.2.3.堆的初始化
void HeapInit(Heap* HP)
{
	assert(HP);
	HP->a = NULL;
	HP->capacity = HP->size = 0;
}

断言防止传入结构体变量地址为空(下面这点不在赘述。) 不妨将a置空,将capacity和size置0。

3.2.4.堆的插入
void swap(HeapDataType* parent, HeapDataType* child)
{
	HeapDataType tmp = *parent;
	*parent = *child;
	*child = tmp;
}

void AdjustUp(HeapDataType* a, int childcoordinate)
{
	int parentcoordinate = (childcoordinate - 1) / 2;
	while (childcoordinate > 0)
	{
		if (a[parentcoordinate] < a[childcoordinate])
		{
			swap(&a[parentcoordinate], &a[childcoordinate]);
			childcoordinate = parentcoordinate;
			parentcoordinate = (parentcoordinate - 1) / 2;
		}
		else
		{
			break;
		}
	}
}

void HeapPush(Heap* HP, HeapDataType x)
{
	assert(HP);

	//扩容
	if (HP->capacity == HP->size)
	{
		int newcapacity = HP->capacity == 0 ? 4 : HP->capacity * 2;
		HeapDataType* tmp = (HeapDataType*)realloc(HP->a, sizeof(HeapDataType) * newcapacity);
		if (tmp == NULL)
		{
			perror("realloc fail");
			exit(-1);
		}
		HP->a = tmp;
		HP->capacity = newcapacity;
	}
	HP->a[HP->size++] = x;

	//向上调整
	AdjustUp(HP->a, HP->size - 1);
}

堆的插入我们表面上是在数组中(动态开辟的空间)尾插,但我们逻辑上要想象成数据插入在树的底层,并调整数据使其符合大堆要求。 这里的精髓是调整数据,也就是代码中的向上调整函数。关于向上调整函数,鼠鼠画一个图方便理解吧:

我们可以看到堆的插入代码中主要的时间消耗在向上调整函数上,而向上调整函数最多调整层数-1次,完全二叉树的层数前面算过大概是log2(N),结合堆的插入代码分析,堆的插入时间复杂度是O(logN)。

3.2.5.堆的删除 
void swap(HeapDataType* parent, HeapDataType* child)
{
	HeapDataType tmp = *parent;
	*parent = *child;
	*child = tmp;
}

void AdjustDown(HeapDataType* a, int size, int parentcoordinate)
{
	int childcoordinate = parentcoordinate * 2 + 1;
	
	while (childcoordinate < size)
	{
		if (a[childcoordinate] < a[childcoordinate + 1]&&childcoordinate+1<size)
		{
			childcoordinate++;
		}
		if (a[childcoordinate] > a[parentcoordinate])
		{
			swap(&a[childcoordinate], &a[parentcoordinate]);
			parentcoordinate = childcoordinate;
			childcoordinate = childcoordinate * 2 + 1;
		}
		else
		{
			break;
		}
	}
}

void HeapPop(Heap* HP)
{
	assert(HP);
	assert(HP->size > 0);
	swap(&HP->a[0], &HP->a[HP->size - 1]);
	HP->size--;

	//向下调整
	AdjustDown(HP->a, HP->size,0);
}

 对于堆的删除来说,数据结构规定删除的是堆顶的数据(根节点数据),当然删除完堆顶数据后,我们必须将剩余数据再构成大堆。

为啥删除的是堆顶的数据捏?因为堆顶的数据是堆中所有数据中最大的,删除完堆顶数据后新的堆顶就是“次大的”,这样的话我们可以做到选数的功能,后面可以更好体会。

完成堆的删除,我们采取将堆顶的数据根最后一个数据一换,然后删除数组最后一个数据,再进行向下调整算法。堆的删除过程画图理解如下:

同样堆的删除主要的时间消耗在向下调整函数上,而向下调整函数最多也是调整层数-1次,那么堆的删除时间复杂度也是O(logN)。

完成堆的删除,我们为什么不采用跟顺序表头删一样的挪动覆盖方法呢?因为如果采用了挪动覆盖之后剩余的数据下标关系全乱了,不成堆了;且挪动覆盖的时间复杂度是O(N)。

3.2.6.获取堆顶的数据
HeapDataType HeapTop(Heap* HP)
{
	assert(HP->size > 0);
	return HP->a[0];
}

简单,返回a[0]即可。

3.2.7.堆的数据个数
int HeapSize(Heap* HP)
{
	return HP->size;
}

根据设定,返回size即可。

3.2.8.堆的判空
bool HeapEmpty(Heap* HP)
{
	return HP->size == 0;
}

当堆为空返回真,堆不为空返回假,所以容知上面代码逻辑可以实现逻辑自恰 。

3.2.9.堆的销毁
void HeapDestroy(Heap* HP)
{
	free(HP->a);
	HP->a = NULL;
	HP->capacity = HP->size = 0;
}

销毁动态开辟的空间并将a置成NULL,将capacity和size置成0即可。

4.运行结构分析

各位老爷先看结果如下:

第一条语句:定义一组数组a,元素为100、90、80、110、75和3。

第二条语句:定义一个结构体变量p(创建堆p)。

第三条语句:调用HeapInit函数初始化p。

接下来for循环语句:取数组a成员依次入堆。由HeapPush函数可知,执行完这个for循环语句后,a元素构成大堆!

接下来for循环语句:打印大堆,得出110 100 80 90 75 3。可以看出这样存储确实是大堆。

接下来while循环:当堆不为空时,打印堆顶数据并删除堆顶数据!得出结果为110 100 90 80 75 3,是递减的。

从这里我们可以看出一点堆的管理数据的能力:

删除堆顶数据就是删除了(大)堆中最大的数据并将剩下的数据再成(大)堆,这样新的(大)堆的新的堆顶数据就是“次大的”,再删除堆顶数据再成(大)堆…………这样出来的数据就是递减的(小堆同理成递增)。

感谢阅读,如有不足,恳请指正,谢谢! 

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

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

相关文章

02-MySQL数据库的基本使用与密码设置

一、服务端口 3306端口和33060端口&#xff0c;是我们启动数据库后开启的监听端口&#xff1b; 3306端口&#xff1a;是我们MySQL服务的监听端口&#xff0c;用来连接数据库使用&#xff1b; 33060端口&#xff1a;MySQL-shell服务的端口&#xff0c;MySQL-shell是MySQL架构集群…

基于霍夫检测(hough变换)的人眼瞳孔定位,Matlab实现

博主简介&#xff1a; 专注、专一于Matlab图像处理学习、交流&#xff0c;matlab图像代码代做/项目合作可以联系&#xff08;QQ:3249726188&#xff09; 个人主页&#xff1a;Matlab_ImagePro-CSDN博客 原则&#xff1a;代码均由本人编写完成&#xff0c;非中介&#xff0c;提供…

Java设计模式 | 抽象工厂模式

抽象工厂模式 工厂方法模式中考虑的是一类产品的生产&#xff0c;如幼儿园只培养小朋友&#xff0c;鞋厂只生产鞋子。这些工厂只生产同种类产品&#xff0c;同种类产品称为同等级产品&#xff0c;即工厂方法模式只考虑生产同等级的产品&#xff0c;但是在现实生活中许多工厂都…

一文读懂代理与反向代理

反向代理与正向代理在代理服务器的角色和工作方式上有所不同&#xff0c;这两种代理方式主要取决于代理服务器代理的对象和处理方式。 区别&#xff1a; 正向代理&#xff1a; 正向代理是代理客户端的请求&#xff0c;客户端通过正向代理访问其他服务或资源。客户端知道自己正…

注解总结,Java中的注解,springboot中的注解

注解总结 1、Junit 开始执行的方法&#xff1a;初始化资源&#xff0c;执行完之后的方法&#xff1a;释放资源 测试方法&#xff0c;必须是&#xff1a;公有、非静态、无参无返回值的 在一个类中&#xff0c;可以定义多个测试方法&#xff0c;每个测试方法可以单独运行&#…

Qt/C++通用跨平台Onvif工具/支持海康大华宇视华为天地伟业等/云台控制/预置位管理/工程调试利器

一、前言 在安防视频监控行业&#xff0c;Onvif作为国际标准&#xff0c;几乎主要的厂商都支持&#xff0c;不仅包含了国内的厂商&#xff0c;也包括主要的国际厂商&#xff0c;由于有了这个标准的存在&#xff0c;使得不同设备不同安防平台之间&#xff0c;能够接入各个厂家的…

堆(数据结构)

堆的概念及结构 如果有一个关键码的集合K { &#xff0c; &#xff0c; &#xff0c;…&#xff0c; }&#xff0c;把它的所有元素按完全二叉树的顺序存储方式存储在一个一维数组中&#xff0c;并满足&#xff1a; < 且 < ( > 且 > ) i 0&#xff0c;1&#xff…

Linux技巧|Awk 比较运算符

在处理一行文本中的数字或字符串值时&#xff0c;使用比较运算符过滤文本或字符串对于 Awk 命令来说非常方便。 在 Awk 系列的这一部分中&#xff0c;我们将了解如何使用比较运算符过滤文本或字符串。 比较运算符 Awk 中的比较运算符用于比较数字或字符串的值&#xff0c;它们包…

Sticks POJ 1011

题目链接:1011 -- Sticks 题目描述: 评注: 好暴力的一个题&#xff0c;就当锻炼自己的搜索技巧了,本题中的一些剪枝技巧还蛮有意思的&#xff0c;可以品味一下 思路: 枚举深搜剪枝 看完题面&#xff0c;其实感觉没啥特别巧妙的性质&#xff0c;而且数据范围比较小&#xff0…

双指针(滑动窗口)-算法刷题

一.移动零&#xff08;. - 力扣&#xff08;LeetCode&#xff09;&#xff09; 算法思想 &#xff1a; 设置两个指针left,right&#xff0c;将数组分为三块[0,left]为不为0的元素&#xff0c;[left1,right-1]为0元素&#xff0c;[right,num.size()-1]为未扫描的区域&#xff0c…

【动态规划】Leetcode 746. 使用最小花费爬楼梯

【动态规划】Leetcode 746. 使用最小花费爬楼梯 解法 ---------------&#x1f388;&#x1f388;题目链接&#x1f388;&#x1f388;------------------- 解法 &#x1f612;: 我的代码实现> 动规五部曲 ✒️确定dp数组以及下标的含义 dp[i] 表示跳跃到第 i 层&#x…

10一维数组

一维数组是什么&#xff1f; // 相同的数据类型的元素组成的有序集合 // 创建一个一维数组 // 例如&#xff1a; int a[5]{1,2,3,4,5}; //说明&#xff1a; // int:表示数据类型 // a:表示数组名 // [5]&#xff1a;数组的长度 // {1,2,3,4,5} &#xff1a;数组的元素 // 一…

适配器模式与桥接模式-灵活应对变化的两种设计策略大比拼

​&#x1f308; 个人主页&#xff1a;danci_ &#x1f525; 系列专栏&#xff1a;《设计模式》 &#x1f4aa;&#x1f3fb; 制定明确可量化的目标&#xff0c;坚持默默的做事。 &#x1f680; 转载自&#xff1a;设计模式深度解析&#xff1a;适配器模式与桥接模式-灵活应对变…

SSD268G系列高性价比DIS CAM方案

技术详情 星宸科技&#xff08;Sigmastar&#xff09;高端智能显示芯片SSD268G&#xff0c;适用于高分辨率的视频录制和播放应用。作为一款高集成度&#xff0c;多功能的高性能处理器&#xff0c;其内置ARM Cortex-A53双核64位处理器&#xff0c;最高工作主频为1.2GHz&#xf…

下载安装anaconda和pytorch的详细方法,以及遇到的问题和解决办法

下载安装Anaconda 首先需要下载Anaconda&#xff0c;可以到官网Anaconda官网或者这里提供一个镜像网站去下载anaconda镜像网站 安装步骤可参考该文章&#xff1a;Anaconda安装步骤&#xff0c;本篇不再赘述 注意环境变量的配置&#xff0c;安装好Anaconda之后一定要在环境变量…

初始Java篇(JavaSE基础语法)(2)(逻辑控制)

个人主页&#xff08;找往期文章包括但不限于本期文章中不懂的知识点&#xff09;&#xff1a;我要学编程(ಥ_ಥ)-CSDN博客 目录 逻辑控制 顺序结构 分支结构 if语句 switch 语句 循环结构 while 循环 for 循环 do while 循环 输入输出 输出到控制台 从键盘输入 …

<REAL-TIME TRAFFIC OBJECT DETCTION FOR AUTONOMOUS DRIVING>论文阅读

Abstract 随着计算机视觉的最新进展&#xff0c;自动驾驶迟早成为现代社会的一部分&#xff0c;然而&#xff0c;仍有大量的问题需要解决。尽管现代计算机视觉技术展现了优越的性能&#xff0c;他们倾向于将精度优先于效率&#xff0c;这是实时应用的一个重要方面。大型目标检测…

使用git+ssh访问github,避免下载资源失败

一、创建github账户之后&#xff0c;记住注册邮箱和账户名 我的邮箱&#xff1a;yuanyan23mails.ucas.ac.cn 账户名&#xff1a;thekingofjumpshoot 下边的相关位置需要用自己的邮箱和用户名替代 二、输入本地生成秘钥和公钥命令&#xff0c;并且生成公私钥对 ssh-keygen …

WooCommerce Appointments: 建立会话或预订类型网站的详细教程- US Domain Center主机

第一步&#xff1a;了解会话或预订类型网站 会话或预订类型网站是一种让用户可以预订会议、咨询、活动或其他服务的网站。这种类型的网站通常使用预订插件来管理日程安排、接受预订、处理付款等功能。 第二步&#xff1a;注册 US Domain Center 账户 美国域名中心US Domain …

linux之centos7vmware虚拟机的安装

目录 一、下载合适的vmware和操作系统镜像安装文件 来自引用文章的软件下载本片文章使用的软件下载 二、根据教程进行安装 三、网络配置解说 四、配置网络 编辑虚拟机网络 对VMWARE虚拟机网络进行配置 设置虚拟机网络为NAT模式 设置自定义网络为 VMnet8(NAT模式) 编辑li…