由红黑树到map/set

news2024/11/25 23:53:22

文章目录

  • 一.map/set 的封装思路
    • 1.封装思路
    • 2.红黑树节点调整
    • 3.map 和 set 的定义
    • 4.仿函数 KeyOfValue
    • 5.map/set 的插入
  • 二.map/set 迭代器实现
    • 1.迭代器的定义
    • 2.解引用运算符重载
    • 3.成员访问运算符重载
    • 4.(不)等于运算符重载
    • 5.begin() 与 end()
    • 6.++ 运算符重载
    • 7.-- 运算符重载
    • 8.[ ]下标访问运算符重载
  • 三.源码
    • 1.RBTree
    • 2.map.h
    • 3.set.h

预备知识:

一.map/set 的封装思路

在实现了红黑树的部分功能后,我们可以便可以将红黑树作为底层结构来封装map set,其中mapK-Value 模型 ,而 set Key 模型。

我们接下来将使用模板、仿函数和一棵红黑树实现 mapset

1.封装思路

因为 map 存储的是pair ,而 set 存储的是 Key ,所以其解决的根本方向就是:

  • 如果是 map,红黑树中就按照 pairK进行比较,从而插入;

  • 如果是 set,红黑树中就按照 Key 值进行比较,从而插入。

map / set 主动传出待比较的数据,红黑树只用根据数据间关系进行插入即可,不用在乎待比较的数据是何种结构。

2.红黑树节点调整

上文我们实现的红黑树是按照键值对的方式进行存储的,而接下来我们要同时封装 map/set,故不能直接定死存储的结构,所以我们在此进行修改。
将原来的kv 模型改为data模型,data 即是比较的数据内容。

在这里插入图片描述

注意,将 Kv模型改为 data后,插入与查找中比较的代码都要进行更新,稍后会讲解。

3.map 和 set 的定义

mapset 底层都使用的红黑树,所以我们 map/set 的功能就是调用红黑树的成员函数即可。

template<class K, class V>
class Map
{
private:
	RBTree<K, pair<K, V>> _t;
};
 
template<class K>
class Set
{
private:
	RBTree<K,K> _t;
};

因为Map有两个模板参数,而 Set 只有一个模板参数。所以当我们使用的一个红黑树实现时,要进行匹配处理。即使 Set 是一个模板参数,在调用红黑树时也要传入两个模板参数。因为第一个模板参数是匹配 Map 满足红黑树的两个模板参数,而第二个模板参数是为了让底层红黑树拿到比较的数据。

为什么Map除了传入pair外,第一个参数直接传入 K,为什么不能省略?

因为Find的存在,mapFind函数是直接按pair中的 K 进行查找的,所以要额外设置该参数。

4.仿函数 KeyOfValue

接下来我们就要将数据取出供红黑树比较了,如果是 map,就按pair中的 K去比较,如果是 set,就按Key比较。

为此我们可以在 mapset 内部定义一个仿函数将其数据取出。

template<class K, class V>
class Map
{
    //Map-keyofvalue 仿函数
	struct MapKeyOfvalue
	{
		const K& operator()(const std::pair<K, V>& kv)
		{
			return kv.first;
		}
	};
private:
	RBTree<K, pair<K, V>> _t;
};
 
template<class K>
class Set
{
    //Set-keyofvalue 仿函数
    struct SetKeyOfvalue
	{
		const K& operator()(const K& key)
		{
			return key;
		}
	};
private:
	RBTree<K,K> _t;
};

然后我们将其仿函数也作为模板,传入红黑树中,对应的,红黑树要添加一个模板参数来接收该仿函数。

改动代码如下:

在这里插入图片描述
改动这些之后,我们便要将红黑树中比较数据大小的地方进行修改

用仿函数将数据取出,然后进行比较:

//根据模板参数创建仿函数
KeyOfvalue kovalue;
if (!_root)
{
	_root = new Node(data);
	_root->_col = BLACK;
	return true;
}
Node* parent = nullptr;
Node* cur = _root;
while (cur)
{
    //比较处————进行改动
	if (kovalue(cur->_data) > kovalue(data))
	{
		parent = cur;
		cur = cur->_left;
	}
    //比较处————进行改动
	else if (kovalue(cur->_data) < kovalue(data))
	{
		parent = cur;
		cur = cur->_right;
	}
	else
	{
		return false;
	}
}
//创建新节点,使用data进行构造
cur = new Node(data);
//比较处————进行改动
if (kovalue(parent->_data) > kovalue(data))
{
	parent->_left = cur;
}
else
{
	parent->_right = cur;
}
cur->_parent = parent;

这样,红黑树便可以适配 map/set 的插入了。

5.map/set 的插入

接下来map/set 的插入直接套用红黑树的即可。

代码如下:

//map的插入,插入pair
bool insert(const pair<K, V>& kv)
{
	return _t.Insert(kv);
}
 
//set的插入,插入key
bool insert(const K& key)
{
	return _t.Insert(key);
}

二.map/set 迭代器实现

1.迭代器的定义

//		节点数据	引用/const引用  指针/const指针
template <class T,class Ref,class Ptr>
struct __RBTreeIterator
{
	typedef RBTreeNode<T> Node;
	typedef __RBTreeIterator<T, Ref, Ptr> self;
	Node* _node;
public:
	__RBTreeIterator(Node* node)
		:_node(node)
	{}
}

首先,我们要明确,其实 map/set 只是一层套壳,其中的功能都是由红黑树实现后,再封装到map/set中供我们使用,迭代器也不例外。

2.解引用运算符重载

解引用即返回该节点的存储的数据,主要用于 set 中,返回该数据的引用。

Ref operator*()
{
	return _node->_data;
}

3.成员访问运算符重载

成员访问操作符即返回该节点的地址,主要用于 map 中,方便访问 pair 中的first以及second

Ptr operator->()
{
	return &(_node->_data);
}

4.(不)等于运算符重载

bool operator==(const self& s) 
{
	return _node == s._node;
}
 
 
bool operator!=(const self& s) 
{
	return _node != s._node;
}

5.begin() 与 end()

迭代器常用成员函数begin()end(),其中begin()对应红黑树的最左节点,end()对应最后一个节点的下一个节点,即nullptr(为了简化,并未设置哨兵节点实现将其完美实现)

iterator begin()
{
	Node* left = _root;
	while (left && left->_left)
	{
		left = left->_left;
	}
	return iterator(left);
}
 
iterator end()
{
	return iterator(nullptr);
}

如果 map/set 中想使用红黑树中的迭代器,我们需要在 map/set 中进行声明。

声明如下:

如果想取一个类模板中的一个类型,要使用 typedname 进行声明。
告诉编译器这是一个类型,并不是一个静态变量

//如果想取一个类模板中的一个类型,要使用 typedname 进行声明。
//告诉编译器这是一个类型,并不是一个静态变量
typedef typename RBTree<K, pair<K, V>, MapKeyOfvalue>::iterator iterator;

6.++ 运算符重载

首先我们需要明确,迭代器++是让当前迭代器指向红黑树中序遍历的下一个节点。

以下图的35节点为例。

  • 当迭代器指向 35 时,进行 ++,指向右子树最左节点,即 40。
  • 当迭代器指向 40 时,进行 ++,右子树为空,指向父节点,即 45。
  • 当迭代器指向 45 时,进行 ++,指向右子树最左节点,即 48。
  • 当迭代器指向 48 时,进行 ++,指向未遍历的父节点,即 50。

在这里插入图片描述
分析上面的情况,发现迭代器 ++ 始终围绕着右子树是否存在进行。

现在我们将其抽象化,分析其规律:

  1. 右子树不为空,进行 ++ 则是指向右子树中序的第一个(最左节点)。
  2. 右子树为空,++ 找孩子不是父亲右节点的祖先。
self& operator++()
{
	//如果右子树存在
	if (_node->_right)
	{
		Node* left = _node->_right;
		//则寻找右子树的最左节点
		while (left->_left)
		{
			left = left->_left;
		}
		_node = left;
	}
	//如果右子树不存在
	else
	{
        //找孩子不是父亲右节点的节点
		Node* parent = _node->_parent;
		Node* cur = _node;=
		while (cur == parent->_right)
		{
			cur = cur->_parent;
			parent = parent->_parent;
			//防止最后一个节点寻找祖先导致程序崩溃
			if (parent == nullptr)
			{
				break;
			}
		}
		_node = parent;
	}
	return *this;
}

需要注意,当 ++ 到最后一个节点的时候。有可能在寻找非父亲右节点的祖先时,父节点一路走到 nullptr 的情况,如图:

在这里插入图片描述
所以在每次 parent 更新时都进行一次判断,即可。

7.-- 运算符重载

有了前面++的模拟实现,实现 --就是反着遍历即可。

  1. 左子树不为空,进行 -- 则是指向左子树中序的最后一个(最右节点)。
  2. 左子树为空,-- 找孩子不是父亲左节点的祖先。
self& operator--()
{
	//如果左子树存在
	if (_node->left)
	{
		//找左子树的最右节点
		Node* right = _node->_left;
		while (right->_right)
		{
			right = right->_right;
		}
		_node = rihgt;
	}
	//如果左子树不存在
	else
	{
		//找孩子不是父亲左节点的节点
		Node* parent = _node->parent;
		Node* cur = _node;
		while (parent->_left == cur)
		{
			cur = cur->_parent;
			parent = parent->_parent;
			if (parent == nullptr)
			{
				break;
			}
		}
		_node = parent;
	}
	return *this;
}

8.[ ]下标访问运算符重载

我们来看 map[]下标访问操作符,其中 []返回的是mapped_type(pair) 类型。

在这里插入图片描述

再看 set 中的insert也是返回 pair,虽然很反常,但是官方库中确实是这样书写的。

在这里插入图片描述

因为 set 没有[]运算符重载,所以 set 中不必提供该函数,只用在 map 中提供即可。

首先,我们向 mapinsert 数据 pairpair的第一个参数为用户传入的 key 值,第二个参数则是用户声明的第二个模板参数的默认构造函数(如果是 int,则调用 int的构造函数,如果是string,则默认构造 string)。

pair<iterator, bool> result = insert(make_pair(key, V()));

然后我们返回迭代器指向的 pair 数据中的second

//result.first取出迭代器,使用->运算符重载取出data地址,访问second并返回
return result.first->second;
V& operator[](const K& key)
{
	pair<iterator, bool> result = insert(make_pair(key, V()));
	//如果存在,则插入失败
	//如果不存在,则插入数据
	//无论是否存在,都返回 second;
	return result.first->second;
}

三.源码

1.RBTree

enum Colour
{
	RED,
	BLACK,
};

template<class T>
struct RBTreeNode
{
	RBTreeNode<T>* _left;
	RBTreeNode<T>* _right;
	RBTreeNode<T>* _parent;
	T _data;
	Colour _col;

	RBTreeNode(const T& data)
		:_left(nullptr)
		, _right(nullptr)
		, _parent(nullptr)
		, _data(data)
		, _col(RED)
	{}
};

template<class T, class Ref, class Ptr>
struct __RBTreeIterator
{
	typedef RBTreeNode<T> Node;
	typedef __RBTreeIterator<T, Ref, Ptr> Self;
	Node* _node;

	__RBTreeIterator(Node* node)
		:_node(node)
	{}

	// 1、typedef __RBTreeIterator<T, T&, T*> itertaor;  拷贝构造
	// 2、 typedef __RBTreeIterator<T, const T&, const T*> const_itertaor;
	//  支持普通迭代器构造const迭代器的构造函数

	__RBTreeIterator(const __RBTreeIterator<T, T&, T*>& it)
		:_node(it._node)
	{}

	Ref operator*()
	{
		return _node->_data;
	}

	Ptr operator->()
	{
		return &_node->_data;
	}

	bool operator!=(const Self& s)
	{
		return _node != s._node;
	}

	Self& operator++()
	{
		if (_node->_right)
		{
			// 1、右不为空,下一个就是右子树的最左节点
			Node* subLeft = _node->_right;
			while (subLeft->_left)
			{
				subLeft = subLeft->_left;
			}

			_node = subLeft;
		}
		else
		{
			// 2、右为空,沿着到根的路径,找孩子是父亲左的那个祖先
			Node* cur = _node;
			Node* parent = cur->_parent;
			while (parent && cur == parent->_right)
			{
				cur = parent;
				parent = parent->_parent;
			}

			_node = parent;
		}

		return *this;
	}

	Self& operator--()
	{
		if (_node->_left)
		{
			// 1、左不为空,找左子树最右节点
			Node* subRight = _node->_left;
			while (subRight->_right)
			{
				subRight = subRight->_right;
			}

			_node = subRight;
		}
		else
		{
			// 2、左为空,孩子是父亲的右的那个祖先
			Node* cur = _node;
			Node* parent = cur->_parent;
			while (parent && cur == parent->_left)
			{
				cur = parent;
				parent = parent->_parent;
			}

			_node = parent;
		}

		return *this;
	}
};

// 仿函数
template<class K, class T, class KeyOfT>
class RBTree
{
	typedef RBTreeNode<T> Node;
public:
	~RBTree()
	{
		_Destroy(_root);
		_root = nullptr;
	}
public:
	typedef __RBTreeIterator<T, T&, T*> itertaor;
	typedef __RBTreeIterator<T, const T&, const T*> const_itertaor;

	itertaor begin()
	{
		Node* cur = _root;
		while (cur && cur->_left)
		{
			cur = cur->_left;
		}

		return itertaor(cur);
	}

	itertaor end()
	{
		return itertaor(nullptr);
	}

	const_itertaor begin() const
	{
		Node* cur = _root;
		while (cur && cur->_left)
		{
			cur = cur->_left;
		}

		return const_itertaor(cur);
	}

	const_itertaor end() const
	{
		return const_itertaor(nullptr);
	}

	Node* Find(const K& key)
	{
		Node* cur = _root;
		KeyOfT kot;
		while (cur)
		{
			if (kot(cur->_data) < key)
			{
				cur = cur->_right;
			}
			else if (kot(cur->_data) > key)
			{
				cur = cur->_left;
			}
			else
			{
				return cur;
			}
		}

		return nullptr;
	}

	pair<itertaor, bool> Insert(const T& data)
	{
		if (_root == nullptr)
		{
			_root = new Node(data);
			_root->_col = BLACK;

			return make_pair(itertaor(_root), true);
		}

		KeyOfT kot;
		Node* parent = nullptr;
		Node* cur = _root;
		while (cur)
		{
			if (kot(cur->_data) < kot(data))
			{
				parent = cur;
				cur = cur->_right;
			}
			else if (kot(cur->_data) > kot(data))
			{
				parent = cur;
				cur = cur->_left;
			}
			else
			{
				return make_pair(itertaor(cur), false);
			}
		}

		cur = new Node(data);
		Node* newnode = cur;
		if (kot(parent->_data) > kot(data))
		{
			parent->_left = cur;
		}
		else
		{
			parent->_right = cur;
		}
		cur->_parent = parent;

		while (parent && parent->_col == RED)
		{
			Node* grandfather = parent->_parent;
			if (grandfather->_left == parent)
			{
				Node* uncle = grandfather->_right;
				// 情况1:u存在且为红,变色处理,并继续往上处理
				if (uncle && uncle->_col == RED)
				{
					parent->_col = BLACK;
					uncle->_col = BLACK;
					grandfather->_col = RED;

					// 继续往上调整
					cur = grandfather;
					parent = cur->_parent;
				}
				else // 情况2+3:u不存在/u存在且为黑,旋转+变色
				{
					//     g
					//   p   u
					// c 
					if (cur == parent->_left)
					{
						RotateR(grandfather);
						parent->_col = BLACK;
						grandfather->_col = RED;
					}
					else
					{
						//     g
						//   p   u
						//     c
						RotateL(parent);
						RotateR(grandfather);
						cur->_col = BLACK;
						//parent->_col = RED;
						grandfather->_col = RED;
					}

					break;
				}
			}
			else // (grandfather->_right == parent)
			{
				//    g
				//  u   p
				//        c
				Node* uncle = grandfather->_left;
				// 情况1:u存在且为红,变色处理,并继续往上处理
				if (uncle && uncle->_col == RED)
				{
					parent->_col = BLACK;
					uncle->_col = BLACK;
					grandfather->_col = RED;

					// 继续往上调整
					cur = grandfather;
					parent = cur->_parent;
				}
				else // 情况2+3:u不存在/u存在且为黑,旋转+变色
				{
					//    g
					//  u   p
					//        c
					if (cur == parent->_right)
					{
						RotateL(grandfather);
						grandfather->_col = RED;
						parent->_col = BLACK;
					}
					else
					{
						//    g
						//  u   p
						//    c
						RotateR(parent);
						RotateL(grandfather);
						cur->_col = BLACK;
						grandfather->_col = RED;
					}

					break;
				}
			}
		}

		_root->_col = BLACK;

		return make_pair(itertaor(newnode), true);;
	}

	bool IsBalance()
	{
		if (_root && _root->_col == RED)
		{
			cout << "根节点颜色是红色" << endl;
			return false;
		}

		int benchmark = 0;
		Node* cur = _root;
		while (cur)
		{
			if (cur->_col == BLACK)
				++benchmark;
			cur = cur->_left;
		}

		// 连续红色节点
		return _Check(_root, 0, benchmark);
	}

	int Height()
	{
		return _Height(_root);
	}

private:
	void _Destroy(Node* root)
	{
		if (root == nullptr)
		{
			return;
		}

		_Destroy(root->_left);
		_Destroy(root->_right);
		delete root;
	}

	int _Height(Node* root)
	{
		if (root == NULL)
			return 0;

		int leftH = _Height(root->_left);
		int rightH = _Height(root->_right);

		return leftH > rightH ? leftH + 1 : rightH + 1;
	}

	bool _Check(Node* root, int blackNum, int benchmark)
	{
		if (root == nullptr)
		{
			if (benchmark != blackNum)
			{
				cout << "某条路径黑色节点的数量不相等" << endl;
				return false;
			}

			return true;
		}

		if (root->_col == BLACK)
		{
			++blackNum;
		}

		if (root->_col == RED 
			&& root->_parent 
			&& root->_parent->_col == RED)
		{
			cout << "存在连续的红色节点" << endl;
			return false;
		}

		return _Check(root->_left, blackNum, benchmark)
			&& _Check(root->_right, blackNum, benchmark);
	}

	void RotateL(Node* parent)
	{
		Node* subR = parent->_right;
		Node* subRL = subR->_left;

		parent->_right = subRL;
		if (subRL)
			subRL->_parent = parent;

		Node* ppnode = parent->_parent;

		subR->_left = parent;
		parent->_parent = subR;

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

			subR->_parent = ppnode;
		}
	}

	void RotateR(Node* parent)
	{
		Node* subL = parent->_left;
		Node* subLR = subL->_right;

		parent->_left = subLR;
		if (subLR)
			subLR->_parent = parent;

		Node* ppnode = parent->_parent;

		subL->_right = parent;
		parent->_parent = subL;

		if (parent == _root)
		{
			_root = subL;
			_root->_parent = nullptr;
		}
		else
		{
			if (ppnode->_left == parent)
			{
				ppnode->_left = subL;
			}
			else
			{
				ppnode->_right = subL;
			}
			subL->_parent = ppnode;
		}
	}

private:
	Node* _root = nullptr;
};

2.map.h

template<class K, class V>
class map
{
	struct MapKeyOfT
	{
		const K& operator()(const pair<const K, V>& kv)
		{
			return kv.first;
		}
	};
public:
	typedef typename RBTree<K, pair<const K, V>, MapKeyOfT>::itertaor iterator;

	iterator begin()
	{
		return _t.begin();
	}

	iterator end()
	{
		return _t.end();
	}

	V& operator[](const K& key)
	{
		pair<iterator, bool> ret = _t.Insert(make_pair(key, V()));
		return ret.first->second;
	}

	pair<iterator, bool> insert(const pair<const K, V>& kv)
	{
		return _t.Insert(kv);
	}
private:
	RBTree<K, pair<const K, V>, MapKeyOfT> _t;
};

void test_map1()
{
	map<string, string> dict;
	dict.insert(make_pair("sort", ""));
	dict.insert(make_pair("string", "ַ"));
	dict.insert(make_pair("count", ""));
	dict.insert(make_pair("string", "(ַ)")); // ʧ

	map<string, string>::iterator it = dict.begin();
	while (it != dict.end())
	{
		cout << it->first << ":" << it->second << endl;
		/*it->first = "1111";
		it->second = "111";*/

		++it;
	}
	cout << endl;

	for (auto& kv : dict)
	{
		cout << kv.first << ":" << kv.second << endl;
	}
	cout << endl;
};

3.set.h

template<class K>
class set
{
	struct SetKeyOfT
	{
		const K& operator()(const K& key)
		{
			return key;
		}
	};
public:
	typedef typename RBTree<K, K, SetKeyOfT>::const_itertaor iterator;
	typedef typename RBTree<K, K, SetKeyOfT>::const_itertaor const_iterator;


	iterator begin()
	{
		return _t.begin();
	}

	iterator end()
	{
		return _t.end();
	}

	pair<iterator, bool> insert(const K& key)
	{
		return _t.Insert(key);
	}
private:
	RBTree<K, K, SetKeyOfT> _t;
};

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

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

相关文章

公文,需要明确分工和流程,以确保公文的准时完成和质量保障

对于需要多人协作的公文&#xff0c;需要明确分工和流程&#xff0c;以确保公文的准时完成和质量保障。 具体来说&#xff0c;可以采取以下几个方面的工作&#xff1a; 1.明确分工&#xff1a;在多人协作的公文中&#xff0c;需要明确各自的职责和任务&#xff0c;合理分配工作…

Codeforces Round 881 (Div. 3)F1题解

文章目录 [Omsk Metro (simple version)](https://codeforces.com/contest/1843/problem/F1)问题分析1.分析如何知道根节点到某个结点的区间内是否存在一个子段和为k2.方法1使用树形DP来动态维护每个节点到根节点的最大子段和和最小子段和代码 Omsk Metro (simple version) 问题…

专业商城财务一体化-线上商城+进销存管理软件,批发零售全行业免费更新

订货流程繁琐&#xff1f;订单处理效率低&#xff1f;小程序商城与进销存系统不打通&#xff1f;数据需要手动输入同步&#xff1f;财务与的结算对账需要大量手工处理&#xff1f;零售批发从业者&#xff0c;如何你也有以上烦恼&#xff0c;可以看看进销存小程序订货商城&#…

软件设计师(六)结构化开发方法

结构化方法由结构化分析、结构化设计、结构化程序设计构成&#xff0c;它是一种面向数据流的开发方法。 分类说明结构化分析&#xff08;SA&#xff09;根据分解与抽象的原则&#xff0c;按照系统中数据处理的流程&#xff0c;用数据流图来建立系统的功能模型&#xff0c;从而…

Java错误小全

文章目录 NullPointer异常&#xff1a; 错误描述&#xff1a;当试图访问一个空引用对象的属性或调用空引用对象的方法时&#xff0c;会抛出NullPointer异常。 复现示例&#xff1a; String str null; System.out.println(str.length());解决方案&#xff1a;在使用对象之前&am…

JVM之两种垃圾判断方式

文章目录 一、所谓的垃圾二、引用计数器法三、可达性分析法GC Roots工作原理三色标记标记算法并发标记 一、所谓的垃圾 垃圾&#xff1a;如果一个或多个对象没有任何的引用指向它了&#xff0c;那么这个对象现在就是垃圾 在堆里存放着几乎所有的 Java 对象实例&#xff0c;在…

linux环境下pip下载包的基础使用

内网环境如何使用pip安装包 1、能连接到外网的电脑先下载好对应的whl文件 使用命令&#xff1a; pip3 download pymysql -i http://mirrors.aliyun.com/pypi/simple/ --trusted-host mirrors.aliyun.com下载好的文件&#xff1a; 将whl文件上传到内网机器中进行安装 pip3 in…

SpringCloudAlibaba之Sentinel(一)流控篇

前言&#xff1a; 为什么使用Sentinel&#xff0c;这是一个高可用组件&#xff0c;为了使我们的微服务高可用而生 我们的服务会因为什么被打垮&#xff1f; 一&#xff0c;流量激增 缓存未预热&#xff0c;线程池被占满 &#xff0c;无法响应 二&#xff0c;被其他服务拖…

React Native元素旋转一定的角度

mMeArrowIcon: {fontSize: 30, color: #999, transform: [{rotate: 180deg}]},<Icon name"arrow" style{styles.mMeArrowIcon}></Icon>参考链接&#xff1a; https://reactnative.cn/docs/transforms https://chat.xutongbao.top/

uniapp自定义头部导航栏

有时我们需要一些特殊的头部导航栏页面&#xff0c;取消传统的导航栏&#xff0c;来增加页面的美观度。 下面我就教大家如何配置&#xff1a; 一、效果图 二、实现 首先在uniapp中打开pages.json配置文件&#xff0c;在单个路由配置style里面设置导航栏样式​​​​​​nav…

《HeadFirst设计模式(第二版)》第四章代码——工厂模式

代码文件目录结构&#xff1a; Cheese: 原料ingredient类中只以Cheese为例&#xff0c;不重复展示&#xff1a; package Chapter4_FactoryPattern.abstractFactoryPattern.Ingredient;/*** Author 竹心* Date 2023/8/4**/public abstract class Cheese {String name;String g…

国货香水,真“香”吗?

【潮汐商业评论/原创】 Judy在选择香水这块还属于入门级选手&#xff0c;“我只买过一次YSL的自由之水&#xff0c;其他都是生日时朋友送给我的。如果真让我选&#xff0c;说实话我还真不知道买啥。”Judy偷笑着说。 在电影《香水》中&#xff0c;有一句台词说到&#xff0c;…

走进人工智能|自主无人系统 从概念到现实的飞跃

前言&#xff1a; 自主无人系统是具备自主感知、决策和执行能力的智能系统&#xff0c;无需人类干预即可完成任务的技术体系。 文章目录 序言AUS的现有应用从概念到现实的飞跃技术发展历程 目前形式领跑人困难和挑战 总结 自主无人系统&#xff08;Autonomous Unmanned Systems…

Vue2 第十九节 Vuex(二)

1.Vuex API 补充内容 2.getters 配置项 3.四个Map方法的使用 4.多组件共享数据 5.Vux模块化和命名空间 一.Vuex API补充内容 ① 在actions中&#xff0c;如果一个函数处理不完&#xff0c;可以继续调dispatch处理 ② 开发者工具是跟mutations中的数据进行交互的&#xff…

About Multiple regression

ps:this article is not very strict,just some ml and mathematic basic knowledge.My english is poor too.So If this passage make you confuse and uncomfortable.Please give me a feedback in the comment :-D Prior to this(在此之前), we learned the concept of sin…

linuxARM裸机学习笔记(2)----汇编LED灯实验

MX6ULL 的 IO IO的复用功能 这里的只使用了低五位&#xff0c;用来配置io口&#xff0c;其中bit0~bit3(MUX_MODE)就是设置 GPIO1_IO00 的复用功能的&#xff0c;GPIO1_IO00 一共可以复用为 9种功能 IO&#xff0c;分别对应 ALT0~ALT8。每种对应了不同的功能 io的属性配置 HY…

优思学院|精益生产如何真正落地?你要掌握这4个P!

怎样才能让精益生产的方法落地&#xff1f;这是一个经常听到的问题&#xff0c;但对于这个问题很多人有不同的想法和答法&#xff0c;因为大家对“落地”一词有着不同的看法。 “落地”是指整个管理系统的重组&#xff0c;是企业把所学的管理知识和方法&#xff0c;在经营实践…

那些不想骑车的正常理由和十三不骑。

骑友们最热门&#xff0c;也是常常讨论的话题大多是如何骑自行车&#xff1f;但有时候&#xff0c;我们也会思考一些另类的问题。例如&#xff0c;那些不想骑车的理由。 首先&#xff0c;我们可以从科学角度来看待这个问题。骑车虽然有益于健康&#xff0c;但是骑车的姿势也会对…

用msys2安装verilator并用spinal进行仿真

一 参考 SpinalHDL 开发环境搭建一步到位(图文版) - 极术社区 - 连接开发者与智能计算生态 (aijishu.com)https://aijishu.com/a/1060000000255643Setup and installation of Verilator — SpinalHDL documentation

微信认证申请流程(政府/事业单位类型)

第一步&#xff1a;登录微信公众平台->设置->微信认证->开通 第二步&#xff1a;同意协议&#xff1a;签署《微信公众平台认证服务协议》 第三步&#xff1a;验证管理员 第四步&#xff1a;选择认证类型及填写认证资料 选择认证类型及上传申请公函 政府/事业单位资质…