二叉树与递归的相爱相杀

数据结构之二叉树

  • 一、基于二叉树的基础操作
    • 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.hqwc.cn/news/138547.html

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

相关文章

antd的RangePicker设置默认值,默认近七天(andt+react)

import moment from "moment";state {initData:[moment().startOf(day).subtract(6, d), moment().endOf(day)], }<FormItem label"产生时间" {...tailItemLayout}>{getFieldDecorator("produceTime", {initialValue: initData})(<Ran…

深度学习——卷积神经网络(CNN)基础二

深度学习——卷积神经网络&#xff08;CNN&#xff09;基础二 文章目录 前言三、填充和步幅3.1. 填充3.2. 步幅3.3. 小结 四、多输入多输出通道4.1. 多输入通道4.2. 多输出通道4.3. 11卷积层4.4. 小结 总结 前言 上文对卷积有了初步的认识&#xff0c;其实卷积操作就是通过卷积…

Android---自定义View

当 Android SDK 中提供的系统 UI 控件无法满足业务需求时&#xff0c;需要考虑自己实现 UI 控件。掌握自定义控件&#xff0c;是理解整套 Android 渲染体系的基础。自定义 UI 控件有2种方式&#xff1a; 继承系统提供的成熟控件&#xff08;比如 LinearLayout、RelativeLayout、…

项目管理软件排行榜:点赞榜TOP5揭晓!

通过项目管理软件企业可以快速、高效地管理项目、整合团队成员以及资源。现如今市场上各类项目管理软件层出不穷&#xff0c;因此选择一款适合自身企业需求的软件显得尤为重要。本文将为大家介绍项目管理软件排行榜点赞榜&#xff0c;为大家选购提供一些参考。 1.Zoho Project…

app查看 证书公钥和md5

获取App的公钥和MD5是一项重要的安全操作&#xff0c;需要谨慎处理。一般情况下&#xff0c;我们无法直接从已安装的App中获取其公钥和MD5信息。如果你是App的开发者&#xff0c;你可以通过以下方式获取&#xff1a; 在你的项目中&#xff0c;找到生成APK的地方&#xff08;一…

C/C++笔试易错与高频题型图解知识点(二)—— C++部分(持续更新中)

目录 1.构造函数初始化列表 1.1 构造函数初始化列表与函数体内初始化区别 1.2 必须在初始化列表初始化的成员 2 引用&引用与指针的区别 2.1 引用初始化以后不能被改变&#xff0c;指针可以改变所指的对象 2.2 引用和指针的区别 3 构造函数与析构函数系列题 3.1构造函数与析…

el-checkbox-group变成竖着的样式

加 style"display: block; padding-top: 10px; margin-left: 27px" <el-checkbox:indeterminate"isIndeterminate"v-model"checkAll"change"handleCheckAllChange">全选&#xff08;{{ memberList.length }}&#xff09;</el…

七大排序 (9000字详解直接插入排序,希尔排序,选择排序,堆排序,冒泡排序,快速排序,归并排序)

一&#xff1a;排序的概念及引入 1.1 排序的概念 1.1 排序的概念 排序&#xff1a;所谓排序&#xff0c;就是使一串记录&#xff0c;按照其中的某个或某些关键字的大小&#xff0c;递增或递减的排列起来的操作。 稳定性&#xff1a;假定在待排序的记录序列中&#xff0c;存在…

Nginx:反向代理(示意图+配置)

示意图&#xff1a; 反向代理 反向代理&#xff08;Reverse Proxy&#xff09;是代理服务器的一种&#xff0c;它代表服务器接收客户端的请求&#xff0c;并将这些请求转发到适当的服务器。当请求在后端服务器完成之后&#xff0c;反向代理搜集请求的响应并将其传输给客户端。…

基于龙格-库塔优化的BP神经网络(分类应用) - 附代码

基于龙格-库塔优化的BP神经网络&#xff08;分类应用&#xff09; - 附代码 文章目录 基于龙格-库塔优化的BP神经网络&#xff08;分类应用&#xff09; - 附代码1.鸢尾花iris数据介绍2.数据集整理3.龙格-库塔优化BP神经网络3.1 BP神经网络参数设置3.2 龙格-库塔算法应用 4.测试…

香港高才通通过后要做什么?- 2 缴费

今天缴费系统已经可以使用&#xff0c;登录缴费一下 打开从邮箱下载的确认邮件 打开文件中提供的【入境处网站】链接 单击【缴费和领取电子签证】&#xff0c;单击链接【线上付款】 在付款页面单击【电子签证&#xff0c;线上付款】 单击【开始】 输入档案编号&#xff0c…

【Java基础面试十二】、说一说你对面向对象的理解

文章底部有个人公众号&#xff1a;热爱技术的小郑。主要分享开发知识、学习资料、毕业设计指导等。有兴趣的可以关注一下。为何分享&#xff1f; 踩过的坑没必要让别人在再踩&#xff0c;自己复盘也能加深记忆。利己利人、所谓双赢。 面试官&#xff1a; 说一说你对面向对象的理…