【C++之容器篇】二叉搜索树的理论与使用

news2024/11/14 4:18:08

目录

    • 前言
    • 一、二叉搜索树的概念
    • 二、二叉搜素树的模拟实现(增删查非递归实现)
      • 1. 二叉搜素树的结点
      • 2. 二叉搜索树的实现
        • (1). 二叉搜索树的基本结构
        • (2)构造函数
        • (3)查找函数
        • (4)插入函数
        • (5) 删除函数
        • (6)中序遍历
        • (7)析构函数
        • (8)拷贝构造函数
        • (9)赋值运算符重载(现代写法)
    • 三、二叉搜索树的模拟实现(增删查递归实现)
      • 1. 查找函数
      • 2. 插入函数
      • 3. 删除函数

前言

在数据结构初阶我们学习了二叉树的相关知识,普通的二叉树的作用只是用来存储数据,并没有任何的性质,所以在任何方面都没有什么优势,今天学习的二叉搜索树是在普通的二叉树的基础上加上了一些性质,使整体的搜索效率大大提升。

在这里插入图片描述

一、二叉搜索树的概念

二叉搜索树可能是一棵空树,也可能是一棵具有以下性质的二叉树:

  1. 如果左子树存在,则左子树所有结点的值都比根节点的值小
  2. 如果右子树存在,则右子树所有结点的值都比根节点的值大
  3. 树是递归创建的,所以二叉搜索树中的每一棵子树都要满足以上性质

二、二叉搜素树的模拟实现(增删查非递归实现)

1. 二叉搜素树的结点

// 二叉搜索树的结点
template <class K>
struct BSTreeNode
{
	// 成员函数

	//成员变量
	BSTreeNode<K>* _left;// 指向左孩子的指针(指针域)
	BSTreeNode<K>* _right;// 指向右孩子的指针(指针域)
	K _key;// 存储数据的地方(数据域)
};

二叉搜索树的结点中需要包含三个内容:

  1. _left:指向左孩子的结点的指针,通过这个指针找到左孩子,如果这个指针为空指针,说明当前这棵树不存在左子树
  2. _right:指向右孩子的结点的指针,通过这个指针找到右孩子,如果这个指针为空指针,说明当前这棵树不存在右子树
  3. _key:数据域,存放结点的数据的,一般也称为是二叉搜索树的关键字,二叉搜索树的性质就是以这个关键字为准的

2. 二叉搜索树的实现

(1). 二叉搜索树的基本结构

// 二叉搜素树的基本结构
template <class K>
class BSTree
{
	// 需要使用结点类型
	typedef BSTreeNode<T> Node;
	
public:
	// 成员函数
	
private:
	// 成员变量
	Node* _root = nullptr;
};

二叉搜索树的底层本质上就是一个根节点,然后通过这个根节点无限去创建其左右子树,在树中需要使用树的结点类型,所以为了方便表示,可以在树中对树的结点类型进行重定义(这个技巧在list的实现中也用到过)。刚开始_root的值默认为nullptr。

(2)构造函数

树不需要实现构造函数,只需要给_root一个缺省值即可。

(3)查找函数

// 查找函数
	bool find(const K& key)
	{
		Node* cur = _root;
		while (cur)
		{
			if (key < cur->_key)
			{
				cur = cur->_key;
			}
			else if(key>cur->_key)
			{
				cur = cur->_right;
			}
			else
			{
				// 找到了
				return true;
			}
		}
		// 找不到
		return false;
	}

上面这个代码中是不需要单独判断树为空树的情况的,因为当树为空树的时候,那么_root = nullptr,此时cur = nullptr,所以循环压根不会进去,直接返回false。

(4)插入函数

// 插入函数
	bool insert(const K& key)
	{
		if (_root == nullptr)
		{
			// 空树
			_root = new Node(key);
			return true;
		}
		// 非空树
		// 找到插入的地方
		Node* cur = _root;// 找插入的位置
		Node* parent = nullptr;// 记录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;
			}
		}
		// 当cur为空的时候,跳出循环,找到插入的位置
		cur = new Node(key);
		if (key < parent->_key)
		{
			// 插在左子树
			parent->_left = cur;
		}
		else
		{
			parent->_right = cur;
		}
		return true;
	}

思路:
主题判断空树的情况,如果是空树,将结点直接插入在根,其他情况:先找到插入的位置,使用一个cur结点指针去标识找插入的位置,parent结点指针去记录cur的父亲结点,所以在cur找到了插入的位置之后,那么parent就是插入位置的父亲,查找的过程中,如果key的值比cur的值小,那么就是插入在左子树,如果key的值比cur的值大,那么就是插入在右子树,如果出现key的值和cur的值是相等的,则说明树中存在该值,不允许插入。最终当cur走到空的时候,则找到插入的位置,此时需要判断cur应该插入在parent的左边还是右边,可以通过插入的值和parent的值进行对比,如果插入的值比parent的值小,则插入在左子树,否则插入在右边。

  • 测试查找和插入函数
    代码:
void test_BSTree1()
{
	BSTree<int> t;
	t.insert(1);
	t.insert(3);
	t.insert(2);
	t.insert(6);
	t.insert(5);
	t.insert(9);

	// 查找
	cout << "1:" << t.find(1) << endl;
	cout << "2:" << t.find(2) << endl;
	cout << "3:" << t.find(3) << endl;
	cout << "4:" << t.find(4) << endl;
	cout << "5:" << t.find(5) << endl;
	cout << "6:" << t.find(6) << endl;

}

运行结果:
在这里插入图片描述

(5) 删除函数

// 删除函数
	bool erase(const K& key)
	{
		Node* cur = _root;
		Node* parent = nullptr;
		while (cur)
		{
			// 找删除的值
			if (key < cur->_key)
			{
				// 左子树找
				parent = cur;
				cur = cur->_left;
			}
			else if (key > cur->_key)
			{
				// 右子树找
				parent = cur;
				cur = cur->_right;
			}
			else
			{
				// 找到了
				// 分类讨论
				// 1. 左孩子空:包含叶子节点
				// 2. 右孩子为空:不包含叶子节点
				// 3. 左右孩子都不为空
				if (cur->_left == nullptr)
				{
					// 左子树为空,右子树不一定为空
					if (cur == _root)
					{
						// cur就是根结点
						_root = cur->_right;
					}
					else
					{
						// cur不是根,cur的父亲结点是parent
						if (cur == parent->_left)
						{
							parent->_left = cur->_right;
						}
						else
						{
							parent->_right = cur->_right;
						}
					}
					delete cur;
				}
				else if (cur->_right == nullptr)
				{
					// 右子树为空,左子树一定不为空
					if (cur == _root)
					{
						// cur是根节点
						_root = cur->_left;
					}
					else
					{
						// cur不是根节点,cur的父亲就是parent
						if (cur == parent->_left)
						{
							parent->_left = cur->_left;
						}
						else
						{
							parent->_right = cur->_left;
						}
					}
					delete cur;
				}
				else
				{
					// 左子树和右子树都不为空
					// 找到删除结点所在树的右子树中的最小值
					Node* minRight = cur->_right;
					Node* minparent = cur;
					while (minRight->_left)
					{
						minparent = minRight;
						minRight = minRight->_left;
					}
					swap(cur->_key, minRight->_key);
					if (minparent->_left == minRight)
					{
						minparent->_left = minRight->_right;
					}
					else
					{
						minparent->_right = minRight->_right;
					}
					delete minRight;
					
				}
				return true;
			}
		}
		// cur走到空,说明树中不存在这个值,所以删除是被==失败
		return false;
	}

思路: 找到删除的值的结点,如果找不到,则删除失败,空树也是属于找不到的一种情况,如果找到了,需要对删除的结点进行分类讨论:

  • 左孩子为空:不存在左子树,右子树是否存在不一定,可能存在也可能不存在,当右子树不存在的时候,此时该结点的左右子树均不存在,属于叶子结点
  • 右孩子为空:此时左孩子一定不为空,因为左孩子为空的情况属于上面的请情况,所以这种情况的结点只存在右子树
  • 左右孩子均不为空:同时存在左右子树的结点,需要采取替换法,然后转化为第一或者第二种情况进行删除。

细节

  1. 在第一种情况和第二种情况中,删除的可能是根节点,当根节点没有左子树的时候,就属于第一种情况,当根节点没有右子树的时候,就属于第二种情况,如果删除的是根节点,该结点左子树为空时,则需要更新根节点到删除结点的右子树,该结点的右子树为空的时候,需要更新根节点到该节点的左子树。
  2. 第三种情况采取替换法,先找到删除的结点的左子树的最大值结点或者右子树的最小值结点,找这两种结点的原因是这个结点和删除的结点交换后可以保证该值比左子树的所有结点值都大,比右子树所有结点值都小。迭代的过程中一定要记录minRight的父亲,最终才能让该父亲去托管minRight的右子树,这个minparent一定是从删除的结点开始,因为minRight可能就是删除结点的右子树的根节点,这种情况就是删除结点的右子树的根节点没有左孩子的,此时循环是不会进去的,这种情况的minRight就是删除结点的右子树的根节点,那么其父亲就是删除的结点,所以在交换删除结点的值和minRight的值之后,问题就转化为删除minRight,此时minRight的左子树一定是空的,所以就算删除情况的第一种情况。

(6)中序遍历

// 中序遍历
	void InOrder()
	{
		// 因为在类外的对象不方便使用根节点,所以需要套一层进行递归
		_InOrder(_root);
		cout << endl;
	}

// 中序遍历的子函数
		void _InOrder(Node* root)
		{
			if (root == nullptr)
			{
				return;
			}
			// 先遍历左子树
			_InOrder(root->_left);
			// 再访问根节点
			cout << root->_key << " ";
			// 最后遍历右子树
			_InOrder(root->_right);

		}

思路:在我们之前学习的二叉树的中序遍历中是直接进行递归的,因为C语言实现的二叉树并不是封装的,也就是对象可以直接访问树中的任何成员,但是C++不同,C++采用类对象进行封装的结构,类外的对象是不能访问类中的私有成员的,所以在类外不能访问到树的根节点,所以我们采取的方法就算套一层进行递归。递归的思路和之前一样:中序遍历就算先递归左子树,再访问根节点的值,再递归右子树。

(7)析构函数

二叉树中的结点都需要析构函数进行释放,所以析构函数需要我们自己实现,编译器形成析构函数无法完成这项工作。析构函数的实现思想同样采用递归的思路,而且采用的是后序递归,就是先将树的左子树递归,再递归右子树,最后将根节点释放。还有代码中的析构函数是public的,DestroyTree函数是私有的,析构函数是要给类外的对象进行使用的

public:
// 析构函数
	~BSTree()
	{
		DestroyTree(_root);
	}

private:
void DestroyTree(Node* root)
	{
		if (root == nullptr)
		{
			// 空树
			return;
		}
		// 通过后序递归的方法将树中的每一个结点释放
		DestroyTree(root->_left);
		DestroyTree(root->_right);
		delete root;
	}

(8)拷贝构造函数

编译器默认生成的拷贝构造函数完成浅拷贝,如果采用拷贝,会导致两棵树中的根节点指针指向同一个根节点,也就是最终的两棵树是同一棵树,但是有两个对象,所以最终两个对象在生命周期到的时候都会调用析构函数清理资源,所以会出现同一棵树被清理两次,从而使程序崩溃。

void test_BSTree4()
{
	int a[] = { 2,3,7,1,5,9 };
	BSTree<int> t;
	for (auto& e : a)
	{
		t.insert(e);
	}
	t.InOrder();
	cout << endl;
	BSTree<int> t1 = t;
	t1.InOrder();
}

运行结果:
在这里插入图片描述

  • 深拷贝
    代码:
// 拷贝构造函数
public:
	BSTree(const BSTree<K>& t)
	{
		_root = copyTree(t._root);
	}

private:
// copy函数
	Node* copyTree(const Node* root)
	{
		if (root == nullptr)
		{
			return nullptr;
		}
		Node* newnode = new Node(root->_key);
		newnode->_left = copyTree(root->_left);
		newnode->_right = copyTree(root->_right);
		return newnode;
	}
// 测试代码:
void test_BSTree4()
{
	int a[] = { 2,3,7,1,5,9 };
	BSTree<int> t;
	for (auto& e : a)
	{
		t.insert(e);
	}
	t.InOrder();
	cout << endl;
	BSTree<int> t1 = t;// 调用拷贝构造函数
	t1.InOrder();
}

运行结果:
在这里插入图片描述

(9)赋值运算符重载(现代写法)

// 赋值运算符重载
	BSTree<K>& operator=(BSTree<K> t)
	{
		swap(_root, t._root);
		return *this;
	}

void test_BSTree5()
{
	int a[] = { 2,3,7,1,5,9 };
	BSTree<int> t;
	for (auto& e : a)
	{
		t.insert(e);
	}
	t.InOrder();
	cout << endl;
	BSTree<int> t1;
	t1 = t;// 调用赋值运算符重载
	t1.InOrder();
}

运行结果:
在这里插入图片描述

三、二叉搜索树的模拟实现(增删查递归实现)

1. 查找函数

public:
	// 递归版本
	bool FindR(const K& key)
	{
		return _FindR(_root,key);
	}
private:

	// 查找函数的子函数(递归版本)
	bool _FindR(Node* root,const K& key)
	{
		if (root == nullptr)
		{
			// 空树
			return false;
		}

		// 非空树
		if (key < root->_key)
		{
			// 左子树找
			return _FindR(root->_left, key);
		}
		else if (key > root->_key)
		{
			// 右子树找
			return _FindR(root->_right, key);
		}
		else
		{
			// 找到了
			return true;
		}
	}
//测试代码:
void test_BSTree6()
{
	// 测试树的非递归查找函数
	int a[] = { 2,3,7,1,5,9 };
	BSTree<int> t;
	for (auto& e : a)
	{
		t.insert(e);
	}
	t.InOrder();

	for (auto& e : a)
	{
		cout << t.FindR(e) << " ";
	}
}

运行结果:
在这里插入图片描述

2. 插入函数

public:
// 插入函数(递归版本)
	bool InsertR(const K& key)
	{
		return _InsertR(_root, key);
	}
private:
	// 插入函数递归版本的子函数
	bool _InsertR(Node*& root, const K& key)
	{
		if (root == nullptr)
		{
			root = new Node(key);

			return true;
		}
		// 查找插入的位置
		if (key < root->_key)
		{
			// 插入在当前树的左子树
			return _InsertR(root->_left, key);
		}
		else if (key > root->_key)
		{
			// 插入在当前树的右子树
			return _InsertR(root->_right, key);
		}
		else
		{
			// 该值在树中已经存在,插入失败
			return false;
		}
	}
// 测试代码
void test_BSTree7()
{
	// 测试插入函数的递归版本
	int a[] = { 2,3,7,1,5,9 };
	BSTree<int> t;
	for (auto& e : a)
	{
		t.InsertR(e);
	}
	t.InOrder();

}

运行结果:
在这里插入图片描述

3. 删除函数

public:
// 删除函数的递归版本
	bool EraseR(const K& key)
	{
		return _EraseR(_root, key);
	}
private:

	// 删除函数递归版本的子函数
	bool _EraseR(Node*& root, const K& key)
	{
		if (root == nullptr)
		{
			// 空树
			return false;
		}
		// 非空树,找删除的结点
		if (key < root->_key)
		{
			// 到左子树找
			return _EraseR(root->_left, key);
		}
		else if (key > root->_key)
		{
			// 到右子树找
			return _EraseR(root->_right, key);
		}
		else
		{
			// 找到了
			// 分类讨论
			Node* del = root;
			if (root->_left == nullptr)
			{
				// 左子树为空
				root = root->_right;
			}
			else if (root->_right == nullptr)
			{
				// 右子树为空
				root = root->_left;
			}
			else
			{
				// 左右子树均不为空
				// 替换法
				Node* minRight = root->_right;
				while (minRight->_left)
				{
					minRight = minRight->_left;
				}
				swap(minRight->_key, root->_key);
				// 转化为第一种情况
				return _EraseR(root->_right, key);
			}
			delete del;
			return true;
		}
	}
// 测试代码:

void test_BSTree8()
{
	// 测试插入函数的递归版本
	int a[] = { 2,3,7,1,5,9 };
	BSTree<int> t;
	for (auto& e : a)
	{
		t.InsertR(e);
	}
	t.InOrder();
	t.EraseR(7);
	t.InOrder();
	t.EraseR(1);
	t.InOrder();

	for (auto& e : a)
	{
		t.EraseR(e);
	}
	t.InOrder();
}

运行结果:
在这里插入图片描述

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

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

相关文章

1225057-68-0,Alkyne PEG4 TAMRA-5,四甲基罗丹明-四聚乙二醇-炔基TAMRA红色荧光染料连接剂

中英文别名&#xff1a;CAS号&#xff1a;1225057-68-0 | 英文名&#xff1a;5-TAMRA-PEG4-Alkyne |中文名&#xff1a;5-四甲基罗丹明-四聚乙二醇-炔基物理参数&#xff1a;CASNumber&#xff1a;1225057-68-0Molecular formula&#xff1a;C36H41N3O8Molecular weight&#x…

P16 激活函数与Loss 的梯度

参考&#xff1a;https://www.ngui.cc/el/507608.html?actiononClick这里面简单回顾一下PyTorch 里面的两个常用的梯度自动计算的APIautoGrad 和 Backward, 最后结合 softmax 简单介绍一下一下应用场景。目录&#xff1a;1 autoGrad2 Backward3 softmax一 autoGrad输入 x输出损…

buu [UTCTF2020]basic-crypto 1

题目描述&#xff1a; 01010101 01101000 00101101 01101111 01101000 00101100 00100000 01101100 01101111 01101111 01101011 01110011 00100000 01101100 01101001 01101011 01100101 00100000 01110111 01100101 00100000 01101000 01100001 01110110 01100101 00100000 0…

【Kubernetes】【七】命令式对象配置和声明式对象配置

命令式对象配置 命令式对象配置就是使用命令配合配置文件一起来操作kubernetes资源。 1&#xff09; 创建一个nginxpod.yaml&#xff0c;内容如下&#xff1a; apiVersion: v1 kind: Namespace metadata:name: dev---apiVersion: v1 kind: Pod metadata:name: nginxpodnames…

调用Windows安全中心实现登录验证

文章目录运行效果用到的运行库代码实现使用日志Win10 Flat风格XP风格总结运行效果 输入用户名和密码点击确定后获取到的信息&#xff1a; 用到的运行库 NuGet搜索安装即可 Kang.ExtendMethodKang.ExtendMethod.Log https://gitee.com/haozekang/kang Vanara.PInvoke https:…

安全算法 - 国密算法

国密即国家密码局认定的国产密码算法。主要有SM1&#xff0c;SM2&#xff0c;SM3&#xff0c;SM4&#xff0c;SM7, SM9。国密算法分类国家标准官方网站如下&#xff1a;http://openstd.samr.gov.cn/bzgk/gb/SM1 为对称加密。其加密强度与AES相当。该算法不公开&#xff0c;调用…

Nacos——配置管理基础应用

目录 一、快速入门 1.1 发布配置 1.2 nacos client远程获取配置 1.2.1 导入坐标 1.2.2 程序代码 二、Nacos配置管理基础应用 2.1 Nacos配置管理模型 2.1.2 配置集(Data Id) 2.1.3 配置项 2.1.4 配置分组 (Group) 2.1.5 命名空间(Namespace) 2.1.6 最佳实践&#xff0…

Worok:专门针对亚洲实体的网络间谍组织

ESET 的研究人员发现了一个全新的攻击组织 Worok&#xff0c;该组织自动 2020 年就一直处于活跃状态。Worok 使用的工具集包括一个 C 编写的加载程序 CLRLoad、一个 PowerShell 编写的后门 PowHeartBeat 和一个 C# 编写的加载程序 PNGLoad&#xff0c;攻击者使用隐写术来提取隐…

PPT与Inkscape自定义色板

PPT与Inkscape自定义色板简述 本文主要分享了PPT与Inkscape中自定义色板功能&#xff0c;以满足个性化配色需求。此外&#xff0c;文末分享了常见的配色网站和图片网站&#xff0c;前者可以满足配色需求&#xff0c;后者可以满足配图需求。 PPT自定义色板 在常见的办公三件套中…

pycharm远程连接服务器,并单步调试服务器上的代码

每天都有不同的朋友来Push我 那如果比较健忘的话&#xff0c;为啥不问一下chatGPT呢 问题的缘由在我想在本地单步调试代码。。。 我的代码完全在云端服务器的&#xff0c;还有数据集都是&#xff0c;但实际上本地代码可以通过pycharm给他传上去。 但是在后面配置的时候需要两…

高密度部署,基于动态库的尝试,rust动态调库

目录前言faas特点方案思考实践制作动态库调用动态库尾语前言 最近在搞faas平台&#xff0c;也试了各大云厂商的产品&#xff0c;效果都不是很理想。和我心目中的faas想去甚远。  和小伙伴们吹完牛逼&#xff0c;心有所感&#xff0c;写下这篇文章&#xff0c;时间跨度较长&…

「AI人工智能」Node.js如何接入OpenAI开发

文章目录前言一、创建OpenAI账号二、安装axios 库三、导入 axios 库四、调用 OpenAI API五、测试 OpenAI API前言 本文主要介绍如何将 Node.js 应用程序与 OpenAI 集成&#xff0c;可以使用 OpenAI API。 一、创建OpenAI账号 创建一个 OpenAI 帐户并注册 API 密钥。你可以在 …

Linux系统一键检测和加固脚本

主要是为了Linux系统的安全&#xff0c;通过脚本对Linux系统进行一键检测和一键加固。 Check_Script #包含2个文件 CentOS_Check_Script.sh README.txt 操作说明 #执行CentOS-Check_Script.sh脚本文件进行检查,命令格式如下 sudo sh CentOS_Check_Script.sh | tee check_da…

Spring Boot 2.x系列【28】应用篇之JAVA执行服务器操作命令

有道无术&#xff0c;术尚可求&#xff0c;有术无道&#xff0c;止于术。 本系列Spring Boot版本2.7.0 文章目录前言本地服务器远程服务器前言 在某些实际开发场景中&#xff0c;我们需要调用JAVA程序去执行一些服务器操作命令&#xff0c;比如&#xff1a; 获取服务器的CPU…

牛逼的不停服定位线上问题-arthas

​ Hello&#xff0c;大家好我是你们可爱的小花。 前言 你是不是为了生产环境问题&#xff0c;无法定位、无法中断、无法解决 项目无故异常&#xff0c;日志无报错、报错不够明确 测试环境无法复现、生产环境问题偶发 但重启项目后问题消失&#xff0c;无法给领导一个答复而苦…

ChatGPT冷观察:没有大模型的土壤,开不出ChatBot的花

文|智能相对论作者|叶远风谁在跟风&#xff0c;谁又有真本事能做出中国版的对标产品来&#xff1f;这恐怕是ChatGPT这股热潮以来&#xff0c;关心中国AI发展的业界人士最想问的问题。或者说&#xff0c;在中国人工智能不落后于全世界的当下&#xff0c;业界也在普遍渴望一个真正…

AWS Directory Service

Hello大家好&#xff0c;我们接下来讨论AWS Directory Service&#xff0c;AWS目录服务的内容。 什么是微软活动目录&#xff08;AD&#xff09; 在认证考试中有很多的考点是关于微软AD部分的&#xff0c;也就是微软活动目录以及AWS活动目录服务—AWS Directory Service的内容…

PowerShell Install Mysql 8

MySQL介绍 MySQL 是最流行的关系型数据库管理系统,在 WEB 应用方面 MySQL 是最好的 RDBMS(Relational Database Management System:关系数据库管理系统)应用软件之一。 mysql download Mysql Serverdownload创建一键安装Mysql 8自动化脚本 Expand-Archive #解压文件Start-P…

【我有一个梦想:帮你实现你的梦想】

很多人都曾有过这样的经历&#xff0c;某天突然对某个东西产生了兴趣&#xff0c;于是兴致勃勃的开始在网上各种搜&#xff0c;各种查&#xff0c;去各大论坛到处看到处问&#xff0c;在网上搜集很多免费的电子书&#xff0c;看了很多视频&#xff0c;最后忙活了一阵子&#xf…

若依框架如何新增自定义主题风格

若依框架新增主题风格1.实现结果2.实现步骤2.1Settings目录下2.2 variables.scss2.3 sidebar.scss2.4 Logo.vue2.5 Siderbar目录下的index.vue1.实现结果 2.实现步骤 需要改动的文件目录&#xff1a; 2.1Settings目录下 <div class"setting-drawer-block-checbox-it…