二叉树与递归的相爱相杀

数据结构之二叉树

  • 一、基于二叉树的基础操作
    • 1.二叉树的构建
    • 2.二叉树的遍历
      • ①前序遍历(深度遍历)
      • ②中序遍历
      • ③后序遍历
      • ④层序遍历
        • 判断一棵二叉树是否是完全二叉树(基于层序遍历的思想)
    • 3.二叉树的数量问题
      • ①求二叉树结点个数
      • ②求二叉树叶子结点个数
      • ③求二叉树第K层结点个数
    • 4.查找某个结点所在位置
    • 5.二叉树的高度
  • 二、与二叉树相关的练习题(点击标题即可跳转至对应题目)
    • 1.单值二叉树
    • 2.判断两棵二叉树是否相同
    • 3.对称二叉树
    • 4.另一棵树的子树
    • 4.二叉树的前序遍历
    • 6.二叉树的构建及遍历
  • 三、第一部分的全部代码(复制粘贴到vs一定能跑通)
    • BinaryTree.h
    • BinaryTree.c
    • test.c

一、基于二叉树的基础操作

1.二叉树的构建

先看下面两句话
    我们整个操作是建立在三个文件上的。BinaryTree.h放置全部需要引用的头文件、二叉树结点的定义以及所有自定义函数的声明;BinaryTree.c放置所有自定义函数的实现(这里并不是很准确,有一些自定义函数是供其他一些自定义函数使用的函数是可以不用放到.h文件中去的,.h文件中放置的自定义函数主要是在test.c文件中需要使用的函数)test.c就放置主函数,供我们测试二叉树写得是否正确。其中BinaryTree.c和test.c文件引用BinaryTree.h
    前序遍历构建方法在第六道练习题中体现

刚开始我们用很简单的方法构建(三步搞定)
第一步:首先我们要定义单个结点

//BinaryTree.h
typedef struct BinaryTreeNode
{struct BinaryTreeNode* left;struct BinaryTreeNode* right;int val;
}BTNode;

第二步:基本架构

//test.c
int main()
{//构建6个节点BTNode* node1 = BuyNode(1);BTNode* node2 = BuyNode(2);BTNode* node3 = BuyNode(3);BTNode* node4 = BuyNode(4);BTNode* node5 = BuyNode(5);BTNode* node6 = BuyNode(6);//建立这六个节点的指向关系node1->left = node2;node2->left = node3;node1->right = node4;node4->left = node5;node4->right = node6;
}

请添加图片描述
第三步:把BuyNode函数补上

BTNode* BuyNode(int x)
{BTNode* node = (BTNode*)malloc(sizeof(BTNode));if (node == NULL){perror("malloc failed");exit(-1);}node->left = NULL;node->right = NULL;node->val = x;return node;
}

2.二叉树的遍历

①前序遍历(深度遍历)

正如文章标题所说的,二叉树与递归相爱相杀,所以这里必然用的是递归来遍历
至于原因呢,就是二叉树是一个很好的递归结构(二路递归)

  • 就拿前序遍历来说,先访问根,然后是左子树,右子树。其中访问左子树的时候,也是先访问左子树的根结点、左子树的左子树、左子树的右子树
  • 这就很好的满足了递归的思想——把大问题化成与其类似的规模较小的子问题,通过递归调用解决小问题。
  • 要注意的每次递归都会使问题变得更简单,直到问题已经简单到不需要进一步递归即可解决
  • 递归的两个关键属性是基本情况递推关系。基本情况是指递归过程中不再继续递归的条件,而递推关系则是将所有其他情况转换为基本情况的规则。
  • 一般情况下基本情况写在递推关系前面
void PreOrder(BTNode* root)
{//前面说到的,这就是递归的基本情况----递归不再继续的条件if (root == NULL)return;//递归的基本关系----大问题化小问题printf("%d ", root->val);//先访问根,遇到根就打印PreOrder(root->left);//根访问完,访问左子树PreOrder(root->right);//再访问右子树
}

递归图,按顺序走
请添加图片描述
最终打印结果(空未打印)
在这里插入图片描述

②中序遍历

中序、后序和前序很类似,只是改一下根节点访问时机,这里我就放一下代码

void InOrder(BTNode* root)
{if (root == NULL)return;InOrder(root->left);printf("%d ", root->val);InOrder(root->right);
}

③后序遍历

void PastOrder(BTNode* root)
{if (root == NULL)return;PastOrder(root->left);PastOrder(root->right);printf("%d ", root->val);
}

④层序遍历

层序遍历要结合队列来解决

  • 二叉树的层序遍历利用队列的原因主要在于队列的先进先出(FIFO)特性。
    层序遍历的目标是按层级顺序遍历二叉树的所有节点。
  • 具体地说,首先将二叉树的根节点推入队列,然后检查队列是否为空。如果不为空,就从队列中取出队头的元素,并访问这个元素代表的节点。
    然后,如果这个节点有左子树,就将左子树推入队列;如果有右子树,也将右子树推入队列。重复这个过程,直到队列为空。
  • 这样做的原因是,队列保证了我们总是先处理最先进入队列的节点,即按照层级顺序进行遍历。同时,这种方法适用于各种不同的二叉树结构

代码如下:(前提是有队列这个数据结构哈,没有的我会把代码一起放在第三部分,这里就展示层序遍历这部分的代码)

void LevelOrder(BTNode* root)
{Queue q;QueueInit(&q);//	首先根不为空,让根进队列if (root != NULL)QueuePush(&q, root);//在队列不为空的情况下while (!QueueEmpty(&q)){//读取对头元素printf("%d", QueueFront(&q)->val);//在左右子树不为空的情况下,让左右子树入队列if (QueueFront(&q)->left != NULL)QueuePush(&q,QueueFront(&q)->left);if (QueueFront(&q)->right != NULL)QueuePush(&q, QueueFront(&q)->right);//让对头元素出队列QueuePop(&q);}QueueDestroy(&q);
}

层序遍历的过程如下图(子树为空的时候不进队列)

请添加图片描述

判断一棵二叉树是否是完全二叉树(基于层序遍历的思想)
  • 完全二叉树的特点大家还记得吗,就是假如完成二叉树有k层,那么其前k-1层都是满的,而第k层所有结点都连续集中在最左边
  • 那么如何和层序遍历结合起来呢,就是我们按照层序遍历的方式一次将每一层入队列,然后出结点,接着带入左右子树。左右子树为空的时候也要入进去,就入个空值就好
  • 如果不是完全二叉树,那么在出队列时遇到空值时,队列里还有非空元素。而如果是完全二叉树,遇到空值的时候也代表则元素已经遍历完了
    非完全二叉树在这里插入图片描述
    完全二叉树
    在这里插入图片描述
int BinaryTreeComplete(BTNode* root)
{Queue q;QueueInit(&q);if (root == NULL)return 1;QueuePush(&q, root);while (!QueueEmpty(&q)){//此处碰到空值就跳出循环开始判断是否是完全二叉树if (QueueFront(&q) == NULL)break;//if (QueueFront(&q)->left != NULL)QueuePush(&q, QueueFront(&q)->left);//if (QueueFront(&q)->right != NULL)QueuePush(&q, QueueFront(&q)->right);QueuePop(&q);}//上述代码就是入队列的过程//走到这意味着遇到空,如果此时队列里都是空,则表示是完成二叉树while (!QueueEmpty(&q)){if (QueueFront(&q) == NULL){QueuePop(&q);continue;}//走到这里说明此时队列不为空,而出现了空值QueueDestroy(&q);return 0;}QueueDestroy(&q);return 1;
}

3.二叉树的数量问题

①求二叉树结点个数

递归思想:要求二叉树结点的个数,可以化为求左子树的结点个数+右子树结点个数+1(这个1就是算上根结点)

//相当于二叉树的后序遍历
int TreeSize(BTNode* root)
{//写法1//if (root == NULL)//	return 0;划分为左树的节点数+右树的节点数+1//return TreeSize(root->left) + TreeSize(root->right) + 1;//写法2//更简洁的写法return root == NULL ? 0 : TreeSize(root->left) + TreeSize(root->right) + 1;
}

②求二叉树叶子结点个数

思路和上一题类似,只不过是找到叶子结点才算数

int TreeLeafSize(BTNode* root)
{//1.空节点返回0if (root == NULL)return 0;//注意,这里不能写成 return ;  因为 return ; 时默认就return 1回去,所以这样子求得的数值就是最后一层满载的时候的节点个数//2.叶子节点返回1if (root->left == NULL && root->right == NULL)return 1;//3.其他节点就递归到左右子树return TreeLeafSize(root->left) + TreeLeafSize(root->right);
}

③求二叉树第K层结点个数

  • 这个有难度了,该怎么求第k层的结点个数呢?
  • 其实也是递归的思想:求从根结点开始的第k层的个数,等同于求从第二层开始的第k-1层的结点个数,也等同于求从第三层开始的第k-2层结点的个数。。。。。。
int TreeKLevelSize(BTNode* root, int k)
{if (root == NULL)return 0;//从第一层看第k层等于第二层看第k-1层//走到这k == 1时表示递归走到了该层,此时节点不会为空,,即表示这层有节点if (k == 1)return 1;//二叉树中双路递归的思想真的很重要!!!return TreeKLevelSize(root->left, k - 1) + TreeKLevelSize(root->right, k - 1);
}

4.查找某个结点所在位置

  • 看到这个问题,大概思路大家肯定都能想到,就遍历呗,在遍历的过程中比较值是否相等呗,很简单
  • 但是这里有个问题哈,我们函数的返回值是找到的结点的地址,如果直接return回去,假如我们最开始就找到了这个地址,但是在后续的递归过没找到。而这个地址被NULL值覆盖了怎么办?
    -解决办法就是:加个判断,当ret不等于NULL时才return,这样子及时在函数最开始root == NULL时(即到了叶子结点)返回了NULL,但在后续对ret的判断时也不会让NULL覆盖真正的地址
BTNode* BinaryTreeFind(BTNode* root, int x)
{if (root == NULL)return NULL;else if (root->val == x)return root;BTNode* ret = NULL;//通过判空的方式很好的解决了如果后续的值不符合时返回的null值如何规避ret = BinaryTreeFind(root->left,x);if (ret != NULL)return ret;ret = BinaryTreeFind(root->right,x);if (ret != NULL)return ret;
}

5.二叉树的高度

  • 有了上面这些递归事例的基础,看这个问题就很简单了
  • 思路就是二叉树的高度等于左右子树的中较高的高度+1,然后再把子树给向下递归即可
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;
}
  • 不过这里要注意一个问题
  • 很多同学为了偷懒而像下面这样简写是不对的
  • 这样子看似代码简洁,但是这个代码的效率很低,你看不论较高的子树是左子树还是右子树,比较完之后还需要计算一遍左右子树的高度,效率很低!
int BinaryTreeHeight(BTNode* root)
{if (root == NULL)return 0;return BinaryTreeHeight(root->left) >  BinaryTreeHeight(root->right) ? BinaryTreeHeight(root->left) + 1 : BinaryTreeHeight(root->right)+ 1;
}

二、与二叉树相关的练习题(点击标题即可跳转至对应题目)

1.单值二叉树

  • 思路:判断二叉树是不是单值二叉树,就是看其左右子树是不是单值二叉树
  • 在这个递归的题目中,有一点很重要的就是,既然是判断是否,那肯定就是有些条件下是return false,有些条件是return true,所以代码中第二个if处不能写成
    if(root->left = = NULL && root->left->val == root->val)
        return true;
    这个条件其实是继续递归的条件,你在这里就return了,如果后面还有不等的情况怎么办?
bool isUnivalTree(struct TreeNode* root){if(root == NULL)return true;if(root->left != NULL && root->left->val != root->val)return false;if(root->right != NULL && root->right->val != root->val)return false;return isUnivalTree(root->left) && isUnivalTree(root->right);
}

2.判断两棵二叉树是否相同

这个题相对来说就比较简单了,但是这个题是下个题的基础

bool isSameTree(struct TreeNode* p, struct TreeNode* q){if(p == NULL && q == NULL)return true;//走到这里肯定只有一个会为空,一空一非空肯定不相等if(p == NULL || q == NULL)return false;//走到这里肯定两个都不为空if(p->val != q->val)return false;return isSameTree(p->left, q->left) && isSameTree(p->right, q->right);
}

3.对称二叉树

把上一题的两棵树合成一棵树了

bool isSame(struct TreeNode* p,struct TreeNode* q)
{if(p == NULL && q == NULL)return true;if(p == NULL || q == NULL)return false;if(p->val != q->val)return false;return isSame(p->left,q->right) && isSame(p->right,q->left);
}bool isSymmetric(struct TreeNode* root){return isSame(root->left,root->right);    
}

4.另一棵树的子树

思路:让root及其子树依次的去和subRoot比较,用于比较的函数就是上上一题所写的

bool isSameTree(struct TreeNode* p, struct TreeNode* q){if(p == NULL && q == NULL)return true;//走到这里肯定只有一个会为空if(p == NULL || q == NULL)return false;//走到这里肯定两个都不为空if(p->val != q->val)return false;return isSameTree(p->left, q->left) && isSameTree(p->right, q->right);
}//subRoot不动root动bool isSubtree(struct TreeNode* root, struct TreeNode* subRoot){if(root == NULL)return false;if(root->val == subRoot->val){//为什么不直接return isSameTree,因为这里如果isSameTree结果是false,并不能直接return回去,因为还要去子树继续比较,唯一返回false的条件就是走到空了if(isSameTree(root,subRoot))return true;}//这里用‘或’就行,只要子树有一个满足条件就OK了return isSubtree(root->left,subRoot) || isSubtree(root->right,subRoot);
}

4.二叉树的前序遍历

这里题目要求,需要把遍历的结果放入数组中,所以我们需要先计算树中有多少个结点,然后构建多大的数组。

int TreeSize(struct TreeNode* root)
{if (root == NULL)return 0;return TreeSize(root->left) + TreeSize(root->right) + 1;
}// static int i = 0;
void PreTree(struct TreeNode* root, int* a,int* pi)
{if (root == NULL)return;a[(*pi)++] = root->val;//如果直接定义普通的i,那么在两路递归里这俩i是形参,值改变了对另外一个i没有影响PreTree(root->left, a,pi);PreTree(root->right,a, pi);
}//returnSize是返回数组的元素个数,要返回的还是数组!!!
int* preorderTraversal(struct TreeNode* root, int* returnSize) {int n = TreeSize(root);int* a = (int*)malloc(sizeof(int) * n);int i = 0;PreTree(root, a,&i);*returnSize = n;return a;
}

这俩就留着你们来砍瓜切菜了
二叉树的中序遍历
二叉树的后序遍历

6.二叉树的构建及遍历

  • 实现是构建,思路也是用递归,这里我用前序遍历构建,既然是前序遍历,那我们是按照根-左-右的顺序来的,所以先构建根结点,然后递归调用CreateTree,最后别忘了终止条件——在遇到’#'代表着空节点,记得return NULL
    -遍历的话,就按照题目要求的后序遍历即可
#include <stdio.h>
#include<stdlib.h>typedef struct BinaryTreeNode
{struct BinaryTreeNode* left;struct BinaryTreeNode* right;char val;
}BTNode;BTNode* CreateTree(char* str,int* pi)
{if(str[*pi] == '#'){(*pi)++;return NULL;}BTNode* root = (BTNode*)malloc(sizeof(BTNode));root->val = str[*pi];(*pi)++;root->left = CreateTree(str,pi);root->right = CreateTree(str,pi);return root;
}void InOrder(BTNode* root)
{if(root == NULL){return;}InOrder(root->left);printf("%c ",root->val);InOrder(root->right);
}int main() {char str[100];scanf("%s",str);int i = 0;BTNode* root = CreateTree(str,&i);InOrder(root);return 0;
}

三、第一部分的全部代码(复制粘贴到vs一定能跑通)

BinaryTree.h

#pragma once#include<stdio.h>
#include<stdlib.h>typedef struct BinaryTreeNode
{struct BinaryTreeNode* left;struct BinaryTreeNode* right;int val;
}BTNode;BTNode* BuyNode(int x);void PreOrder(BTNode* root);
void InOrder(BTNode* root);
void PastOrder(BTNode* root);int TreeSize(BTNode* root);
int TreeLeafSize(BTNode* root);
int TreeKLevelSize(BTNode* root, int k);void LevelOrder(BTNode* root);//层序遍历BTNode* BinaryTreeFind(BTNode* root, int x);
void BinaryTreeDestroy(BTNode* root);
//是完全二叉树返回1,否则返回0
int BinaryTreeComplete(BTNode* root);
int BinaryTreeHeight(BTNode* root);

BinaryTree.c

#define _CRT_SECURE_NO_WARNINGS 1#include"BinaryTree.h"
#include"Queue.h"BTNode* BuyNode(int x)
{BTNode* node = (BTNode*)malloc(sizeof(BTNode));if (node == NULL){perror("malloc failed");exit(-1);}node->left = NULL;node->right = NULL;node->val = x;return node;
}void PreOrder(BTNode* root)
{if (root == NULL)return;printf("%d ", root->val);PreOrder(root->left);PreOrder(root->right);
}void InOrder(BTNode* root)
{if (root == NULL)return;InOrder(root->left);printf("%d ", root->val);InOrder(root->right);
}void PastOrder(BTNode* root)
{if (root == NULL)return;PastOrder(root->left);PastOrder(root->right);printf("%d ", root->val);
}//相当于二叉树的后序遍历
int TreeSize(BTNode* root)
{//if (root == NULL)//	return 0;划分为左树的节点数+右树的节点数+1//return TreeSize(root->left) + TreeSize(root->right) + 1;//更简洁的写法(不过有弊端,在下个函数提)return root == NULL ? 0 : TreeSize(root->left) + TreeSize(root->right) + 1;
}//这个写法ok
//int TreeLeafSize(BTNode* root)
//{
//	//这里也要记得判断
//	//当递归不断深入时,遇到NULL不能继续使用了
//	if (root == NULL)
//		return;
//
//	static count = 0;
//
//	if (root->left == NULL && root->right == NULL)
//		count++;
//	
//	TreeLeafSize(root->left);
//	TreeLeafSize(root->right);
//
//	return count;
//	//这里在递归里可以return是因为虽然随着递归的进行,每次递归分路回流的时候都会return一次count
//	//但是最后正确的count会覆盖之前的值
//}//递归写法
int TreeLeafSize(BTNode* root)
{//1.空节点返回0if (root == NULL)return 0;//为什么这里空节点 return; 时结果却是4呢//经测试,return ; 时默认就return 1回去,所以这样子求得的数值就是最后一层满载的时候的节点个数//2.叶子节点返回1if (root->left == NULL && root->right == NULL)return 1;//3.其他节点就递归到左右子树return TreeLeafSize(root->left) + TreeLeafSize(root->right);
}int TreeKLevelSize(BTNode* root, int k)
{if (root == NULL)return 0;//从第一层看第k层等于第二层看第k-1层//k == 1时表示递归走到了该层,此时节点不会为空,,即表示这层有节点if (k == 1)return 1;//二叉树中双路递归的思想真的很重要!!!return TreeKLevelSize(root->left, k - 1) + TreeKLevelSize(root->right, k - 1);
}BTNode* BinaryTreeFind(BTNode* root, int x)
{if (root == NULL)return NULL;else if (root->val == x)return root;BTNode* ret = NULL;//通过判空的方式很好的解决了如果后续的值不符合时返回的null值如何规避ret = BinaryTreeFind(root->left,x);if (ret != NULL)return ret;ret = BinaryTreeFind(root->right,x);if (ret != NULL)return ret;
}//后序遍历销毁
void BinaryTreeDestroy(BTNode* root)
{if (root == NULL)return;BinaryTreeDestroy(root->left);BinaryTreeDestroy(root->right);free(root);//在这里置空无用,因为是形参//root = NULL;
}//要利用队列先进先出的特点
//最高层先入队列,然后依次出队列,在出队列的过程中将左右子树带入队列,直到队列为空
void LevelOrder(BTNode* root)
{Queue q;QueueInit(&q);if (root != NULL)QueuePush(&q, root);while (!QueueEmpty(&q)){printf("%d", QueueFront(&q)->val);if (QueueFront(&q)->left != NULL)QueuePush(&q,QueueFront(&q)->left);if (QueueFront(&q)->right != NULL)QueuePush(&q, QueueFront(&q)->right);QueuePop(&q);}QueueDestroy(&q);
}//思路类似层序遍历,只不过在入队列的时候要把左右子树全部入进去,即时是为空的情况下
//为什么呢,因为完全二叉树在物理结构上一定是连续的
//如果队列还没出完的情况下,就已经遇到空值了,说明就不是完全二叉树
int BinaryTreeComplete(BTNode* root)
{Queue q;QueueInit(&q);if (root == NULL)return 1;QueuePush(&q, root);while (!QueueEmpty(&q)){if (QueueFront(&q) == NULL)break;//if (QueueFront(&q)->left != NULL)QueuePush(&q, QueueFront(&q)->left);//if (QueueFront(&q)->right != NULL)QueuePush(&q, QueueFront(&q)->right);QueuePop(&q);}//走到这意味着遇到空,如果此时队列里都是空,则表示是完成二叉树while (!QueueEmpty(&q)){if (QueueFront(&q) == NULL){QueuePop(&q);continue;}QueueDestroy(&q);return 0;}QueueDestroy(&q);return 1;
}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;
}

test.c

#define _CRT_SECURE_NO_WARNINGS 1#include"BinaryTree.h"
#include"Queue.h"int main()
{//手动构建一棵树BTNode* node1 = BuyNode(1);BTNode* node2 = BuyNode(2);BTNode* node3 = BuyNode(3);BTNode* node4 = BuyNode(4);BTNode* node5 = BuyNode(5);BTNode* node6 = BuyNode(6);//BTNode* node7 = BuyNode(7);//BTNode* node8 = BuyNode(8);node1->left = node2;node2->left = node3;//node2->right = node7;node1->right = node4;node4->left = node5;node4->right = node6;//node3->right = node8;//前中后序// //PreOrder(node1);//printf("\n");////InOrder(node1);//printf("\n");//PastOrder(node1);//printf("\n");//总节点数、叶子节点数、第k层节点数// //printf("%d\n", TreeSize(node1));//printf("%d\n", TreeLeafSize(node1));//count = 0;//这里有个缺陷就是使用static局部变量之后这个函数只能调用一次//printf("%d\n", TreeLeafSize(node1));//printf("%d", TreeKLevelSize(node1,3));//BTNode* ret = BinaryTreeFind(node1,5);//printf("%d", ret->val);//BinaryTreeDestroy(node1);//node1 = NULL;//在外面置空一下就好了//LevelOrder(node1);//printf("%d", BinaryTreeComplete(node1));//printf("%d", BinaryTreeHeight(node1));return 0;
}

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

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

相关文章

PixMIM论文笔记

论文名称&#xff1a;PixMIM: Rethinking Pixel Reconstruction in Masked Image Modeling 发表时间&#xff1a;2023 年 3 月 4 日 作者及组织&#xff1a;上海人工智能实验室、西蒙菲莎大学、香港中文大学 GitHub&#xff1a;https://github.com/open-mmlab/mmselfsup/tree/d…

transformer_01

一、传统RNN存在的问题 1.序列前序太长&#xff0c;每个xi要记住前面的特征&#xff0c;而且一直在学&#xff0c;没有忘记&#xff0c;可能特征不能学的太好 2.串行&#xff0c;层越多越慢&#xff0c;难以堆叠很多层&#xff1b; 3.只能看到过去&#xff0c;不能看到未来 搞…

什么是NetApp的DQP和如何安装DQP?

首先看看什么是DQP&#xff0c;DQPDisk Qualification Package&#xff0c;文字翻译就是磁盘验证包。按照NetApp的最佳实践&#xff0c;要定期升级DQP包&#xff0c;保证对最新磁盘和磁盘扩展柜的兼容。 本文主要介绍7-mode下如何升级DQP&#xff0c;至于cluster mode另外文章…

Linux Zabbix企业级监控平台+cpolar实现远程访问

文章目录 前言1. Linux 局域网访问Zabbix2. Linux 安装cpolar3. 配置Zabbix公网访问地址4. 公网远程访问Zabbix5. 固定Zabbix公网地址 前言 Zabbix是一个基于WEB界面的提供分布式系统监视以及网络监视功能的企业级的开源解决方案。能监视各种网络参数&#xff0c;保证服务器系…

基于边缘网关构建水污染监测治理方案

绿水青山就是金山银山&#xff0c;生态环境才是人类最宝贵的财富。但是在日常生活生产中&#xff0c;总是免不了各种污水的生产、排放。针对生产生活与环境保护的均衡&#xff0c;可以借助边缘网关打造环境污水监测治理体系&#xff0c;保障生活与环境的可持续性均衡发展。 水污…

NewStarCTF2023week2-Upload again!

尝试传修改后缀的普通一句话木马&#xff0c;被检测 尝试传配置文件 .htaccess 和 .user.ini 两个都传成功了 接下来继续传入经过修改的木马 GIF89a <script language"php"> eval($_POST[cmd]); </script> 没有被检测&#xff0c;成功绕过 直接上蚁剑…

微查系统,一站式查询,让您的查询更加便捷

微查系统是挖数据一款功能强大的查询系统&#xff0c;是一个集多种查询和核验工具于一身的综合性平台。它可以大大简化企业和个人的查询流程&#xff0c;节省时间和成本&#xff0c;提高查询的准确性和效率。本文将介绍微查系统的主要特点&#xff0c;功能和使用方法&#xff0…

C++数据结构X篇_15_求二叉树叶子数与高度(递归方法)

本篇参考求二叉树叶子数与高度&#xff08;C&#xff09;进行整理。 文章目录 1. 二叉树中叶子数与高度2. 求二叉树叶子数与高度的实现代码 1. 二叉树中叶子数与高度 我们首先来看一看二叉树中叶子数与高度的定义&#xff1a; 叶子数&#xff1a;对于一个二叉树的节点&#x…

新型的终端复用器 tmux

以前遇到长时间执行任务时&#xff0c;一般是使用nohup加后台运行&#xff0c;但是涉及到少量代码编写。 同事介绍了一个screen命令&#xff0c;根据文档&#xff0c;此命令已经过时&#xff0c;最新的命令是tmux。 tmux的介绍文档&#xff0c;RedHat的这一篇非常不错。 在文…

图详解第四篇:单源最短路径--Dijkstra算法

文章目录 1. 最短路径问题2. 单源最短路径--Dijkstra算法算法思想图解如何存储路径及其权值代码实现调式观察打印最短路径Dijkstra算法的缺陷 3. 源码 1. 最短路径问题 最短路径问题&#xff1a; 从带权有向图&#xff08;求最短路径通常是有向图&#xff09;G中的某一顶点出发…

linux下的rsync(文件同步) 用法教程

一、简介 rsync 是一个常用的 Linux 应用程序&#xff0c;用于文件同步。 它可以在本地计算机与远程计算机之间&#xff0c;或者两个本地目录之间同步文件&#xff08;但不支持两台远程计算机之间的同步&#xff09;。它也可以当作文件复制工具&#xff0c;替代cp和mv命令。 …

BIO实战、NIO编程与直接内存、零拷贝深入剖析

原生 JDK 网络编程 BIO BIO&#xff0c;意为 Blocking I/O&#xff0c;即阻塞的 I/O。   BIO 基本上就是我们上面所说的生活场景的朴素实现。在 BIO 中类 ServerSocket 负责绑定 IP 地址&#xff0c;启动监听端口&#xff0c;等待客户连接&#xff1b;客户端 Socket 类的实例…

SpringMVC源码分析(三)HandlerExceptionResolver启动和异常处理源码分析

问题&#xff1a;异常处理器在SpringMVC中是如何进行初始化以及使用的&#xff1f; Spring MVC提供处理异常的方式主要分为两种&#xff1a; 1、实现HandlerExceptionResolver方式&#xff08;HandlerExceptionResolver是一个接口&#xff0c;在SpringMVC有一些默认的实现也可以…

【算法练习Day22】 组合总和 III电话号码的字母组合

​&#x1f4dd;个人主页&#xff1a;Sherry的成长之路 &#x1f3e0;学习社区&#xff1a;Sherry的成长之路&#xff08;个人社区&#xff09; &#x1f4d6;专栏链接&#xff1a;练题 &#x1f3af;长路漫漫浩浩&#xff0c;万事皆有期待 文章目录 组合总和 III剪枝 电话号码…

node 通过axios发送post请求(FormData)

方案一&#xff1a; const axios require(axios) const FormData require(form-data) const fs require(fs)const sdUpscaleOnAzure async (req, res) > {const data new FormData()data.append(image, fs.readFileSync(/temp/ai/sd/download/1.png))let config {hea…

R/d2及S/C4估计总体标准差,比较其CPK及规格限概率的差异

R/d2 和 S/C4 是用于估计总体标准差的无偏估计方法&#xff0c;通常用于控制图中。这些估计方法的主要目的是通过样本数据来估计总体标准差&#xff0c;以便监测过程的稳定性和变异性&#xff0c;而不需要收集整个总体的数据。 具体来说&#xff1a; R图中的 R/d2 和 S图中的…

gitlab自编译 源码下载

网上都是怎么用 gitlab&#xff0c;但是实际开发中有需要针对 gitlab 进行二次编译自定义实现功能的想法。 搜索了网上的资料以及在官网的查找&#xff0c;查到了如下 gitlab 使用 ruby 开发。 gitlab 下载包 gitlab/gitlab-ce - Packages packages.gitlab.com gitlab/gitl…

本地搭建渲染农场和云渲染农场哪个更推荐?看完帮你省下几个w

&#xfeff; 渲染农场是由众多机器组成的渲染集群&#xff0c;主要用于渲染单帧效果图或动画项目。凭借渲染农场的强大计算能力&#xff0c;设计师能够满足3D项目紧迫的交期要求。最近&#xff0c;小编注意到许多设计师对以下问题产生了疑惑&#xff1a; 是否可以自行搭建渲…

MySQL查询优化看一篇就够了

关联查询优化 数据准备 #分类 CREATE TABLE IF NOT EXISTS type( id INT(10) UNSIGNED NOT NULL AUTO_INCREMENT, card INT(10) UNSIGNED NOT NULL, PRIMARY KEY ( id ) );#图书 CREATE TABLE IF NOT EXISTS book(bookid INT(10) UNSIGNED NOT NULL AUTO_INCREMENT,cardINT(10…

fatal:Could not read from remote repository解决方法

Linux服务器如何连接GitHub&#xff1f; 生成SSH密钥 ssh-keygen -C “邮箱” -t rsa 存放位置一般是/root/.ssh/id_rsa 登录个人github&#xff0c;添加客户端生成的公钥 打开Settings&#xff0c;点击SSH and GPG keys&#xff0c;点击New SSH Key。Key中粘贴id_rsa.pub…