二叉树顺序存储结构

news2025/1/19 23:02:58

目录

1.二叉树顺序存储结构

2.堆的概念及结构

3.堆的相关接口实现

3.1 堆的插入及向上调整算法

3.1.1 向上调整算法

3.1.2 堆的插入

3.2 堆的删除及向下调整算法

3.2.1 向下调整算法

3.2.2 堆的删除

3.3 其它接口和代码实现

4.建堆或数组调堆的两种方式及复杂度分析

4.1 向上调整建堆

4.1.1 建堆步骤

4.1.2 代码实现

4.1.3 时间复杂度分析 --- O(N*logN)

4.2 向下调整建堆

4.2.1 建堆步骤

4.2.2 代码实现

4.2.3 时间复杂度分析 --- O(N)

5.堆的应用

5.1 堆排序(假设升序)

5.1.1 堆排序步骤

5.1.2 代码实现

5.2 TopK问题

5.2.1 TopK解决步骤

5.2.2 代码实现(数据从文件读取)


1.二叉树顺序存储结构

顺序存储结构就是用数组来存储,一般是用数组只适合来表示完全二叉树,因为不是完全二叉树会有空间浪费的现象。

二叉树的顺序存储结构在物理上是一个数组,在逻辑上是一个二叉树。

现实中我们通常把堆使用顺序存储结构的数组来存储,而什么又是堆呢?

需要注意的是这里的堆和操作系统虚拟进程地址空间中的堆是两回事,一个是数据结构,一个是操作系统中管理内存的一块区域分段。

2.堆的概念及结构

其实堆就是一个完全二叉树,堆中的所有元素按完全二叉树的顺序存储方式存储在一个一维数组中,并且满足:堆中某个结点的值总是不大于其父节点的值(大堆)或者堆中某个结点的值总是不小于其父节点的值(小堆)。

总结来说:

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

注意:所有的数组都可以表示成完全二叉树,但是他并不一定是堆。

3.堆的相关接口实现


补充:

对于具有n个结点的完全二叉树,如果按照从上至下从左至右的数组顺序对所有节点从0开始编号,则对于序号为i的结点有:
1.若i>0,i位置节点的双亲序号:(i-1)/2; i=0,i为根节点编号,无双亲节点
2.若2i+1 < n,左孩子序号:2i+1;若2i+1>=n(数组越界), 无左孩子

(叶子就是没有左孩子,也就是叶子结点的左孩子下标2i+1>=n越界)
3.若2i+2 < n,右孩子序号:2i+2; 若2i+2>=n(数组越界), 无右孩子


3.1 堆的插入及向上调整算法

  1. 先将元素插入到堆的末尾,即最后一个数组元素之后
  2. 插入之后如果堆的性质遭到破坏,插入的结点就根据向上调整算法找到合适位置即可

3.1.1 向上调整算法

那么什么是向上调整算法呢?如何实现?

例如: 堆:[4, 27, 11, 28, 35, 19, 15, 89, 2] Push:2

图片展示:

代码实现:

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

代码注意事项:

  • 除了插入的元素,堆中的其他元素都符合堆的性质,所以调整到 待调整节点和父节点 符合堆的性质即可退出调整。
  • 循环退出条件:待调整节点调整到根节点 或者 待调整节点和父节点的大小关系符合堆的性质。
  • while语句中的条件不能写parent >= 0,因为 (0-1)/ 2 之后依旧是0,并不符合预期的循环退出条件。
  • 如果调大堆,比较符号就用>;如果调小堆,比较符号就用<;方便以后更改。

3.1.2 堆的插入

注意:堆的物理结构是一个数组,也就是用顺序表实现的,插入时容量不够要记得扩容。

void HeapPush(Heap* php, HPDataType x)
{
	assert(php);

	if (php->capacity == php->size)
	{
		int newCapacity = php->capacity == 0 ? 4 : php->capacity * 2;
		HPDataType* tmp = (HPDataType*)realloc(php->a, sizeof(HPDataType) * newCapacity);
		if (tmp == NULL)
		{
			perror("HeapPush:");
			exit(-1);
		}

		php->a = tmp;
		php->capacity = newCapacity;
	}

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

3.2 堆的删除及向下调整算法

这里堆的删除是删除堆顶数据,因为只有堆顶数据才有意义(堆顶数据都是最值,删除堆顶后能获得次大或者次小的数)

这里我们能直接删除堆顶数据吗?很明显不可以,根据顺序表删除数据的特点,后面的元素会依次覆盖前面的元素,删除堆顶数据后,堆的结构就被破坏了。

其实删除思想是这样的:

  1. 将堆顶元素与堆中最后一个元素交换
  2. 删除堆中最后一个元素
  3. 将堆顶元素向下调整直到满足堆的结构

这种思想很巧妙,在后面的堆排序中也会用到这种思想。

3.2.1 向下调整算法

代码实现:

void AdjustDown(int* a, int size, int parent)
{
	int child = parent * 2 + 1;
	while (child < size)
	{
		if (child + 1 < size && a[child + 1] > a[child])
			child++;

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

代码注意事项:

  • 除了堆顶元素,堆中的其他元素都符合堆的性质,所以调整到 待调整节点和左右孩子 符合堆的性质即可退出调整。
  • 循环退出条件:待调整节点调整到叶子节点 或者 待调整节点和左右孩子节点的大小关系符合堆的性质。
  • 因为要判断越界,函数参数中要有数组大小size。
  • 向下调整时,我们要和该结点的左右孩子中较小的那个交换。代码设计时我们可以先默认左孩子小,再和右孩子进行比较得出较小的那个节点。

3.2.2 堆的删除

void HeapPop(Heap* php)
{
	assert(php);
	assert(php->size > 0);

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

3.3 其它接口和代码实现

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

typedef int HPDataType;

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

void Swap(int* a, int* b);
void AdjustUp(int* a, int child);//a:要调整的孩子结点所在的数组  child:要调整的孩子节点的下标
void AdjustDown(int* a, int size, int parent);//a:要调整的父亲结点所在的数组   size:数组的size   parent:要调整的父亲节点的下标

void HeapPrint(Heap* php);
void HeapInit(Heap* php);
void HeapDestory(Heap* php);
void HeapPush(Heap* php, HPDataType x);
void HeapPop(Heap* php);
HPDataType HeapTop(Heap* php);
int HeapSize(Heap* php);
int HeapEmpty(Heap* php);
#include "Heap.h"

void Swap(int* a, int* b)
{
	int tmp = *a;
	*a = *b;
	*b = tmp;
}

//调da堆
void AdjustUp(int* a, int child)
{
	int parent = (child - 1) / 2;
	while (child > 0)
	{
		if (a[child] > a[parent])
		{
			Swap(a + child, a + parent);
			child = parent;
			parent = (parent - 1) / 2;
		}
		else
		{
			break;
		}
	}
}

//调da堆
void AdjustDown(int* a, int size, int parent)
{
	int child = parent * 2 + 1;
	while (child < size)
	{
		if (child + 1 < size && a[child + 1] > a[child])
			child++;

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

void HeapInit(Heap* php)
{
	assert(php);

	php->a = NULL;
	php->capacity = php->size = 0;
}

void HeapDestory(Heap* php)
{
	free(php->a);
	php->capacity = php->size = 0;
}

void HeapPrint(Heap* php)
{
	for (int i = 0; i < php->size; i++)
	{
		printf("%d ", php->a[i]);
	}
	printf("\n");
}

void HeapPush(Heap* php, HPDataType x)
{
	assert(php);

	if (php->capacity == php->size)
	{
		int newCapacity = php->capacity == 0 ? 4 : php->capacity * 2;
		HPDataType* tmp = (HPDataType*)realloc(php->a, sizeof(HPDataType) * newCapacity);
		if (tmp == NULL)
		{
			perror("HeapPush:");
			exit(-1);
		}

		php->a = tmp;
		php->capacity = newCapacity;
	}

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

//这里删除的是堆顶数据,只有堆顶数据才有意义
//1.swap(堆顶数据,最后一个数据)
//2.删除最后一个数据
//3.堆顶数据AdjustDown
void HeapPop(Heap* php)
{
	assert(php);
	assert(php->size > 0);

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

HPDataType HeapTop(Heap* php)
{
	assert(php);
	assert(php->size > 0);

	return php->a[0];
}

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

	return php->size;
}

int HeapEmpty(Heap* php)
{
	assert(php);

	return php->size == 0;
}

4.建堆或数组调堆的两种方式及复杂度分析


前面我们说过,所有的数组都可以表示成完全二叉树,但是他并不一定是堆。那么我们如何将这个数组调整成堆呢?

我们首先会想到:把数组的值依次push到堆中,再把堆中数据依次赋值给数组,这样就把数组调整成了堆。但是实际应用中我们不会再写一个堆这样的数据结构,其次这种方式会有空间复杂度的消耗,所以我们不提倡这么做。

调堆方式有两种:向上调整建堆和向下调整建堆


4.1 向上调整建堆

4.1.1 建堆步骤

参考堆插入的思想,数组中的每个元素都可以看做新插入的节点。

从根结点开始调整,一直调整到最后一个结点。

想要调成成小堆:如果该结点小于父节点,就一直向上交换,直到不小于其父节点或者调整到根结点。

4.1.2 代码实现

int main()
{

	int a[] = { 27,15,19,28,35,11,4,89,2 };
	int size = sizeof(a) / sizeof(a[0]);
	//这里我们建小堆
	//方法一:向上调整算法
	for (int i = 0; i < size; i++)//从根结点开始调整,一直调整到最后一个结点。
	{
		AdjustUp(a, i);
	}

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

	return 0;
}

4.1.3 时间复杂度分析 --- O(N*logN)

​​

4.2 向下调整建堆

4.2.1 建堆步骤

在解释向下调整算法之前,先说明一下:

向下调整算法有一个前提:左右子树必须是一个堆,才能调整。

那么我们如何调整呢?

这里我们可以利用递归思想来解决:先从倒数第一个非叶子结点的子树开始调整,一直调整到根结点的树。也就是倒着调整。

4.2.2 代码实现

int main()
{

	int a[] = { 27,15,19,28,35,11,4,89,2 };
	int size = sizeof(a) / sizeof(a[0]);
	//这里我们建小堆
	//方法二:向下调整算法
	for (int i = (size - 1 - 1) / 2; i >= 0; i--)
	{
		AdjustDown(a, size, i);
	}

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

	return 0;
}

代码注意:

size - 1是最后一个数组元素的下标,对他减1除2后,就是他父节点的下标,也就是倒数第一个非叶子结点;

4.2.3 时间复杂度分析 --- O(N)

总结:向下调整算法建堆要比向上调整算法建堆要高效一些,并且向下调整算法要更常用(通常对堆顶数据进行向下调整操作),所以我们一般使用向下调整来建堆。

5.堆的应用


那么我们为什么要学堆呢?为什么要设计堆这种数据结构呢?

主要用于解决两个问题:

  • 排序
  • Topk问题:在N个数据中找最大或者最小的前k个(这里的N一般非常大)

注意:这里堆的应用问题和前面数组调堆的问题是同一个道理,我们不能使用堆数据结构的相关接口,需要在原生数组上进行操作。


5.1 堆排序(假设升序)

5.1.1 堆排序步骤

首先建堆这里就有一个坑了,正常思维来看,我们升序是建小堆,因为小堆的堆顶是最小值。

我们来看看升序建小堆的效率如何:

  1. 选出最小的数,放在第一个位置
  2. 最小的数删除后,剩下的看做一个堆。但是之前建好的关系都乱了,只能重新建堆,才能选出次小的数。

此时的时间复杂度:建堆的时间复杂度O(n),建了n次堆,时间复杂度O(n*n),这种效率还不如暴力遍历排序来的直接。

这里花里胡哨的建堆选堆顶的最值进行排序,结果效率和冒泡差不多,显然不是我们想要的结果。

那么堆排序到底是怎么排的呢,下面给出步骤

1.建堆

升序:建大堆

降序:建小堆

2.利用堆删除思想进行排序

(1)升序建的大堆,堆顶是最大元素,

(2)把堆顶(最大元素)和最后一个元素交换,

(3)最后一个元素(最大值)不看做堆中元素,堆顶元素向下调整,堆顶元素就变成了次大值,

(4)依次类推,重复(2)~(3)

可以计算一下这里的时间复杂度来和上面的建小堆方法来比较一下:

建大堆:n次向下调整,每次调整时间复杂度为O(logn),所以时间复杂度为:O(n*logn)

建小堆的时间复杂度O(n*n),很显然,数据非常多时,这两种方法的效率是天差地别

5.1.2 代码实现

//堆排序,升序
void HeapSort(int* a, int n)
{
	//第一步:建大堆
	for (int i = (n - 1 - 1) / 2; i >= 0; i--)
	{
		AdjustDown(a, n, i);
	}

	//第二步:堆删除思想进行排序(依次选数,调堆)
	for (int i = n - 1; i > 0; i--)
		//最后一步交换后就一个堆顶元素(最小值),AdjustDown没有进行调整
	{
		//将堆顶元素和最后一个元素交换,交换后最后一个元素不计入堆内
		Swap(&a[0], &a[i]);
		AdjustDown(a, i , 0);//这里第二个参数是数据个数,最后一个元素不计入堆内,正好是i
	}
}

代码注意事项:

  • 以后我们建堆都用向下调整建堆。首先高效,其次堆排序的选数调堆也用的向下调整。
  • 这里的循环变量i是最后一个元素的下标,也正好是交换后数组的元素个数(交换后最后一个元素不计入数组内)
  • 最后一步i == 1时,交换后就一个堆顶元素(最小值),AdjustDown没有进行调整,但这一步也要执行,因为a[0]和a[1]要进行交换。

5.2 TopK问题

5.2.1 TopK解决步骤

Topk问题就是在N个数中找最大或者最小的前k个。(这里的N一般非常大,大到内存装不下)

第一次碰到这个问题,我们的惯性思维会去怎么解决呢?

  • 方法一:先排降序,前k个最大
  • 方法二:N个数依次插入大堆,pop k次,每次取的都是堆顶数据

但是当N非常大时,甚至内存都放不下,很显然这两种方法不靠谱。

我们可以算一下时间复杂度:

方法一:O(N*logN)

方法二:O(N+klogN) 建堆:N,k次pop :klogN

我们直接来说说Topk问题的实际解决办法:

  1. 用数据集合的前k个元素来建堆

前k个最大的元素:建小堆

前k个最小的元素:建大堆

2.用剩余的N-k个元素依次与对顶元素比较,找最大(小)的k个:比堆顶大(小),替换,向下调整。

3.最后堆中的k个元素就是最大(最小)的k个数

计算时间复杂度:O(k+(N-k)logk)~O(Nlogk)

5.2.2 代码实现(数据从文件读取)

int* TopK(int k)
{
	int* retArr = (int*)malloc(sizeof(int) * k);
	//打开文件
	FILE* pf = fopen("data,txt", "r");
	if (pf == NULL) 
	{
		perror("TopK:");
		exit(-1); 
	}

	//前k个数据读入数组
	for (int i = 0; i < k; i++)
	{
		fscanf(pf, "%d", &retArr[i]);
	}

	//数组建堆(小堆)
	for (int i = (k - 2) / 2; i >= 0; i--)
	{
		AdjustDown(retArr, k, i);
	}

	//剩余N-k个数据,依次和堆顶数据比较
	for (int i = 0; i < N - k; i++) 
	{
		int x;
		fscanf(pf, "%d", &x);
		if (x > retArr[0])
		{
			retArr[0] = x;
			AdjustDown(retArr, k, 0);
		}
	}

	fclose(pf);

	return retArr; 
}

void testTopK()
{
	int* arr = TopK(10);
	for (int i = 0; i < 10; i++)
		printf("%d ", arr[i]);
	printf("\n");

	free(arr);
}

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

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

相关文章

SpringCloud Eureka搭建会员中心服务提供方-集群

&#x1f600;前言 本篇博文是关于SpringCloud Eureka搭建会员中心服务提供方-集群&#xff0c;希望你能够喜欢 &#x1f3e0;个人主页&#xff1a;晨犀主页 &#x1f9d1;个人简介&#xff1a;大家好&#xff0c;我是晨犀&#xff0c;希望我的文章可以帮助到大家&#xff0c;您…

【Transformer系列】深入浅出理解Attention和Self-Attention机制

一、参考资料 课件&#xff1a;10_Transformer_1.pdf 视频&#xff1a;Transformer模型(1/2): 剥离RNN&#xff0c;保留Attention 二、Attention without RNN Attention模型可以看到全局的信息。 本章节以 Seq2Seq&#xff08; (encoder decoder)&#xff09; 模型为例&…

算法通关18关 | 回溯模板如何解决复原IP问题

18关的前几篇文章看过之后&#xff0c;对回溯的模板问题基本解题思路就知道了&#xff0c;就是固定的for循环问题&#xff0c;外层for循环控制横向&#xff0c;递归控制纵向&#xff0c;还要考虑撤销操作和元素是否能被重复利用问题&#xff0c;重复利用的情景较少&#xff0c;…

【物联网】简要解释RTK(Real-Time Kinematic)>>实时动态差分定位

引言&#xff1a; RTK&#xff08;Real-Time Kinematic&#xff09;技术是一种基于差分GPS的高精度定位技术&#xff0c;它通过实时通信和数据处理&#xff0c;能够提供厘米级甚至亚米级的定位精度。RTK技术在许多领域都得到了广泛应用&#xff0c;如测绘、航空航天、农业等。本…

C++ 内存模型 Memory Model

CPU 现在CPU都是多核结构&#xff0c;每个核心都有自己的一级缓存&#xff0c;二级缓存&#xff0c;以及共享的三级缓存。如下图&#xff0c;其中一级缓存分为指令缓存IL1和数据缓存DL1&#xff0c;二级缓存L2 256kB&#xff0c;三级缓存 L3 8MB。 从上图可以看出L3比L2大得多…

从零开始:PostgreSQL入门完全指南

&#x1f337;&#x1f341; 博主猫头虎&#xff08;&#x1f405;&#x1f43e;&#xff09;带您 Go to New World✨&#x1f341; &#x1f405;&#x1f43e;猫头虎建议程序员必备技术栈一览表&#x1f4d6;&#xff1a; &#x1f6e0;️ 全栈技术 Full Stack: &#x1f4da…

【漏洞复现】大华智慧园区综合管理平台前台任意文件上传漏洞

文章目录 前言声明一、简介二、影响范围三、资产搜索四、漏洞测试四、修复建议前言 大华智慧园区综合管理平台存在前台任意文件上传漏洞,攻击者可通过特定Payload获取服务器敏感信息,进而获取服务器控制权限。 声明 请勿利用文章内的相关技术从事非法测试,由于传播、利用…

树和二叉树的相关概念及结构

目录 1.树的概念及结构 1.1 树的概念 1.2 树的相关概念 1.3 树的表示 1.3.1 孩子兄弟表示法 1.3.2 双亲表示法 1.4 树的实际应用 2.二叉树的概念及结构 2.1 二叉树的概念 2.2 特殊的二叉树 2.3 二叉树的性质 2.4 二叉树的存储 2.4.1 顺序存储 2.4.2 链式存储 1.树…

idea内存不足

The IDE is running low on memory and this might affect performance. Please consider increasing available heap. 参考 【IDEA】The IDE is running low on memory and this might affect performance._datagrip提示ide內存不足_Coder_贾俊浩的博客-CSDN博客 (亲测好用…

零基础教你搭建日用百货线上商城小程序

在当今的数字化时代&#xff0c;小程序商城已成为许多企业和商家的首选平台&#xff0c;尤其是日用百货行业。通过小程序商城&#xff0c;消费者可以更方便地购买各类日用品&#xff0c;商家也可以提高销售效率、扩大市场影响力。本文将详细介绍如何从零开始制作一个日用百货小…

论文解读 | MVSNet:非结构化多视图立体的深度推理

原创 | 文 BFT机器人 这篇论文的题目是《MVSNet: Depth Inference for Unstructured Multi-view Stereo》。这是一篇关于深度学习在多视角立体视觉&#xff08;MVS&#xff09;中的应用的研究论文。MVS任务的目标是从多个视角的图像中还原出三维场景的深度信息&#xff0c;从而…

佛山融资融券(两融)开户利率最低能做到多少?5%!

佛山融资融券(两融)开户利率最低能做到多少?5%! 具体佛山融资融券(两融)开户利率最低能做到多少&#xff0c;需要根据不同的券商政策而定。不同的券商可能具有不同的优惠政策和开户条件&#xff0c;因此开户前应该仔细了解券商的政策和条件。 融资融券是投资者通过证券公司向…

VScode配置Ros环境

VScode配置Ros环境 VScode配置Ros环境 1. VSCode下载 直接百度搜索VScode&#xff0c;去官网安装Ubuntu版本的VScode&#xff0c;下载完成之后用Ububtu Software进行安装。 2. VScode配置 2.1 功能包配置 下载完成之后直接打开ROS的工作目录&#xff0c;之后安装ROS包。 …

LeetCode 238. 除自身以外数组的乘积

题目链接 力扣&#xff08;LeetCode&#xff09;官网 - 全球极客挚爱的技术成长平台 题目解析 使用前缀和进行解决该题&#xff0c;只不过与之前前缀和不同的是这个题目计算前缀和的时候不需要计算当前元素&#xff0c;也就是当前位置前缀和的值其实是不包含当前元素的前缀和。…

递归二进制【典中典】

洛谷 #include<iostream> #include<algorithm> using namespace std; const int N3e7; int path[N]; int n,idx;//我们使用二进制的形式来解决这个问题 //移位运算的优先级高于按位与的优先级 void dfs(int x,int st) {if(xn){path[idx]st;return;}//选----1dfs(x1…

C语言之指针进阶篇(3)

目录 思维导图 回调函数 案例1—计算器 案例2—qsort函数 关于qsort函数 演示qsort函数的使用 案例3—冒泡排序 整型数据冒泡排序 回调函数搞定各类型冒泡排序 cmp_int比较大小 cmp传参数 NO1. NO2. 解决方案 交换swap 总代码 今天我们学习指针难点之回调函数…

网站搭建从零开始(0)--域名的选择与解析

目录 确定用途 购买域名 使用可靠的注册商购买域名 想好域名关键词 检查域名是否可用 添加域名到购物车并完成购买 域名的解析 登录注册商账户 选择要配置的域名 进入DNS解析设置 添加DNS记录 保存配置 检查解析是否生效 提示 确定用途 在购买域名之前&#xf…

基于 MATLAB 的电力系统动态分析研究【IEEE9、IEEE68系节点】

&#x1f4a5;&#x1f4a5;&#x1f49e;&#x1f49e;欢迎来到本博客❤️❤️&#x1f4a5;&#x1f4a5; &#x1f3c6;博主优势&#xff1a;&#x1f31e;&#x1f31e;&#x1f31e;博客内容尽量做到思维缜密&#xff0c;逻辑清晰&#xff0c;为了方便读者。 ⛳️座右铭&a…

TypeScript接口和类

&#x1f3ac; 岸边的风&#xff1a;个人主页 &#x1f525; 个人专栏 :《 VUE 》 《 javaScript 》 ⛺️ 生活的理想&#xff0c;就是为了理想的生活 ! 目录 接口 类 在 TypeScript 中&#xff0c;接口&#xff08;Interfaces&#xff09;和类&#xff08;Classes&#xff…

长尾关键词挖掘软件-免费的百度搜索关键词挖掘

嗨&#xff0c;大家好&#xff01;今天&#xff0c;我想和大家聊一聊长尾关键词挖掘工具。作为一个在网络世界里摸爬滚打多年的人&#xff0c;我对这个话题有着一些个人的感悟和见解&#xff0c;希望能与大家分享。 首先&#xff0c;让我坦白一点&#xff0c;长尾关键词挖掘工具…