AVL树实现

news2024/11/15 13:58:01

目录

​编辑

一,AVL树的概念

二,实现AVL树(部分)

1.AVL树的节点

2.AVL数的插入

1.当根节点为nullptr时要执行如下代码:

2.当根节点不为nullptr时

1.当parent的_bf变为0时,parent之前的_bf的大小就是-1或者1如下:

2.当parent的平衡因子被更新为1或者-1时:

3.当parent的平衡因子更新后变成-2或者2时

三,一些其他函数

四,全部代码


一,AVL树的概念

首先我们得先知道AVL树是个啥,AVL树是个啥呢?百度一下你就知道:

在百度上的概念如上。根据AVL树的概念我们可以总结出AVL树有如下特点:

1.本身首先是一棵二叉搜索树。

2.带有平衡条件:每个结点的左右子树的高度之差的绝对值(平衡因子)最多为1。

二,实现AVL树(部分)

1.AVL树的节点

 首先我们根据AVL树的特点可以想到的AVL树节点成员有:

1.因为AVL树还是一棵二叉树,所以AVL树中必有指针left与right,而且还有一个变量表示节点值。

2.因为二AVL树需要记录高度差,所以在节点内就会有一个变量来记录高度差,这个变量便是平衡因子。并且这个变量的类型是int。

3.最后为了方便后续操作,我们的节点内还会存放一个指针叫做parent,这个节点指向当前节点的父节点。

所以写出的节点代码如下:

template <class K,class V>
	struct AVL_Node//节点
	{
		AVL_Node(pair<K,V>& key)
			:parent(nullptr)
			,_left(nullptr)
			,_right(nullptr)
			,_bf(0)
			,_key(key)
		{}

		AVL_Node<K, V>* parent;//父节点指针
		AVL_Node<K, V>* _left;//左孩子指针
		AVL_Node<K, V>* _right;//右孩子指针
		int _bf;//平衡因子
		pair<K, V>_key;//val
	};

2.AVL数的插入

AVL树的插入是我们这次实现的重头戏,在插入时要理解的重点内容便是:

1.插入时的旋转。

2.平衡因子的调整。

现在先来写在旋转前的代码:

1.当根节点为nullptr时要执行如下代码:
if (_root == nullptr)//当根节点为空时
{
	_root = new AVL_Node<K, V>(key);
}
2.当根节点不为nullptr时

1.先找到插入位置:

AVL_Node<K, V>* cur = _root;
AVL_Node<K, V>* parent = cur;
while (cur)
{
if (key < cur->_key)
{
  parent = cur;
  cur = cur->_left;
}
else if (key > cur->_key)
{
	parent = cur;
	cur = cur->_right;
}
else
{
	return false;
}
}						

这段寻找插入位置的代码和二叉搜索树的代码相同。

2.找到插入位置以后便开始进行插入

cur = new AVL_Node<K, V>(key);
if (key.first > parent->_key.first)
{
	parent->_right = cur;
}
	else
{
  parent->_left = cur;
}

 cur->_parent = parent;//更新父节点

3.调整上一个节点的平衡因子。依据:_bf = 右节点的高度--左节点的高度

所以当我插入左边时:

if (cur == parent->_left)//左边添加节点,parent的_bf减1
{
	parent->_bf--;
}

当我插入右边时:

else if (cur == parent->_right)//右边添加节点,parntt的高度加1
{
  parent->_bf++;
}

4.根据父节点的_bf的大小来决定是否需要往上调整

1.当parent的_bf变为0时,parent之前的_bf的大小就是-1或者1如下:

其实我的parent变成0以后AVL树的高度下降,所以对我的其它节点的平衡因子的大小是没有什么影响的,所以在这种情况下我便可以直接退出:

if (parent->_bf == 0)//子树高度不变不影响整棵树高度
{
  break;
}
2.当parent的平衡因子被更新为1或者-1时:

能变更新成这种情况,那我之前的平衡因子一定为0了。这时候,我整棵树的高度肯定是增加了,所以平衡因子必须改变所以代码如下:

else if (parent->_bf == 1 || parent->_bf == -1)//子树高度变了,但是在接受范围内,节点的平衡因子要做调整
{
   cur = parent;
  parent = parent->_parent;
}

这段代码是不全的,这里只展示了节点向上调整的操作,如果想看整段代码请往后看。

3.当parent的平衡因子更新后变成-2或者2时

这个时候就发生异常了,这个时候就要开始旋转了。这个时候的旋转又分为四种:

1.单纯的右边高

如以下情况:

可以看到在插入新的节点以后,树的高度发生了变化并且都是右边的高度高且出现了异常情况。所以这个时候就要发生左旋来调整高度,示意图如下:

根据这个过程写出如下代码:

void Leftuniflex(AVL_Node<K, V>* parent)
	{
        //记录要旋转的节点
		AVL_Node<K, V>* subR = parent->_right;
		AVL_Node<K, V>* subRL = subR->_left;

        //旋转
		parent->_right = subRL;
		subR->_left = parent;

		//更新父节点
		AVL_Node<K, V>* ppNode = parent->_parent;
		parent->_parent = subR;
		if (subRL)
			subRL->_parent = parent;

		if (parent == _root)
		{
			_root = subR;
		}
		 
		subR->_parent = ppNode;

		//更新平衡因子
		parent->_bf = subR->_bf = 0;
	}

因为在示意图里没有显示父节点的更新,所以在这里要注意对于父节点的更新。

2.单纯的左边高

如下情况:

看到这个示意图是否有一种似曾相识的感觉呢?其实单纯的左边高的旋转操作方法和单纯右边高的操作方法一样,只不过旋转的方向改变了。根据示意图写出代码如下:
 

void Rightuniflex(AVL_Node<K, V>* parent)
	{
		//旋转
		AVL_Node<K, V>* subL = parent->_left;
		AVL_Node<K, V>* subLR = subL->_right;
		parent->_left = subLR;
		subL->_right = parent;

		//更新父节点
		AVL_Node<K, V>* ppNode = parent->_parent;
		parent->_parent = subL;
		if (subLR)
			subLR->_parent = parent;

		if (parent == _root)
		{
			_root = subL;
		}

		subL->_parent = ppNode;

		//更新平衡因子
		parent->_bf = subL->_bf = 0;
	}

3.右边整体高但是局部矮

解决方法:1.将整棵AVL树通过右单旋调整成单纯的右边高然后再将整棵树通过左单旋调整成平衡的AVL树。比如如以下情况:

一,新增节点插入到60的右节点b处,此时60的_bf为-1

在这种情况下,首先便要以90这个节点为parent进行右单旋让整棵树变成单纯的右边高。如下图所示:

在转化为单纯的右边高以后,便可以执行第二步了。第二步的操作便是和左旋操作是一样的,如图所示:

二,新增节点插入到60的右节点c处,此时60的_bf为1

画出示意图如下:

然后还是一样的操作将这棵树变成单纯的右边高:

然后继续执行第二步,也是单纯的左旋操作:

三,当h为0时,新增节点为60。此时60的_bf = 0

在这个时候还是一样的操作:先变成单纯的右边高

然后再操作:

再右边整体高,局部左边高的节点插入情况便是以上三种。其实这些情况的旋转操作都是一样的但是对应的平衡因子_bf的更新是不一样的。

根据以上情况写出代码如下:

//右左双旋
	void RLuniflex(AVL_Node<K, V>* parent)
	{
		AVL_Node<K, V>* subR = parent->_right;
		AVL_Node<K, V>* subRL = subR->_left;
		int bf = subRL->_bf;

		Rightuniflex(subR);//先局部右旋
		Leftuniflex(parent);//再整体左旋


		//更新平衡因子
		if (bf == 0)//subRL自己就是新增
		{
			parent->_bf = subR->_bf = subRL->_bf = 0;
		}

		else if (bf == -1)
		{
			subR->_bf = 1;
			parent->_bf = subRL->_bf = 0;
		}

		else if (bf == 1)
		{
			parent->_bf = -1;
			subRL->_bf = subR->_bf = 0;
		}
		else
		{
			assert(false);
		}
		
	}

4.当左边整体高但是右边局部高时:

在这种情况下的分析方法和《当右边整体高但是左边局部高》的分析方法相同。也分为三步:

1.现局部左旋让AVL树变成单纯的左边高。

2.再整体右旋调整树的平衡。

3.调整平衡因子。

分析方法和上述代码相同,在这里就直接写代码了:

//左右双旋(狗日的平衡因子,让我搞了半天,哭了)
	void LRuniflex(AVL_Node<K, V>* parent)
	{
		AVL_Node<K, V>* subL = parent->_left;
		AVL_Node<K, V>* subLR = subL->_right;
		int bf = subLR->_bf;

		Leftuniflex(subL);//先局部左旋
		Rightuniflex(parent);//再整体右旋


		//更新平衡因子
		if (bf == 0)//subRL自己就是新增
		{
			parent->_bf = subL->_bf = subLR->_bf = 0;
		}

		else if (bf == -1)
		{
			subL->_bf = 0;
			parent->_bf = 1;
		    subLR->_bf = 0;
		}

		else if (bf == 1)
		{
			parent->_bf = 0;
			subLR->_bf = 0;
			subL->_bf = -1;
		}
		else
		{
			assert(false);
		}

	}

兄弟们在写代码时千万不要和我一样粗心,找bug找了好久!!!!

三,一些其他函数

1.中序遍历函数

void Inorder()
{
  _Inorder(_root);
}
void _Inorder(AVL_Node<K, V>* root)
{
	if (root == nullptr)
	{
		return;
	}

  _Inorder(root->_left);
  cout << root->_key.first << " ";
 _Inorder(root->_right);
}

记住,AVL树的中序遍历也是一个有序的数列。

2.判断平衡的函数

int Height(AVL_Node<K, V>* root)
		{
			if (root == nullptr)
			{
				return 0;
			}

			int L = Height(root->_left);
			int R = Height(root->_right);

			return L > R ? L + 1 : R + 1;
		}

		bool _Isblance(AVL_Node<K, V>* root)
		{
			if (root == nullptr)
			{
				return true;
			}

			int R = Height(root->_right);
			int L = Height(root->_left);
			int bf = root->_bf;

			if (R - L!=bf)
			{
				cout << "R-L:" << R - L << endl;
				cout << root->_key.first << "->" << root->_bf <<"平衡因子异常" << endl;
				return false;
			}

			return (abs(R-L)<2)&& _Isblance(root->_left) && _Isblance(root->_right);
			 
		}

四,全部代码

#include<iostream>
#include<assert.h>
using namespace std;
using std::pair;

namespace AVL
{
	template <class K,class V>
	struct AVL_Node//节点
	{
		AVL_Node(pair<K,V>& key)
			:_parent(nullptr)
			,_left(nullptr)
			,_right(nullptr)
			,_bf(0)
			,_key(key)
		{}

		AVL_Node<K, V>* _parent;
		AVL_Node<K, V>* _left;
		AVL_Node<K, V>* _right;
		int _bf;
		pair<K, V>_key;
	};


	template <class K, class V>
	class AVL_Tree
	{
	public:
		bool insert( pair<K, V> key)
		{
			if (_root == nullptr)//当根节点为空时
			{
				_root = new AVL_Node<K, V>(key);
			}

			else//当根节点不为空时
			{
				AVL_Node<K, V>* cur = _root;
				AVL_Node<K, V>* parent = cur;
				while (cur)
				{
					if (key.first < cur->_key.first)
					{
						parent = cur;
						cur = cur->_left;
					}
					else if (key.first > cur->_key.first)
					{
						parent = cur;
						cur = cur->_right;
					}
					else
					{
						return false;
					}
				}

				cur = new AVL_Node<K, V>(key);
				if (key.first > parent->_key.first)
				{
					parent->_right = cur;
				 }
				else
				{
					parent->_left = cur;
				}

				cur->_parent = parent;

				while (parent)
				{
					if (cur == parent->_left)//左边添加节点,parent的高度减1
					{
						parent->_bf--;
					}
					else if (cur == parent->_right)//右边添加节点,parent的高度加1
					{
						parent->_bf++;
					}

					if (parent->_bf == 0)//子树高度不变不影响整棵树高度
					{
						break;
					}
					else if (parent->_bf == 1 || parent->_bf == -1)//子树高度变了,但是在接受范围内,节点的平衡因子要做调整
					{
						cur = cur->_parent;
						parent = parent->_parent;
					}
					else if (parent->_bf == 2 || parent->_bf == -2)
					{
						//Inorder();
						//出现异常该旋转了
						//单纯右边高,发生左单旋
						if (parent->_bf == 2 && cur->_bf == 1)
						{
							 Leftuniflex(parent);
						}
						//单纯左边高,发生右单旋
						else if (parent->_bf == -2 && cur->_bf == -1)
						{
							 Rightuniflex(parent);

						}
						//右边整体高,局部矮
						else if (parent->_bf == 2 && cur->_bf == -1)
						{
							RLuniflex(parent);
						}
						//左边整体高,局部矮
						else if (parent->_bf == -2 && cur->_bf == 1)
						{
							LRuniflex(parent);
						}
					    
						else
						{
							assert(false);
						}
						break;
					}

					else
					{
						assert(false);
					}
			
				}
			}
				return true;
	    }

	void Leftuniflex(AVL_Node<K, V>* parent)
	{
		AVL_Node<K, V>* subR = parent->_right;
		AVL_Node<K, V>* subRL = subR->_left;
		parent->_right = subRL;
		subR->_left = parent;

		//更新父节点
		AVL_Node<K, V>* ppNode = parent->_parent;
		parent->_parent = subR;
		if (subRL)
			subRL->_parent = parent;

		if (parent == _root)
		{
			_root = subR;
		}
		 
		else
		{
			if (parent == ppNode->_left)
			{
				ppNode->_left = subR;
			}
			else
			{
				ppNode->_right = subR;
			}
		}
		subR->_parent = ppNode;

		//更新平衡因子
		parent->_bf = subR->_bf = 0;
	}


	void Rightuniflex(AVL_Node<K, V>* parent)
	{
		//旋转
		AVL_Node<K, V>* subL = parent->_left;
		AVL_Node<K, V>* subLR = subL->_right;
		parent->_left = subLR;
		subL->_right = parent;

		//更新父节点
		AVL_Node<K, V>* ppNode = parent->_parent;
		parent->_parent = subL;
		if (subLR)
			subLR->_parent = parent;

		if (parent == _root)
		{
			_root = subL;
		}

		else
		{
			if (parent == ppNode->_left)
			{
				ppNode->_left = subL;
			}
			else
			{
				ppNode->_right = subL;
			}
		}
		subL->_parent = ppNode;

		//更新平衡因子
		parent->_bf = subL->_bf = 0;
	}

	//右左双旋
	void RLuniflex(AVL_Node<K, V>* parent)
	{
		AVL_Node<K, V>* subR = parent->_right;
		AVL_Node<K, V>* subRL = subR->_left;
		int bf = subRL->_bf;

		Rightuniflex(subR);//先局部右旋
		Leftuniflex(parent);//再整体左旋


		//更新平衡因子
		if (bf == 0)//subRL自己就是新增
		{
			parent->_bf = subR->_bf = subRL->_bf = 0;
		}

		else if (bf == -1)
		{
			subR->_bf = 1;
			parent->_bf = subRL->_bf = 0;
		}

		else if (bf == 1)
		{
			parent->_bf = -1;
			subRL->_bf = subR->_bf = 0;
		}
		else
		{
			assert(false);
		}
		
	}

	//左右双旋(狗日的平衡因子,让我搞了半天,哭了)
	void LRuniflex(AVL_Node<K, V>* parent)
	{
		AVL_Node<K, V>* subL = parent->_left;
		AVL_Node<K, V>* subLR = subL->_right;
		int bf = subLR->_bf;

		Leftuniflex(subL);//先局部左旋
		Rightuniflex(parent);//再整体右旋


		//更新平衡因子
		if (bf == 0)//subRL自己就是新增
		{
			parent->_bf = subL->_bf = subLR->_bf = 0;
		}

		else if (bf == -1)
		{
			subL->_bf = 0;
			parent->_bf = 1;
		    subLR->_bf = 0;
		}

		else if (bf == 1)
		{
			parent->_bf = 0;
			subLR->_bf = 0;
			subL->_bf = -1;
		}
		else
		{
			assert(false);
		}

	}


	void Inorder()
	{
		_Inorder(_root);
	}

	void Isblance()
	{
		_Isblance(_root);
	}
	 


	private:

		void _Inorder(AVL_Node<K, V>* root)
		{
			if (root == nullptr)
			{
				return;
			}

			_Inorder(root->_left);
			cout << root->_key.first << " ";
			_Inorder(root->_right);
		}

		int Height(AVL_Node<K, V>* root)
		{
			if (root == nullptr)
			{
				return 0;
			}

			int L = Height(root->_left);
			int R = Height(root->_right);

			return L > R ? L + 1 : R + 1;
		}

		bool _Isblance(AVL_Node<K, V>* root)
		{
			if (root == nullptr)
			{
				return true;
			}

			int R = Height(root->_right);
			int L = Height(root->_left);
			int bf = root->_bf;

			if (R - L!=bf)
			{
				cout << "R-L:" << R - L << endl;
				cout << root->_key.first << "->" << root->_bf <<"平衡因子异常" << endl;
				return false;
			}

			return (abs(R-L)<2)&& _Isblance(root->_left) && _Isblance(root->_right);
			 
		}

		AVL_Node<K, V>* _root = nullptr;
	};

}

 

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

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

相关文章

使用持久卷部署 WordPress 和 MySQL

&#x1f5d3;️实验环境 OS名称Microsoft Windows 11 家庭中文版系统类型x64-based PCDocker版本Docker version 24.0.6, build ed223bcminikube版本v1.32.0 &#x1f587;️创建 kustomization.yaml 你可以通过 kustomization.yaml 中的生成器创建一个 Secret存储密码或密…

2024年csdn最新最全的web自动化测试思路及实战

Page Objects 设计模式 Page Objects概念&#xff1a; Page Objects是指UI界面上用于与用户进行交互的对象 pageobjects 设计模式概念&#xff1a; pageobjects 模式是Selenium中的一种测试设计模式&#xff0c;主要是将每一个页面设计为一个Class&#xff0c;其中包含页面中…

滑动窗口练习(一)— 固定窗口最大值问题

题目 假设一个固定大小为W的窗口&#xff0c;依次划过arr&#xff0c; 返回每一次滑出状况的最大值 例如&#xff0c;arr [4,3,5,4,3,3,6,7], W 3 返回&#xff1a;[5,5,5,4,6,7] 暴力对数器 暴力对数器方法主要是用来做校验&#xff0c;不在乎时间复杂度&#xff0c;逻辑上…

95. 最长公共子序列

题目 题解 class Solution:def longestCommonSubsequence(self, text1: str, text2: str) -> int:# 定义状态&#xff1a;dp[i][j]表示s1[0:i]和s2[0:j]的最长公共子序列dp [[0 for j in range(len(text2)1)] for i in range(len(text1) 1)]# badcase: dp[i][0] 0, dp[0…

[C/C++]数据结构 LeetCode:用栈实现队列

题目描述: 请你仅使用两个栈实现先入先出队列。队列应当支持一般队列支持的所有操作&#xff08;push、pop、peek、empty&#xff09;&#xff1a; 实现 MyQueue 类&#xff1a; void push(int x) 将元素 x 推到队列的末尾int pop() 从队列的开头移除并返回元素int peek() 返…

漫谈广告机制设计 | 万剑归宗:聊聊广告机制设计与收入提升的秘密(3)

​书接上文漫谈广告机制设计 | 万剑归宗&#xff1a;聊聊广告机制设计与收入提升的秘密&#xff08;2&#xff09;&#xff0c;我们聊到囚徒困境是完全信息静态博弈&#xff0c;参与人存在占优策略&#xff0c;最终达到占优均衡&#xff0c;并且是对称占优均衡。接下来我们继续…

未来 20 年 12 大发展趋势

未来 20 年 12 大发展趋势 周末闲来无聊&#xff0c;翻阅以前的材料&#xff0c;常读常新的感觉。 前言 跟30年后的我们相比&#xff0c;现在的我们就是一无所知。必须要相信那些不可能的事情&#xff0c;因为我们尚处于第一天的第一个小时——开始的开始。 技术都会有一个…

SpringBoot——入门及原理

SpringBoot用来简化Spring应用开发&#xff0c;约定大于配置&#xff0c;去繁从简&#xff0c;是由Pivotal团队提供的全新框架。其设计目的是用来简化新Spring应用的初始搭建以及开发过程。该框架使用了特定的方式来进行配置&#xff08;有特殊需求可以添加自己的配置覆盖默认配…

Unity 场景烘培 ——unity Post-Processing后处理1(四)

提示&#xff1a;文章有错误的地方&#xff0c;还望诸位大神不吝指教&#xff01; 文章目录 前言一、Post-Processing是什么&#xff1f;二、安装使用Post-Processing1.安装Post-Processing2.使用Post-Processing&#xff08;1&#xff09;.添加Post-process Volume&#xff08…

常用组合逻辑verilog实现之8-3优先编码器

文章目录 一、问题描述二、verilog源码三、综合及仿真结果一、问题描述 本例中将实现一个8-3优先编码器。优先编码器允许多个输入信号同时有效,输出针对优先级别高的信号进行编码。 8-3优先编码器有对应的芯片实现比如TI公司的CD4532,可以从下面链接下载其手册。 CD4532数…

【C++】入门三

接下来我们说一下引用这个概念&#xff0c;那么什么是引用呢&#xff1f;简单来说引用就是取别名&#xff0c;比如有一个变量叫a&#xff0c;现在我给它取了一个别名叫b&#xff0c;那么此时a和b管理的都是一块空间 这个例子就可以很好的体现a和b管理的是同一块空间&#xff0…

ClientDateSet:Cannot perform this operation on a closed dataset

一、问题表现 Delphi 三层DataSnap&#xff0c;使用AlphaControls控件优化界面&#xff0c;一窗口编辑时&#xff0c;出现下列错误提示&#xff1a; 编译通过&#xff0c;该窗口中&#xff0c;重新显示数据&#xff0c;下图&#xff1a; 相关代码&#xff1a; procedure…

fusion 360制作机械臂

参考教程&#xff1a;Industrial Robot ( PART - 5) - FUSION 360 TUTORIAL_哔哩哔哩_bilibili

Java,集合框架,关于Map接口与Collections工具类

目录 Map接口 Map及其实现类的对比&#xff1a; HashMap中元素的特点&#xff1a; 相关方法&#xff1a; 添加、修改操作: 删除操作&#xff1a; 元素查询的操作: 元视图操作的方法&#xff1a; TreeMap的使用&#xff1a; Properties类&#xff1a; Collections工具…

【React】React 基础

1. 搭建环境 npx create-react-app react-basic-demo2. 基本使用 JSX 中使用 {} 识别 JavaScript 中的表达式&#xff0c;比如变量、函数调用、方法调用等。 if、switch、变量声明等属于语句&#xff0c;不是表达式。 列表渲染使用 map 。 事件绑定用&#xff1b;on 事件名称…

OSG加载模型时显示读取进度

目录 1. 前言 2. 开发环境说明 3. 功能实现 3.1. 方法1 3.2. 方法2 3.3. 方法3 4. 附加说明 1. 前言 OSG中加载模型文件到视景器&#xff0c;一般通过osgDB::readXXXX系列开头的函数来加载模型&#xff0c;如&#xff1a;osgDB::readNodeFile、osgDB::readImageFile、os…

055-第三代软件开发-控制台输出彩虹日志

第三代软件开发-控制台输出彩虹日志 文章目录 第三代软件开发-控制台输出彩虹日志项目介绍控制台输出彩虹日志实现原理真实代码 总结 关键字&#xff1a; Qt、 Qml、 关键字3、 关键字4、 关键字5 项目介绍 欢迎来到我们的 QML & C 项目&#xff01;这个项目结合了 QM…

MyBatis使用注解操作及XML操作

文章目录 1. 注解操作1.1 打印日志1.2 参数传递1.3 增&#xff08;Insert&#xff09;注意1&#xff1a;重命名注意2&#xff1a;返回主键 1.4 删&#xff08;Delete&#xff09;1.5 改&#xff08;Update&#xff09;1.6 查&#xff08;Select&#xff09;1. 配置&#xff0c;…

STM32串口重定向/实现不定长数据接收

STM32串口重定向/实现不定长数据接收 重定向MicroLIB 不定长数据接收 这是一期STM32内容代码分享&#xff0c;关于STM32重定向的代码和一些出现的问题吗&#xff0c;以及串口接收不定长数据思路 重定向 重定向的功能&#xff1a;能够在STM32中使用printf函数通过串口发送数据 …

最新版微信如何打开青少年模式?

最新版微信如何打开青少年模式&#xff1f; 1、将手机微信升级到最新版&#xff0c;并打开后点击底部我的进入&#xff1b; 2、在我的内&#xff0c;找到并点击设置进入&#xff1b; 3、在设置内找到青少年模式&#xff0c;并点击进入开启微信青少年模式&#xff1b; 原文来源…