二叉树(C/C++)

news2025/2/26 11:35:59

        本篇将较为详细的介绍二叉树的相关知识,以及二叉树的实现。对于二叉树的相关知识,本篇介绍了其概念、特殊的二叉树、性质还有存储结构。

        接着对于实现二叉树的每个函数都有其思路讲解,主要的函数分为:遍历:前中后序遍历;结点个数:二叉树总结点个数、叶子结点个数、第k层结点个数、二叉树的高度;建立销毁二叉树:前序遍历建立二叉树与销毁二叉树;层序遍历与判断是否为完全二叉树;在二叉树中查找值为x的结点。

        然后测试了所有的代码,其中使用到的队列代码本篇就没有讲解,若详细查看队列代码可去主页查找对应的文章。

        目录如下:

目录

1. 二叉树的概念及结构

1.1 概念 1.2 特殊的二叉树 1.3 二叉树的性质 1.4 二叉树的存储结构

2. 二叉树的实现

2.1 二叉树的遍历 2.2 计算结点个数 2.3 通过前序遍历建立二叉树、销毁二叉树 2.4 层序遍历与判断二叉树是否为完全二叉树 2.5 二叉树查找值为x的结点。

3. 所有代码

3.1 All.h 3.2 Queue.c 3.3 BinaryTree.c 3.4 test.c 3.5 测试结果

1. 二叉树的概念及结构

1.1 概念

        一颗二叉树是结点的一个有限集合,该集合:或者为空(空二叉树)或一个根节点加上两颗别称为左子树和右子树的二叉树组成

        从上图中可以看出:

        1.二叉树中不存在度大于2的结点;       

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

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

 1.2 特殊的二叉树

        特殊的二叉树包括两种:满二叉树、完全二叉树。

        满二叉树:一个二叉树,若每一层的节点数都达到最大值,则这个二叉树就为满二叉树。也就是说,若一个二叉树的层数为 k ,且结点总数为 2^k - 1 ,则它就是满二叉树。

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

1.3 二叉树的性质

        1.若规定根节点的层数为1,则一颗非空二叉树的第 i 层上最多有2^( i - 1 )个结点

        2.若规定根节点的层数为1,则深度为 h 的二叉树的最大节点数为 2^h - 1

        3.对任意一棵二叉树,若度为0的叶子节点个数为n0,度为2的分支结点个数为n2,则有n0 = n2 + 1

        4.若规定根节点的层数为1,若具有n个结点的满二叉树的深度:h=\log _2\left( n+1 \right)

        5.对于具有n个结点的完全二叉树,若按照从上至下从左至右的数组顺序对所有结点从0开始编号,则对于序号为i的结点:

                a.若 i > 0, i 位置结点的双亲结点序号:( i - 1 ) / 2;i = 0时,表示为根节点编号,无双亲结点。

                b.若 2 * i + 1 < n, 左孩子序号:2 * i + 1,若2 * i + 1 >= n 表示无左孩子。

                c.若 2 * i + 2 < n, 右孩子序号:2 * i + 2,若2 * i + 2 >= n 表示无右孩子。

1.4 二叉树的存储结构

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

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

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

2. 二叉树的实现

        接下来将实现二叉树,其中主要实现的为链式二叉树,其中包括几个部分:

        1.二叉树的遍历:二叉树的前序、中序、后序遍历;

        2.计算结点个数:计算二叉树结点个数、叶子结点个数、第k层结点的个数和二叉树的高度;

        3.通过前序遍历构建二叉树、二叉树的销毁;

        4.层序遍历二叉树、判断二叉树是否为完全二叉树;

        5.二叉树查找值为x的结点。

        主要就为以上几个部分,其中需要使用到的其他数据结构为:队列。

2.1 二叉树的遍历

        对于二叉树的前序、中序和后序遍历本文使用递归遍历;对于层序遍历就使用非递归遍历,首先介绍的为前序、中序和后序遍历:

        对于递归遍历,主要思路为:递归分治,将问题分为当前问题和子问题,以及递归结束的调节。

        以前序遍历为主:前序遍历的主要关键为,若当前结点不为NULL,我们就将其遍历,然后接着遍历左子树、右子树。若当前结点为NULL,我们就返回当前函数,此次递归结束。中序就为:先遍历左子树,然后遍历根节点、最后遍历右子树;后序为先遍历左子树,然后遍历右子树,最后遍历根节点。代码如下:

// 二叉树前序遍历 
void BinaryTreePrevOrder(BTNode* root) {
	if (root) {
		printf("%c ", root->data);
		BinaryTreePrevOrder(root->left);
		BinaryTreePrevOrder(root->right);
	}
}

// 二叉树中序遍历
void BinaryTreeInOrder(BTNode* root) {
	if (root) {
		BinaryTreeInOrder(root->left);
		printf("%c ", root->data);
		BinaryTreeInOrder(root->right);
	}
}

// 二叉树后序遍历
void BinaryTreePostOrder(BTNode* root) {
	if (root) {
		BinaryTreePostOrder(root->left);
		BinaryTreePostOrder(root->right);
		printf("%c ", root->data);
	}
}

2.2 计算结点个数

        接下来将用递归分别计算二叉树的结点个数、叶子结点个数、第k层结点的个数以及计算二叉树的高度;先以计算二叉树的个数为例:我们要计算二叉树的结点个数,可以将其分为:左子树结点个数+右子树结点个数+1(根节点个数),其中的核心思想就为此,若遇到空结点则返回0,代码具体实现如下:

// 二叉树节点个数
int BinaryTreeSize(BTNode* root) {
	return root == NULL ? 0 : BinaryTreeSize(root->left) + BinaryTreeSize(root->right) + 1;
}

        计算叶子结点个数:我们首先需要找到叶子结点的特点,左右子树为NULL,所以我们可以将此递归分为:遇到叶子结点返回1、左子树叶子结点+右子树叶子结点,当访问到空结点时即返回0,具体代码实现如下:

// 二叉树叶子节点个数
int BinaryTreeLeafSize(BTNode* root) {
	if (root == NULL)
		return 0;
	if (root->left == NULL && root->right == NULL)
		return 1;
	return BinaryTreeLeafSize(root->left) + BinaryTreeLeafSize(root->right);
}

        计算第k层的结点个数:既然需要计算第k层的结点个数,那么我们肯定需要锁定到第k层,所以我们需要在每一次递归时都将k减一,然后在k==1时返回1。所以我们可以将这个递归分为:当k==1时返回1,递归返回左子树第k个结点+右子树第k个结点。具体实现代码如下:

// 二叉树第k层节点个数
int BinaryTreeLevelKSize(BTNode* root, int k) {
	assert(k > 0);
	if (k == 1)
		return 1;
	if (root == NULL)
		return 0;
	return BinaryTreeLevelKSize(root->left, k - 1) + BinaryTreeLevelKSize(root->right, k - 1);
}

        计算二叉树的高度:对于计算二叉树的高度,我们首先需要知道的是左子树更高还是右子树更高,计算出子树的最高高度,然后加上根节点,所以可以将其递归分为:比较左右子树的高度,然后返回最高的高度+1(加根节点),若递归到空节点返回0,具体实现如下:

// 二叉树的高度
int BinaryTreeHeight(BTNode* root) {
	if (root == NULL)
		return 0;
	int leftHeight = BinaryTreeHeight(root->left);
	int rightHeight = BinaryTreeHeight(root->right);
	return leftHeight > rightHeight ? leftHeight + 1 : rightHeight + 1;
}

2.3 通过前序遍历建立二叉树、销毁二叉树

        对于用此方法构建二叉树,我们只需要输入二叉树的前序序列,然后根据此建立出对应的二叉树。其中输入的前序序列需要包含空结点,空结点用”#“表示。

        此函数传入的参数为:字符串(前序序列)、记录当前递归处理到了前序序列字符串的哪一个位置的整数的地址。函数返回根节点。

        对于此函数的实现,主要还是依靠递归,将这个递归分为:若当前字符为:#,返回NULL,若不为:#,malloc一个新节点,新节点的数据为当前字符,然后将整数+1,然后递归新节点的左右孩子结点,最后返回新节点,具体代码实现如下:

BTNode* CreatBTNode(BTDataType x) {
	BTNode* newnode = (BTNode*)malloc(sizeof(BTNode));
	if (newnode == NULL) {
		perror("newnode malloc:\n");
		exit(1);
	}
	newnode->data = x;
	newnode->left = newnode->right = NULL;
	return newnode;
}

// 通过前序遍历的数组"ABD##E#H##CF##G##"构建二叉树
BTNode* BinaryTreeCreate(BTDataType* a, int* pi) {
	if (a[*pi] == '#') {
		(*pi)++;
		return NULL;
	}
	BTNode* newNode = CreatBTNode(a[*pi]);
	(*pi)++;
	newNode->left = BinaryTreeCreate(a, pi);
	newNode->right = BinaryTreeCreate(a, pi);
	return newNode;
}

        二叉树的销毁:二叉树的销毁仍然使用递归进行销毁处理,其主要思想和后序遍历基本一致,主要为:递归删除左子树+递归删除右子树+删除根节点,主要实现代码如下:

// 二叉树销毁
void BinaryTreeDestory(BTNode** root) {
	// 递归销毁,先销毁左子树,后销毁右子树
	if (*root == NULL)
		return;
	BTNode* tmp = *root;
	BinaryTreeDestory(&tmp->left);
	BinaryTreeDestory(&tmp->right);
	free(tmp);
	tmp = NULL;
}

2.4 层序遍历与判断二叉树是否为完全二叉树

        层序遍历:对于层序遍历的主要思想就是将二叉树按层次遍历,先遍历第一层根节点,然后遍历下一层,从左到右,直至遍历结束。对于此方法,我们不使用递归,我们使用队列来辅助我们遍历。

        我们首先将根节点放入队列,然后开始进行遍历循环,只要队列不为空,我们就一直入循环。在循环中,我们得到排在队列的第一个元素将其访问,然后,若第一个元素的左子树不为空,则将其入队列,右子树不为空,将其入队列(孩子结点不为空就入队列),接着将第一个元素出队列,循环往复就可以将二叉树进行层序遍历,因为是将结点一层一层的加入队列,然后又出队列的。具体实现代码如下:

// 层序遍历
void BinaryTreeLevelOrder(BTNode* root) {
	if (root == NULL)
		return;
	Queue pQ;
	QueueInit(&pQ);
	QueuePush(&pQ, *root);
	while (!QueueEmpty(&pQ)) {
		BTNode new = QueueFront(&pQ);
		printf("%c ", new.data);
		if(new.left)
			QueuePush(&pQ, *(new.left));
		if(new.right)
			QueuePush(&pQ, *(new.right));
		QueuePop(&pQ);
	}
	QueueDestory(&pQ);
}

        判断二叉树是否为完全二叉树:对于完全二叉树的判断,主要就是判断一个结点是否存在:不存在左孩子存在右孩子的情况,当出现这种情况时,就可以判断出该二叉树不为完全二叉树。所以我们只需将所有的结点进行判断,若所有结点都判断通过,则返回true,若发现其中一个结点存在:没有左孩子存在右孩子,则返回false。

        对于每个孩子的判断,其实我们就可以使用以上的层序遍历,在遍历每个结点时对结点进行判断,具体实现代码如下:

// 判断二叉树是否是完全二叉树
bool BinaryTreeComplete(BTNode* root) {
	if (root == NULL)
		return true;
	Queue pQ;
	QueueInit(&pQ);
	QueuePush(&pQ, *root);
	while (!QueueEmpty(&pQ)) {
		BTNode new = QueueFront(&pQ);
		if (new.left == NULL && new.right)
			return false;
		if (new.left)
			QueuePush(&pQ, *(new.left));
		if (new.right)
			QueuePush(&pQ, *(new.right));
		QueuePop(&pQ);
	}
	QueueDestory(&pQ);
	return true;
}

2.5 二叉树查找值为x的结点。

         对于二叉树的查找,我们同样使用递归对二叉树进行遍历查找,其主要思想为:若当前结点为NULL,则返回NULL,若当前结点的值等于 x ,则返回当前结点,然后依次遍历查找左子树和右子树,但是在左子树和右子树的查找返回中,我们一个函数只能返回一次,不能同时递归返回左右子树,所有需要在对左子树的递归返回时加上一个判断,若左子树不为NULL,则先查找递归返回左子树,当左子树递归结束都还没找到时,自然递归返回右子树。具体实现代码如下:

// 二叉树查找值为x的节点
BTNode* BinaryTreeFind(BTNode* root, BTDataType x) {
	if (root == NULL)
		return NULL;
	if (root->data == x)
		return root;
	BTNode* leftFind = BinaryTreeFind(root->left, x);
	if (leftFind)
		return leftFind;
	return BinaryTreeFind(root->right, x);
}

3. 所有代码

3.1 All.h

        以下为头文件:All.h的代码:

#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
#include <stdbool.h>
#include <math.h>

typedef char BTDataType;

typedef struct BinaryTreeNode {
	BTDataType data;
	struct BinaryTreeNode* left;
	struct BinaryTreeNode* right;
}BTNode;

typedef BTNode QDataType;

typedef struct Node {
	QDataType val;
	struct Node* next;
}QNode;

typedef struct Queue {
	int size;
	QNode* phead;
	QNode* ptail;
}Queue;

//队列的初始化/销毁
void QueueInit(Queue* pQ);
void QueueDestory(Queue* pQ);

//队列的入队/出队
void QueuePush(Queue* pQ, QDataType x);
void QueuePop(Queue* pQ);

//返回队首元素/队尾元素
QDataType QueueFront(Queue* pQ);
QDataType QueueBack(Queue* pQ);

//判断队列是否为NULL/返回队列的大小
bool QueueEmpty(Queue* pQ);
int QueueSize(Queue* pQ);

// 通过前序遍历的数组"ABD##E#H##CF##G##"构建二叉树
BTNode* BinaryTreeCreate(BTDataType* a, int* pi);
// 二叉树销毁
void BinaryTreeDestory(BTNode** root);
// 二叉树节点个数
int BinaryTreeSize(BTNode* root);
// 二叉树叶子节点个数
int BinaryTreeLeafSize(BTNode* root);
// 二叉树第k层节点个数
int BinaryTreeLevelKSize(BTNode* root, int k);
// 二叉树查找值为x的节点
BTNode* BinaryTreeFind(BTNode* root, BTDataType x);
// 二叉树前序遍历 
void BinaryTreePrevOrder(BTNode* root);
// 二叉树中序遍历
void BinaryTreeInOrder(BTNode* root);
// 二叉树后序遍历
void BinaryTreePostOrder(BTNode* root);
// 层序遍历
void BinaryTreeLevelOrder(BTNode* root);
// 判断二叉树是否是完全二叉树
bool BinaryTreeComplete(BTNode* root);
// 二叉树的高度
int BinaryTreeHeight(BTNode* root);

 3.2 Queue.c

        以下为队列部分的代码:

#define _CRT_SECURE_NO_WARNINGS 1
#include "All.h"

void QueueInit(Queue* pQ) {
	assert(pQ);
	pQ->phead = pQ->ptail = NULL;
	pQ->size = 0;
}

void QueueDestory(Queue* pQ) {
	assert(pQ);
	QNode* cur = pQ->phead;
	while (cur) {
		QNode* next = cur->next;
		free(cur);
		cur = next;
	}
	pQ->phead = pQ->ptail = NULL;
	pQ->size = 0;
}

void QueuePush(Queue* pQ, QDataType x) {
	assert(pQ);
	QNode* newnode = (QNode*)malloc(sizeof(QNode));
	if (newnode == NULL) {
		perror("malloc:");
		exit(1);
	}
	newnode->val = x;
	newnode->next = NULL;
	if (pQ->phead == NULL) {
		pQ->phead = pQ->ptail = newnode;
	}
	else {
		pQ->ptail->next = newnode;
		pQ->ptail = newnode;
	}
	pQ->size++;
}

void QueuePop(Queue* pQ) {
	assert(pQ);
	assert(pQ->phead);
	QNode* cur = pQ->phead;
	pQ->phead = pQ->phead->next;
	free(cur);
	cur = NULL;
	pQ->size--;
}

QDataType QueueFront(Queue* pQ) {
	assert(pQ);
	assert(pQ->phead);
	return pQ->phead->val;
}

QDataType QueueBack(Queue* pQ) {
	assert(pQ);
	assert(pQ->phead);
	return pQ->ptail->val;
}

bool QueueEmpty(Queue* pQ) {
	assert(pQ);
	return pQ->phead == NULL;
}

int QueueSize(Queue* pQ) {
	assert(pQ);
	return pQ->size;
}

3.3 BinaryTree.c

        以下为实现二叉树的代码:

#define _CRT_SECURE_NO_WARNINGS 1
#include "All.h"

// 二叉树节点个数
int BinaryTreeSize(BTNode* root) {
	return root == NULL ? 0 : BinaryTreeSize(root->left) + BinaryTreeSize(root->right) + 1;
}

// 二叉树叶子节点个数
int BinaryTreeLeafSize(BTNode* root) {
	if (root == NULL)
		return 0;
	if (root->left == NULL && root->right == NULL)
		return 1;
	return BinaryTreeLeafSize(root->left) + BinaryTreeLeafSize(root->right);
}

// 二叉树第k层节点个数
int BinaryTreeLevelKSize(BTNode* root, int k) {
	assert(k > 0);
	if (k == 1)
		return 1;
	if (root == NULL)
		return 0;
	return BinaryTreeLevelKSize(root->left, k - 1) + BinaryTreeLevelKSize(root->right, k - 1);
}

// 二叉树查找值为x的节点
BTNode* BinaryTreeFind(BTNode* root, BTDataType x) {
	if (root == NULL)
		return NULL;
	if (root->data == x)
		return root;
	BTNode* leftFind = BinaryTreeFind(root->left, x);
	if (leftFind)
		return leftFind;
	return BinaryTreeFind(root->right, x);
}

// 二叉树前序遍历 
void BinaryTreePrevOrder(BTNode* root) {
	if (root) {
		printf("%c ", root->data);
		BinaryTreePrevOrder(root->left);
		BinaryTreePrevOrder(root->right);
	}
}

// 二叉树中序遍历
void BinaryTreeInOrder(BTNode* root) {
	if (root) {
		BinaryTreePrevOrder(root->left);
		printf("%c ", root->data);
		BinaryTreePrevOrder(root->right);
	}
}

// 二叉树后序遍历
void BinaryTreePostOrder(BTNode* root) {
	if (root) {
		BinaryTreePrevOrder(root->left);
		BinaryTreePrevOrder(root->right);
		printf("%c ", root->data);
	}
}

// 层序遍历
void BinaryTreeLevelOrder(BTNode* root) {
	if (root == NULL)
		return;
	Queue pQ;
	QueueInit(&pQ);
	QueuePush(&pQ, *root);
	while (!QueueEmpty(&pQ)) {
		BTNode new = QueueFront(&pQ);
		printf("%c ", new.data);
		if(new.left)
			QueuePush(&pQ, *(new.left));
		if(new.right)
			QueuePush(&pQ, *(new.right));
		QueuePop(&pQ);
	}
	QueueDestory(&pQ);
}

// 二叉树的高度
int BinaryTreeHeight(BTNode* root) {
	if (root == NULL)
		return 0;
	int leftHeight = BinaryTreeHeight(root->left);
	int rightHeight = BinaryTreeHeight(root->right);
	return leftHeight > rightHeight ? leftHeight + 1 : rightHeight + 1;
}

BTNode* CreatBTNode(BTDataType x) {
	BTNode* newnode = (BTNode*)malloc(sizeof(BTNode));
	if (newnode == NULL) {
		perror("newnode malloc:\n");
		exit(1);
	}
	newnode->data = x;
	newnode->left = newnode->right = NULL;
	return newnode;
}

// 通过前序遍历的数组"ABD##E#H##CF##G##"构建二叉树
BTNode* BinaryTreeCreate(BTDataType* a, int* pi) {
	if (a[*pi] == '#') {
		(*pi)++;
		return NULL;
	}
	BTNode* newNode = CreatBTNode(a[*pi]);
	(*pi)++;
	newNode->left = BinaryTreeCreate(a, pi);
	newNode->right = BinaryTreeCreate(a, pi);
	return newNode;
}

// 二叉树销毁
void BinaryTreeDestory(BTNode** root) {
	// 递归销毁,先销毁左子树,后销毁右子树
	if (*root == NULL)
		return;
	BTNode* tmp = *root;
	BinaryTreeDestory(&tmp->left);
	BinaryTreeDestory(&tmp->right);
	free(tmp);
	tmp = NULL;
}

// 判断二叉树是否是完全二叉树
bool BinaryTreeComplete(BTNode* root) {
	if (root == NULL)
		return true;
	Queue pQ;
	QueueInit(&pQ);
	QueuePush(&pQ, *root);
	while (!QueueEmpty(&pQ)) {
		BTNode new = QueueFront(&pQ);
		printf("%c ", new.data);
		if (new.left == NULL && new.right)
			return false;
		if (new.left)
			QueuePush(&pQ, *(new.left));
		if (new.right)
			QueuePush(&pQ, *(new.right));
		QueuePop(&pQ);
	}
	QueueDestory(&pQ);
	return true;
}

3.4 test.c

        以下为测试的所有代码:

#define _CRT_SECURE_NO_WARNINGS 1
#include "All.h"

int main() {
	char ch[] = "ABD##E#H##CF##G##";
	int i = 0;
	BTNode* root = BinaryTreeCreate(ch, &i);

	printf("BinaryTreePrevOrder: ");
	BinaryTreePrevOrder(root);
	printf("\n");
	printf("BinaryTreeInOrder: ");
	BinaryTreeInOrder(root);
	printf("\n");
	printf("BinaryTreePostOrder: ");
	BinaryTreePostOrder(root);
	printf("\n");

	if (BinaryTreeComplete(root))
		printf("BinaryTree is Complete: true\n");
	else
		printf("BinaryTree is Complete: false\n");

	printf("BinaryTreeLevelKSize: %d\n", BinaryTreeLevelKSize(root, 3));
	printf("BinaryTreeLeafSize: %d\n",BinaryTreeLeafSize(root));
	printf("BinaryTreeSize: %d\n", BinaryTreeSize(root));
	printf("BinaryTreeHeight: %d\n", BinaryTreeHeight(root));

	BTNode* Find = BinaryTreeFind(root, 'A');
	if (Find)
		Find->data = 'Y';
	printf("BinaryTreePrevOrder: ");
	BinaryTreePrevOrder(root);
	printf("\n");

	printf("BinaryTreeLevelOrder: ");
	BinaryTreeLevelOrder(root);

	BinaryTreeDestory(&root);
	return 0;
}

3.5 测试结果

        对于字符串"ABD##E#H##CF##G##"建立的树如下:

        代码实现如下:

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

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

相关文章

redis-Redis主从,哨兵和集群模式

一&#xff0c;Redis的主从复制 ​ 主机数据更新后根据配置和策略&#xff0c; 自动同步到备机的master/slaver机制&#xff0c;Master以写为主&#xff0c;Slave以读为主。这样做的好处是读写分离&#xff0c;性能扩展&#xff0c;容灾快速恢复。 1.1 环境搭建 如果你的redi…

【HDFS】Decommision(退役) EC数据节点剩最后几个块卡住的问题

一、背景 近期操作退役EC集群的节点。在退役的过程中,遇到了一些问题。特此总结一下。 本文描述的问题现象是: 每一批次退役10个节点,完全退役成功后开始操作下一批。 但是,中间有一批次有2台节点的Under Replicated Blocks一直是1,不往下降。 处于Decommissioning状态卡…

iOS App冷启动优化:Before Main阶段

iOS应用冷启动时&#xff0c;在 UIApplicationMain(argc, argv, nil, appDelegateClassName)方法执行前&#xff0c;主要经历以下阶段&#xff1a; 1. 执行exec&#xff08;&#xff09;启动应用程序进程 2. 加载可执行文件&#xff0c;即将应用程序的Mach-O文件加载到内存…

跟着野火学FreeRTOS:第二段(堆存储管理)

F r e e R T O S FreeRTOS FreeRTOS从版本 V 9.0.0 V9.0.0 V9.0.0开始&#xff0c;内核对象所用的存储空间可以在编译时静态分配或在运行时动态分配&#xff0c;早期的版本不同时支持静态分配和动态分配&#xff0c;这里讲到的堆存储管理是和动态分配相关的。从版本 V 9.0.0 V9…

跨区域复制建筑UI输入框脚本迷你世界

--复制区域文件 --设置坐标起点&#xff0c;终点 --创建区域 --获取坐标id,data --星空露珠工作室制作 local pos1{x-16,y7,z28} local pos2{x28,y44,z-9} local block{num0} local str{} local str0{} local num0 local count0 local ui6 --几个输入框 local romath.random(…

【LeetCode:108. 将有序数组转换为二叉搜索树 + 二叉树+递归】

&#x1f680; 算法题 &#x1f680; &#x1f332; 算法刷题专栏 | 面试必备算法 | 面试高频算法 &#x1f340; &#x1f332; 越难的东西,越要努力坚持&#xff0c;因为它具有很高的价值&#xff0c;算法就是这样✨ &#x1f332; 作者简介&#xff1a;硕风和炜&#xff0c;…

Vue3+springboot实现简单登录demo

Vue3从0搭建脚手架步骤【默认已安装node.js】 前置条件&#xff1a;默认已安装node.js、yarn 第一步&#xff1a;创建项目 选择任意一个空白文件夹如下&#xff1a; cmd进入该文件夹下的命令窗口模式&#xff0c;然后输入指令创建vue项目&#xff1a;vue create my-project …

Maya笔记 大纲视图、父子级、打组

文章目录 大纲视图打开大纲视图双层大纲视图 父子级快捷键大纲视图 组菜单操作快捷键操作 大纲视图 打开大纲视图 双层大纲视图 鼠标放到大纲视图最下边的…上&#xff0c;鼠标变成拖动&#xff0c;向上拖&#xff0c;可以拖出两个大纲视图&#xff0c;方便我们同时操作两个物…

[JavaWeb玩耍日记]HTML与CSS快速使用

目录 一.标签 二.指定css 三.css选择器 四.超链接 五.视频与排版 六.布局测试 七.布局居中 八.表格 九.表单 十.表单项 实验项目目录&#xff1a; 必要文件new.css: h1 {color:black; } span{color: #968D92;; } a {color: #000;text-decoration: none; } p {text-i…

水合三氯化铱(三氯化铱水合物)行业技术壁垒较高 应用范围有望扩展

水合三氯化铱&#xff08;三氯化铱水合物&#xff09;行业技术壁垒较高 应用范围有望扩展 水合三氯化铱又称三氯化铱水合物&#xff0c;分子式为IrCl33H2O&#xff0c;是一种无机化合物。水合三氯化铱外观呈绿色结晶或褐色粉末&#xff0c;可溶于水、丙酮和盐酸&#xff0c;难溶…

学习阶段单片机买esp32还是stm32?

学习阶段单片机买esp32还是stm32? 在开始前我有一些资料&#xff0c;是我根据网友给的问题精心整理了一份「stm32的资料从专业入门到高级教程」&#xff0c; 点个关注在评论区回复“888”之后私信回复“888”&#xff0c;全部无偿共享给大家&#xff01;&#xff01;&#xf…

Altair® HyperWorks® 2023 卓越的开放式仿真和设计平台

Altair HyperWorks 2023 卓越的开放式仿真和设计平台 全新的 Altair HyperWorks 2023 软件版本现已发布&#xff01;Altair HyperWorks 2023 是一个开放式仿真和设计平台&#xff0c;功能更强大、用途更广泛&#xff0c;将为工程师提供适用于各行业的全面 CAE 软件套件。通过运…

【appium】Hybrid应用自动化|微信小程序自动化

目录 一、Hybrid&#xff08;nativewebview&#xff09;应用自动化 1、webview 2、Hybrid应用自动化实现 2.1准备工作 Step1&#xff1a;准备android 4.4版本以上的手机/模拟器 Step2&#xff1a;在app源码中将webview调试模式打开 Step3&#xff1a;安装UC开发者工具 U…

Sui主网升级至V1.19.1版本

其他升级要点如下所示&#xff1a; #16190, #16193 现在CLI正确处理并修复了交易没有输入或命令时的输出表格。例如&#xff0c;调用 client call — package 0x2 — module kiosk — function default 现在具有正确格式的输出。 #15928 Move编译器的一系列变更 添加了宏函…

Redis持久化的两种方式RDB和AOF详解

小伙伴们好&#xff0c;欢迎关注&#xff0c;一起学习&#xff0c;无限进步 以下内容为学习 Redis 过程中的笔记 文章目录 Redis持久化RDB&#xff08; Redis DataBase &#xff09;触发机制&#xff1a;如何恢复rbd文件&#xff1a;优点&#xff1a;缺点&#xff1a; AOF &…

搬家了,发现虚拟机链接不上,查找原因,解决了

是网络配置的问题&#xff0c;因为ip地址变动&#xff0c;所以配置文件要进行改动 1.通过cmd查看本地主机ip地址 2.在虚拟网络编辑器中选在vmnet8&#xff0c;用管理员权限修改ip&#xff0c;网关&#xff0c;子网掩码&#xff0c;和物理主机对应 3.在/etc/sysconfig/network…

同局域网共享虚拟机(VMware)

一、前言 首先我们先来了解下 VMware 的三种网络模式桥接模式、NAT模式、仅主机模式&#xff0c;网络类型介绍详情可以参考下我之前的文档 Linux系统虚拟机安装&#xff08;上&#xff09;第三章 - 第9步指定网络类型。了解三种网络模式的原理之后&#xff0c;再来剖析下需求&…

【力扣hot100】刷题笔记Day14

前言 又是新的一周&#xff0c;快乐的周一&#xff0c;快乐地刷题&#xff0c;今天把链表搞完再干活&#xff01; 114. 二叉树展开为链表 - 力扣&#xff08;LeetCode&#xff09; 前序遍历 class Solution:def flatten(self, root: Optional[TreeNode]) -> None:if not r…

clickhouse 随心所欲的聚合模型-AggregatingMergeTree

clickhouse 强大的 MergeTree 系列引擎令人信服&#xff0c;其 ReplacingMergeTree、SummingMergeTree 在数据唯一性和汇总场景中表现非凡。但你是否还有保留最小(大)、平均等预聚合需求&#xff0c;甚至在一个模型中既有唯一性语意也有汇总、最小、最大、平均值语意该如何处理…

【Simulink系列】——Simulink与Matlab接口使用命令行进行仿真

声明&#xff1a;本系列博客参考有关专业书籍&#xff0c;截图均为自己实操&#xff0c;仅供交流学习&#xff01; 一、Simulink与Matlab接口 1、Matlab工作区变量设置模块参数 Matlab工作区的变量可以作为模块的设置参数 2、Matlab工作区变量作为输入信号 使用From Worksp…