二叉树和堆的讲解和实现(图解+代码/C语言)

news2024/11/14 17:42:39

今天和大家分享的是二叉树的实现,关于遍历二叉树部分均采用递归的方式实现,最后附上部分OJ题供大家练习。
在这里插入图片描述

文章目录

  • 一、树的概念及结构
    • 1.1 树的概念
    • 1.2 树的相关概念
    • 1.3 树的表示
  • 二、二叉树的概念及结构
    • 2.1 概念
    • 2.2 二叉树的性质
    • 2.3 二叉树的存储结构
      • 2.3.1 二叉树的顺序结构
      • 2.3.2 二叉树的链式结构
  • 三、二叉树的顺序结构及实现
    • 3.1 二叉树的顺序结构
    • 3.2 堆的概念及结构
    • 3.3 堆的实现
      • 3.3.1 堆的向下调整算法
      • 3.3.2 堆的向上调整算法
      • 3.3.3 堆的创建
      • 3.3.4 堆的插入
      • 3.3.5 堆的删除
      • 3.3.6 堆的应用
        • 堆排序
        • TOP_K 问题
  • 四、二叉树的链式结构及实现
    • 4.1 二叉树的遍历
      • 4.1.1 DFS 深度优先遍历(递归)
        • 前序遍历(先根遍历)
        • 中序遍历(中根遍历)
        • 后序遍历(后根遍历)
      • 4.1.2 BFS 广度优先遍历(队列)
        • 层序遍历
    • 4.2 判断二叉树是否为完全二叉树
    • 4.3 二叉树的节点个数
    • 4.4 二叉树叶子节点的个数
    • 4.5 二叉树第 k 层的节点个数
    • 4.6 二叉树的高度
    • 4.7 二叉树创建和销毁

一、树的概念及结构

1.1 树的概念

树是一种非线性的数据结构,其理论模型像一棵倒立的树,因此得名。我们通常将最上方的结点成为根结点,它是没有先驱结点的。而其余结点都有且仅有一个先驱结点,也就是说,树结构中不存在通路,也就是不存在交集。
如下图,是几种树结构。
在这里插入图片描述
但是下面几种,则不是树结构
在这里插入图片描述

1.2 树的相关概念

我们用以下图片来讲解树的相关概念
在这里插入图片描述

节点的度:一个节点含有的子树的个数称为该节点的度。如上图:A的为2,B的度为3,D的度为0。
叶节点或终端节点:度为0的节点称为叶节点。 如上图:D、H、E、F、G为叶节点。
非终端节点或分支节点:度不为0的节点被称为分支结点。如上图:A、B、C为分支节点。
双亲节点或父节点:若一个节点含有子节点,则这个节点称为其子节点的父节点。如上图:A是B的父节点,B是D、H、E的父节点。
孩子节点或子节点:一个节点含有的子树的根节点称为该节点的子节点。 如上图:B是A的孩子节点,D、H、E都是B的孩子节点。
兄弟节点:具有相同父节点的节点互称为兄弟节点。 如上图:B、C是兄弟节点,D、H、E是兄弟结点,但E、F不是兄弟结点。
树的度:一棵树中,最大的节点的度称为树的度。如上图:B的度最大,为3,因此树的度为3。
节点的层次:从根开始定义起,根为第1层,根的子节点为第2层,以此类推。如上图:A为第一层,B、C为第二层,其余为第三层。
树的高度或深度:树中节点的最大层次。如上图:树的高度为3。
堂兄弟节点:双亲在同一层的节点互为堂兄弟。如上图:D、H、E和F、G互为堂兄弟节点。
祖先:从根到该节点所经分支上的所有节点。如上图:A是所有节点的祖先,B是D、H、E的祖先。
子孙:以某节点为根的子树中任一节点都称为该节点的子孙。如上图:所有节点都是A的子孙,D、H、E为B的子孙。
森林:由m(m>0)棵互不相交的树的集合称为森林。一棵树的情况也是森林,如上图就是一个森林。

1.3 树的表示

此处讲的是树,而树的每一个结点不一定确定,因此我们最常用的是孩子兄弟表示法,如下。

typedef int Datatype;
struct Node
{
	struct Node* first_Child; // 第一个孩子结点
	struct Node* next_Brother; // 指向下一个兄弟结点
	Datatype data; // 数据域
}

在这里插入图片描述

二、二叉树的概念及结构

2.1 概念

二叉树,故名思意就是分叉只有两个,即每一个树结点最多存在两个孩子结点。同时需要注意,这两个孩子结点有左右之分(如下图),因此二叉树也是有序树
在这里插入图片描述

2.2 二叉树的性质

  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的结点有:
    • 若 i > 0,i 位置节点的双亲序号:( i - 1 ) / 2;i = 0时,i 为根节点编号,无双亲节点;
    • 若 2i + 1 < n,左孩子序号:2i + 1;2i + 1 >= n 则无左孩子;
    • 若 2i + 2 < n,右孩子序号:2i + 2;2i + 2 >= n 则无右孩子。

2.3 二叉树的存储结构

二叉树一般有两种储存方式,一种是顺序结构,另一种则是链式结构。但是前者的使用条件有些限制。

2.3.1 二叉树的顺序结构

顺序结构的应用场景通常局限于完全二叉树,主要原因在于,非完全二叉树用顺序结构储存会存在空间的浪费,以及不便于查找是否存在结点。
在这里插入图片描述

2.3.2 二叉树的链式结构

相比之下二叉树更适合链式结构,一方面是空间利用率高,另一方是更符合其逻辑结构。二叉树其实就像有分叉的链表,分叉处便是决定左右孩子处。
在这里插入图片描述

三、二叉树的顺序结构及实现

3.1 二叉树的顺序结构

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

3.2 堆的概念及结构

堆是一种数据结构,是一种特殊的二叉树。堆首先是一种完全二叉树,其次,每个结点的孩子结点均不大于或不小于其父结点
设存在集合 K = {k0,k1,k2,…,kn},将 K 按照完全二叉树的顺序依次存入一维数组,且满足( ki <= k2i+1 , ki <= k2i+2,i = 0,1,2,…,n),则称为小堆(小根堆,最小堆),若上述不等式为 >= ,则称为大堆(大根堆,最大堆)。
在这里插入图片描述

3.3 堆的实现

为了方便大小堆的创建,使用了预处理和必要的重定义,内容如下:

typedef int HPDataType;
typedef struct Heap
{
	HPDataType* _a;
	int _size;
	int _capacity;
}Heap;
#define judge <
// < 为小堆,> 为大堆

3.3.1 堆的向下调整算法

对于一个结点,当其左右子树均为同一类型堆(大堆或小堆)时,可以对该节使用向下调整算法,首先我们需要找出较小的孩子结点,然后使该结点和较小的孩子结点交换,这样就使得该节点和其左右子树共同成为堆。
在这里插入图片描述

3.3.2 堆的向上调整算法

相比于向下调整算法,向上调整算法对子树和父结点没有要求,仅需不断比较然后前移交换即可。
在这里插入图片描述

3.3.3 堆的创建

我们通过读取一个数组的内容来创建一个堆。这里主要用两种方法进行创建,一种是向上调整,一种是向下调整。
向上调整的思路:我们每插入一个数字就向上调整,这样就保证了已经插入的所有数据组成堆,插入的时间复杂度为 O(log2n),建堆的时间复杂度为 O(n*log2n);
向下调整的思路:我们先将数组全部插入堆中,然后按照其逻辑结构,从第一个非叶子结点的结点开始向下调整,插入的时间复杂度为O(log2n),建堆时间复杂度为 O(n)。

void HeapCreate(Heap* hp, HPDataType* a, int size)
{
	hp->_a = (HPDataType*)malloc(sizeof(HPDataType) * size * 2);
	hp->_size = 2 * size;
	hp->_capacity = 0;
	int adWay(0); // 0为向下调整, 1为向上调整
	switch (adWay)
	{
	case 0:
		hp->_capacity = size;
		for (int i = 0; i < size; i++)
			hp->_a[i] = a[i];
		for (int i = (size - 2) / 2; i >= 0; i--)
			AdjustDown(hp, i);
		break;
	case 1:
		for (int i = 0; i < size; i++, hp->_capacity++)
		{
			hp->_a[i] = a[i];
			AdjustUp(hp, i);
		}
		break;
	}
}

3.3.4 堆的插入

我们已经知道了向下调整算法和向上调整算法,但是对于一个已知的堆,插入数据如果使用向下 调整的话,那么不仅需要将数组整体后移,同时数组也不一定保持堆结构,还需要重新建堆,这样一来时间复杂度就上升了,因此我们通常采用向上调整算法。
向上调整思路:由于原本堆结构完好,我们只需再最后插入新的数据,将其向上调整即可,不会破坏原本的堆结构。

void HeapPush(Heap* hp, HPDataType x)
{
	assert(hp);
	if (hp->_capacity == hp->_size)
	{
		hp->_size = hp->_size == 0 ? 10 : 2 * hp->_size;
		hp->_a = (HPDataType*)realloc(hp->_a, sizeof(HPDataType) * (hp->_size));
		if (hp->_a == nullptr)
		{
			perror("realloc fail!\n");
			exit(0);
		}
	}
	hp->_a[hp->_capacity] = x;
	AdjustUp(hp, hp->_capacity);
	hp->_capacity++;
}

3.3.5 堆的删除

通常意义上,堆的删除是指删除堆顶元素,常用的解决思路是覆盖删除法,其思路为,将堆的最后一个元素覆盖到堆顶元素,然后对新的堆顶元素采用向下调整算法即可。

void HeapPop(Heap* hp)
{
	assert(hp);
	assert(!HeapEmpty(hp));
	exchange(hp->_a[hp->_capacity - 1], hp->_a[0]);
	hp->_capacity--;
	AdjustDown(hp, 0);
}

3.3.6 堆的应用

堆作为一种有序二叉树,其主要作用就是解决排序类问题。

堆排序

堆排序实际上就是一个建堆和出堆的过程,在这个过程我们需要注意的是升序建大堆,降序建小堆

// 堆的判空
int HeapEmpty(Heap* hp)
{
	assert(hp);
	return hp->_capacity == 0;
}
// 取堆顶的数据
HPDataType HeapTop(Heap* hp)
{
	assert(hp);
	assert(!HeapEmpty(hp));
	return hp->_a[0];
}

void HeapSort(int* arr, int size)
{
	Heap x;
	HeapCreate(&x, arr, size);
	for (int i = 0; i < size; i++)
	{
		arr[i] = HeapTop(&x);
		HeapPop(&x);
	}
}

TOP_K 问题

TOP_K 问题:在一个集合中,找出前 K 大(小)的元素

这类问题我们需要注意的是:找大建小,找小建大
以找出前 K 大的元素为例:先用集合的前 K 个元素建立小堆,此时堆顶元素是堆内最小的元素,然后遍历集合剩下的其他元素,如果遍历元素小于堆顶元素,则覆盖堆顶元素并向下调整,遍历完之后,堆内元素就是 TOP_K 元素了。

  • 集合元素个数较小时,直接全部元素建堆即可
void TopK_small(int* a, int n, int k)
{
	Heap x;
	HeapCreate(&x, a, n);
	HeapPrint(&x);
	for (int i = 0; i < k; i++)
	{
		printf("%d ", HeapTop(&x));
		HeapPop(&x);
	}
	printf("\n");
}
  • 集合元素较多,需要存放在文件中时,只能建 K 个元素的小堆
void PrintTopK_big(const char* FILE_Name, int n, int k)
{
	HPDataType p(0);
	Heap x;
	FILE* data = fopen(FILE_Name, "r");
	if (data == nullptr)
	{
		perror("FILE open fail!\n");
		exit(0);
	}
	HeapCreate(&x, NULL, 0);
	for (int i = 0; i < k; i++)
	{
		fscanf(data, "%d", &p);
		HeapPush(&x, p);
	}
	while(fscanf(data, "%d", &p) != EOF)
	{
		if (p > HeapTop(&x))
		{
			x._a[0] = p;
			AdjustDown(&x, 0);
		}
	}
	HeapPrint(&x);
	HeapDestory(&x);
}

四、二叉树的链式结构及实现

二叉树的很多问题都可以用递归实现,因此遇到问题首要的想法是能不能转换为左右子树问题求解

4.1 二叉树的遍历

二叉树的核心内容就是遍历,学好遍历我们才能掌握如何按照某种顺序创建二叉树。

4.1.1 DFS 深度优先遍历(递归)

深度优先的意思就是,遍历的顺序主要先探索到某一个子树的最深结点,再依次探索其它子树。

前序遍历(先根遍历)

前序遍历:遇到每个结点,先打印出该节点的值,再去遍历左右子树
在这里插入图片描述

void BinaryTreePrevOrder(BTNode* root)
{
	if (root)
	{
		cout << root->_data << " ";
		BinaryTreePrevOrder(root->_left);
		BinaryTreePrevOrder(root->_right);
	}
	else
	{
		cout << "NULL ";
	}
	
}

中序遍历(中根遍历)

中序遍历:遇到每个结点,先进入左子树,直到遍历完左子树时,打印出该节点的值,再去遍历右子树在这里插入图片描述

void BinaryTreeInOrder(BTNode* root)
{
	if (root)
	{
		BinaryTreeInOrder(root->_left);
		cout << root->_data << " ";
		BinaryTreeInOrder(root->_right);
	}
	else
	{
		cout << "NULL ";
	}
}

后序遍历(后根遍历)

后序遍历:遇到每个结点,先遍历左子树,直到没有左子树时,遍历右子树,当左右子树都遍历完时,打印该节点的值
在这里插入图片描述

void BinaryTreePostOrder(BTNode* root)
{
	if (root)
	{
		BinaryTreePostOrder(root->_left);
		BinaryTreePostOrder(root->_right);
		cout << root->_data << " ";
	}
	else
	{
		cout << "NULL ";
	}
}

4.1.2 BFS 广度优先遍历(队列)

广度优先的意思就是,优先遍历完同一深度的所有结点,再遍历其他深度。

层序遍历

我们用队列的方式遍历每一层,我们先将二叉树的根节点入队,随后我们遍历队内元素,对于每个元素,我们将其左右孩子结点入队,同时将该结点出队,直到队内无其他元素。
在这里插入图片描述在这里插入图片描述

// QueueInit 队列初始化
// QueuePush 元素入队尾
// QueueFront 获取队首元素
// QueuePop 队首元素出队

void BinaryTreeLevelOrder(BTNode* root)
{
	int size = root ? 1 : 0;
	Queue* arr = (Queue*)malloc(sizeof(Queue));
	QueueInit(arr);
	QueuePush(arr, root);
	BTNode* rec = NULL;
	while (size)
	{
		rec = QueueFront(arr);
		if (rec)
		{
			cout << rec->_data << " ";
			if (rec->_left)
			{
				size++;
				QueuePush(arr, rec->_left);
			}
			else
			{
				QueuePush(arr, NULL);
			}
			if (rec->_right)
			{
				size++;
				QueuePush(arr, rec->_right);
			}
			else
			{
				QueuePush(arr, NULL);
			}
		}
		if (!rec)
			cout << "NULL ";
		if (rec)
			size--;
		QueuePop(arr);
	}
	cout << endl;
}

4.2 判断二叉树是否为完全二叉树

判断完全二叉树的方法需要用到我们上面学到的层序遍历,我们依次遍历每一层,如果二叉树为完全二叉树,那么在第一次遇到空结点之后,就不会再有非空结点了。

int BinaryTreeComplete(BTNode* root)
{
	Queue* arr = (Queue*)malloc(sizeof(Queue));
	QueueInit(arr);
	QueuePush(arr, root);
	int flag = 0;
	BTNode* rec = NULL;
	while (!QueueEmpty(arr))
	{
		rec = QueueFront(arr);
		if (rec->_left)
		{
			if (flag)
				return 0;
			QueuePush(arr, rec->_left);
		}
		else
			flag = 1;
		if (rec->_right)
		{
			if (flag)
				return 0;
			QueuePush(arr, rec->_right);
		}
		else
			flag = 1;
		QueuePop(arr);
	}
	return 1;
}

4.3 二叉树的节点个数

这里我们就需要用到之前提到的转换为左右子树求解问题,一棵二叉树的结点数可以分为左右子树的结点数加上自身。

int BinaryTreeSize(BTNode* root)
{
	if (!root)
		return 0;
	return root == NULL ? 0 : BinaryTreeSize(root->_right) + BinaryTreeSize(root->_left) + 1;
}

4.4 二叉树叶子节点的个数

叶子结点的个数为左右子树中叶子结点的个数

int BinaryTreeLeafSize(BTNode* root)
{
	if (!root)
		return 0;
	return (root->_left == NULL && root->_right == NULL) ? 1 : 
		((root->_left == NULL ? 0 : BinaryTreeLeafSize(root->_left)) + (root->_right == NULL ? 0 : BinaryTreeLeafSize(root->_right)));
}

4.5 二叉树第 k 层的节点个数

我们同样是转换为左右子树问题,不过需要注意的是,这里每次递归的子树求解问题发生了一定改变,我们求第 k 层,就是求相对于第二层的第 k-1 层的节点数。 具体的代码如下。

int BinaryTreeLevelKSize(BTNode* root, int k)
{
	if (!root || k <= 0)
		return 0;
	if (k == 1)
		return 1;
	return BinaryTreeLevelKSize(root->_left, k - 1) + BinaryTreeLevelKSize(root->_right, k - 1);
}

4.6 二叉树的高度

同样的,二叉树的高度也可以转换为左右子树求解问题,一颗二叉树的高度为自身加上左右子树中最高的高度
需要注意的是,这里要先计算高度,再用三目运算符,不然会重复计算一次左右子树高度,会浪费一定时间。

int BinaryTreeHigh(BTNode* root)
{
	if (!root)
		return 0;
	int leftHigh = BinaryTreeHigh(root->_left);
	int rightHigh = BinaryTreeHigh(root->_right);
	return 1 + (leftHigh > rightHigh ? leftHigh : rightHigh);
}

4.7 二叉树创建和销毁

这里讲解按照前序遍历的方式创建二叉树,给出一个字符串,用 # 代表空结点,按照前序遍历的方式创建二叉树。

BTNode* BinaryTreeCreate(BTDataType* a, int size, int* returnsize)
{
	if (a[*returnsize] == '#')
	{
		(*returnsize)++;
		return NULL;
	}
	else if (*returnsize < size)
	{
		BTNode* new_node = (BTNode*)malloc(sizeof(BTNode));
		if (!new_node)
			perror("malloc fail\n");
		new_node->_data = a[*returnsize];
		(*returnsize)++;
		new_node->_left = BinaryTreeCreate(a, size, returnsize);
		new_node->_right = BinaryTreeCreate(a, size, returnsize);
		return new_node;
	}
	return NULL;
}

二叉树的销毁则更加简单,只需要遍历整棵树,然后 free 掉每一个结点即可。需要注意的是需要传入二级指针,不然无法 free 掉树的根节点。

void BinaryTreeDestory(BTNode** root)
{
	if (!(*root))
		return;
	if ((*root)->_left)
		BinaryTreeDestory(&((*root)->_left));
	if ((*root)->_right)
		BinaryTreeDestory(&((*root)->_right));
	free(*root);
	*root = NULL;
}

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

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

相关文章

proxy代理与reflect反射

proxy代理与reflect 在这之前插入一个知识点arguments&#xff0c;每个函数里面都有一个arguments&#xff0c;执行时候不传默认是所有参数&#xff0c;如果传了就是按顺序匹配&#xff0c;箭头函数没有 代理函数 代理对象也就是生成一个替身&#xff0c;然后这个替身处理一切的…

【深度学习】认识神经网络

上一章——过拟合与正则化 从本章开始我们将学习深度学习的内容&#xff1a;包括神经网络和决策树等高级算法 文章目录神经网络的生物学原理神经网络的算法结构案例——图像感知神经网络的前向传播神经网络的生物学原理 在最开始&#xff0c;人们想要构建一个能够模拟生物大脑…

Python __doc__属性:查看文档

在使用 dir() 函数和 __all__ 变量的基础上&#xff0c;虽然我们能知晓指定模块&#xff08;或包&#xff09;中所有可用的成员&#xff08;变量、函数和类&#xff09;&#xff0c;比如&#xff1a;import string print(string.__all__)程序执行结果为&#xff1a;[ascii_lett…

Zabbix 构建监控告警平台(六)

监控TCP连接监控MySQL监控php-fpm监控 Apache监控 MySQL A-B监控磁盘I/O1.监控TCP连接 1.1 tcp状态简介 netstat中的各种状态&#xff1a; CLOSED 初始&#xff08;无连接&#xff09;状态。 LISTEN 侦听状态&#xff0c;等待远程机器的连接…

自动驾驶规控课程学习——决策规划

行为决策系统的规划1 行为决策基础1.1 基本概念与任务行为类型&#xff1a;系统输入输出&#xff1a;输入&#xff1a;定位、感知、地图等输出&#xff1a;决策意图小例子&#xff1a;1.2决策系统的评价与挑战评价指标挑战&#xff08;1&#xff09;决策密度&#xff08;2&…

卡尔曼滤波器与DSP实现

卡尔曼滤波器是利用系统状态方程&#xff0c;结合测量结果对系统状态进行进行最优估计的算法。本文介绍它的主要公式&#xff0c;并举例在C6000 DSP上实现。 推荐资料 KalmanFilter.NETUnderstanding Kalman Filters卡尔曼滤波与组合导航原理 “If you can’t explain it sim…

rust 程序设计语言入门(1)

本文是阅读《Rust程序设计语言》的学习记录&#xff0c;配合视频《Rust编程语言入门教程》食用更佳 环境搭建 windows下载rustup_init.exe&#xff0c;点击安装&#xff0c;默认选择msvc的toolchain&#xff0c;一路default即可 解决下载慢的问题&#xff0c;在powershell中修…

libxlsxwriter条件格式

今天来看一个libxlsxwriter的高级用法&#xff1a;一个条件格式的示例。 说它“高级”&#xff0c;也是基于非Excel专家的小白们的视角。对&#xff0c;没错&#xff0c;本小白正是这样的小白。 1 一个简单的问题 来看我们今天的场景问题&#xff1a;有一列数据&#xff0c;有…

操作系统(一): 进程和线程,进程的多种状态以及进程的调度算法

文章目录前言一、进程和线程1. 进程2. 线程二、进程和线程的区别(面试常问)三、进程调度算法3.1. 批处理系统3.2. 交互式系统3.2.1 时间片轮转3.2.2 优先级调度3.2.3 多级别反馈队列3.3. 实时系统四、进程的状态五、进程同步5.1 什么是进程同步5.2 进程同步应该遵循的几点原则前…

Qt 学习(四) —— QGridLayout栅格布局

目录一、QGridLayout布局规则二、创建QGridLayout三、成员函数1. 控件间距2. 可拉伸控件&#xff08;弹簧&#xff09;3. 最小行高/列宽4. 行数和列数5. 锁定纵横比6. 添加控件7. 添加布局8. 设置栅格布局原点位置9. 操作布局项9.1 访问布局项9.2 删除布局项9.3 通过索引获取布…

Git教程个人分享:如何将一个本地项目上传至远程仓库的流程

前言&#xff1a; 今天来分享一下&#xff0c;关于Git的一些教程&#xff0c;同时这也是我自己曾今学习Git时候的笔记&#xff0c;之所以更&#xff0c;也是方便后期自己可以去回顾&#xff0c;当然后面也会出一部分关于Git其他操作方面的内容。 这次我们分享的是&#xff0c…

基于JavaScript的Web端股票价格查看器——大道

&#x1f436; 基于JavaScript的Web端股票价格查看器——大道 一、项目背景 当下互联网发展迅速&#xff0c;互联网已经不断向传统金融领域渗透。在互联网上有大量金融领域的数据&#xff0c;如何利用好这些数据&#xff0c;对于投资者来说是十分重要的一件事情。股票价格实时…

JavaSE学习day4_01 循环for,while,do...while

1. 循环高级 1.1 无限循环 for、while、do...while都有无限循环的写法。 最为常用的是while格式的。 因为无限循环是不知道循环次数的&#xff0c;所以用while格式的 代码示例&#xff1a; while(true){} 1.2 跳转控制语句&#xff08;掌握&#xff09; 跳转控制语句&…

MySQL 插入数据

数据库与表创建成功以后&#xff0c;需要向数据库的表中插入数据。在 MySQL 中可以使用 INSERT 语句向数据库已有的表中插入一行或者多行元组数据。 你可以通过 mysql> 命令提示窗口中向数据表中插入数据&#xff0c;或者通过PHP脚本来插入数据。 语法 以下为向MySQL数据表…

51单片机——步进电机实验,小白讲解,相互学习

步进电机简介&#xff1a; 步进电机是将电脉冲信号转变为角位移或多线位移的开源控制元件。在非超载的情况下&#xff0c;电机的转速&#xff0c;停止的位置只取决于脉冲信号的频率和脉冲数&#xff0c;而不受负载变化的影响&#xff0c;即给电机加一个脉冲信号&#xff0c;电机…

Android - 自动系统签名

一、系统签名 以下是两类应用开发场景&#xff1a; 普通应用开发&#xff1a;使用公司自定义 keystore 进行签名&#xff0c;如&#xff1a;微信、支付宝系统应用开发&#xff1a;使用 AOSP 系统签名或厂商自定义 keystore 进行签名&#xff0c;如&#xff1a;设置、录音 系…

数学建模拓展内容:卡方检验和Fisher精确性检验(附有SPSS使用步骤)

卡方检验和Fisher精确性检验卡方拟合度检验卡方独立性检验卡方检验的前提假设Fisher精确性检验卡方拟合度检验 卡方拟合度检验概要&#xff1a;卡方拟合度检验也被称为单因素卡方检验&#xff0c;用于检验一个分类变量的预期频率和观察到的频率之间是否存在显著差异。 卡方拟…

第一部分:简单句——第二章:简单句的补充

简单句的核心构成&#xff1a;一主一谓 主语/宾语/表语 可以变成名词/代词/doing/to do 谓语动词有四种核心变化&#xff1a;三态 一否 时态语态情态否定 简单句的核心&#xff1a;将简单句给写对 简单句的补充&#xff1a;将简单句给写的更好、更充分 简单句的补充 1、限定…

计算机网络之HTTP04ECDHE握手解析

DH算法 离散读对数问题是DH算法的数学基础 &#xff08;1&#xff09;计算公钥 &#xff08;2&#xff09;交换公钥&#xff0c;并计算 对方公钥^我的私钥 mod p 离散对数的交换幂运算交换律使二者算出来的值一样&#xff0c;都为K k就是对称加密的秘钥 2. DHE算法 E&#…

DNS 原理入门指南(一)

DNS 是互联网核心协议之一。不管是上网浏览&#xff0c;还是编程开发&#xff0c;都需要了解一点它的知识。 本文详细介绍DNS的原理&#xff0c;以及如何运用工具软件观察它的运作。我的目标是&#xff0c;读完此文后&#xff0c;你就能完全理解DNS。 一、DNS 是什么&#xff1…