用红黑树封装map和set

news/2024/11/27 6:37:33/

封装map和set

  • 红黑树的改装
    • 源码中的实现一颗红黑树封装出map和set
      • 对红黑树的第一步改造
      • 对红黑树的第二步改造
  • 迭代器的实现
    • 迭代器++的实现
    • 迭代器--的实现
    • 库中红黑树模型
    • 对insert的改造
    • map中[ ]的重载
    • 反向迭代器的实现
  • 实现map和set

红黑树的改装

🚀由于前面的博客中分享过红黑树的插入和删除: 链接,所以本文中使用的就是那篇文章中的红黑树。就是用这颗红黑树同时封装出map和set。在本文中就将删除的代码去掉了(太长了),在后面封装的时候也就不涉及map和set的erase功能的实现(就是对红黑树erase的改造,并且改造过程与insert类似)。

红黑树源码:

#pragma once
#include <time.h>
#include <stdlib.h>
namespace gy_rbtree
{typedef enum color{BLACK,RED}color;template<class K, class V>struct TreeNode{public:TreeNode<K, V>(const pair<K, V>& kv):_kv(kv), _col(RED){}public:color _col;pair<K, V> _kv;TreeNode<K, V>* _left = nullptr;TreeNode<K, V>* _right = nullptr;TreeNode<K, V>* _parent = nullptr;};template<class K, class V>class rbtree{public:typedef TreeNode<K, V> node;public:rbtree() = default; //默认构造~rbtree() { Destroy(_root); _root = nullptr; } //析构rbtree(const rbtree& t) { _root = Copy(t._root); } //拷贝构造bool insert(const pair<K, V>& kv){//根节点为空if (_root == nullptr){_root = new node(kv);_root->_col = BLACK;return true;}node* cur = _root;node* parent = nullptr;while (cur){if (cur->_kv.first > kv.first){parent = cur;cur = cur->_left;}else if (cur->_kv.first < kv.first){parent = cur;cur = cur->_right;}else{//已经存在了return false;}}//找到了属于它的位置,插入cur = new node(kv);cur->_parent = parent;if (kv.first < parent->_kv.first){parent->_left = cur;}else{parent->_right = cur;}//只要parent结点存在,并且其结点为颜色为红色就需要调整while (parent && parent->_col == RED){node* grandfather = parent->_parent;if (parent == grandfather->_left){//1,第一种情况,存在叔叔结点,并且为红色if (grandfather->_right && grandfather->_right->_col == RED){//祖父结点变红,父亲结点和叔叔结点变黑,继续向上调整,直到根节点为止。grandfather->_col = RED;parent->_col = BLACK;grandfather->_right->_col = BLACK;cur = grandfather;parent = cur->_parent;}//2,叔叔结点不存在或者为黑色else{//2.1 cur为parent的左子树,那么只需要祖父为根的树右旋即可if (cur == parent->_left){grandfather->_col = RED;parent->_col = BLACK;RotateR(grandfather);}//2.2 cur为parent的右子树,那么先对parent左旋,在对祖父右旋else if (cur == parent->_right){grandfather->_col = RED;cur->_col = BLACK;RotateLR(grandfather);}break;}}else{if (grandfather->_left && grandfather->_left->_col == RED){grandfather->_col = RED;parent->_col = BLACK;grandfather->_left->_col = BLACK;cur = grandfather;parent = cur->_parent;}else{if (cur == parent->_right){grandfather->_col = RED;parent->_col = BLACK;RotateL(grandfather);}else if (cur == parent->_left){grandfather->_col = RED;cur->_col = BLACK;RotateRL(grandfather);}break;}}}_root->_col = BLACK;return true;}private:void RotateL(node* parent){node* subR = parent->_right;node* subRL = subR->_left;parent->_right = subRL;if (subRL){subRL->_parent = parent;}subR->_left = parent;node* ppnode = parent->_parent;parent->_parent = subR;if (ppnode == nullptr){_root = subR;subR->_parent = nullptr;}else if (ppnode->_left == parent){ppnode->_left = subR;subR->_parent = ppnode;}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;}subL->_right = parent;node* ppnode = parent->_parent;parent->_parent = subL;if (ppnode == nullptr){_root = subL;subL->_parent = nullptr;}else if (ppnode->_left == parent){ppnode->_left = subL;subL->_parent = ppnode;}else{ppnode->_right = subL;subL->_parent = ppnode;}}void RotateLR(node* parent){node* subL = parent->_left;RotateL(subL);RotateR(parent);}void RotateRL(node* parent){node* subR = parent->_right;RotateR(subR);RotateL(parent);}void Destroy(node* root){if (root == nullptr) return;Destroy(root->_left);Destroy(root->_right);delete root;}node* Copy(node* root){if (root == nullptr) return;node* newroot = new node(root->_kv);newroot->_left = Copy(root->_left);newroot->_right = Copy(root->_right);return newroot;}private:node* _root = nullptr;};}

源码中的实现一颗红黑树封装出map和set

🚀很多小伙伴可能会有疑惑?set对应的红黑树结点中存储的是KEY,而map对应的红黑树结点中存储的是KEY-VALUE,而要将红黑树封装成map和set显然要两棵红黑树。下面看下源码中是如何完成的:

截取了源码中的一部分:
在这里插入图片描述
在这里插入图片描述
🚀源码中map传给红黑树的模板参数为K和pair,set传给红黑树的模板参数为K和K,所以使用一个模板参数就可以让一颗红黑树封装出map和set(起始模板实例化后仍然会实例化出两个类,但是利用泛型提高的代码的复用度)。
🚀还有让人疑惑的一点就是map和set同时向红黑树传了K这个模板参数,对于map来说是为了后面Find和Erase(插入和删除都需要key值),对于set来说完全是没有必要的只是为了配合map。

对红黑树的第一步改造

在这里插入图片描述
🚀对于红黑树的结点中存储的不再是一个pair,而是T类型的data,以便map传入的是pair,T就会实例化为pair,对于set而言传入的是key,T就实例化为key。

🚀同时还会面临一个问题就是,在插入,删除,查找的时候,都会面临要将目标内容与结点中的内容做比较,由于此时的红黑树中存储的是pair还是某个key值是不清楚的,所以比较的时候也无法比较,但无论是pair还是key我们在比较的时候都是用的key值(pair中的first值)来进行比较的。虽然红黑树不知道它存储的是pair还是单个的key,但是map和set是清楚的,所以map和set可以传给红黑树一个仿函数帮助红黑树来获取类型T中的key值。

对红黑树的第二步改造

map初步结构

template<typename K, typename V>
class map
{
private:struct Map_KeyOfT{const K& operator()(const pair<const K, V>& kv){return kv.fitst;}};
private:using tree = gy_rbtree::rbtree<K, pair<const K, V>, Map_KeyOfT>;tree _t;
};

set的初步结构

template<typename K>
class set
{
private:struct Set_KeyOfT{const K& operator()(const K& key){return key;}};
private:using tree = gy_rbtree::rbtree<K,K, Set_KeyOfT>;tree _t;
};

红黑树的进一步改造

在这里插入图片描述

迭代器的实现

🚀上面已经完成了map,set,红黑树的大体框架,下面进行一些缝缝补补即可。STL中的容器都会有它的迭代器,所以下面来实现一下红黑树的迭代器。

//正向迭代器
template<typename T,typename Ref,typename Ptr>
struct __rb_tree_iterator
{using Node = TreeNode<T>;using self = __rb_tree_iterator<T, Ref, Ptr>;Node* _node;bool operator==(const self& it) { return _node == it._node; }bool operator!=(const self& it) { return _node != it._node; }Ref operator*() { return _node->_data; }Ptr operator->() { return &operator*(); }self& operator++(){}self& operator--(){}self operator++(int){}self operator--(int){}
};

🚀主要就是完成++,–的逻辑,对于红黑树来说++之后的迭代器指向的应该是当前结点中序遍历的下一个结点。–之后迭代器指向的应该是当前结点中序遍历的前一个结点。

在这里插入图片描述

迭代器++的实现

🚀找到中序遍历的下一个结点主要有两种情况
1,当前结点有右子树 -> 右子树的最左结点
例如,图中的17结点,它的下一个结点就是其右子树的最左节点22。
2,当前结点无右子树 -> 沿着此节点到根节点的路径上找到某节点的孩子节点不是其右子树的结点
例如,途中的6结点,它的下一个结点就是8结点,因为8号结点是第一个从6号结点到根的路径上的当前结点的子树不是其右子树的结点。

self& operator++()
{Node* cur = _node;if (cur && cur->_right) // 右子树存在{cur = cur->_right;while (cur->_left) cur = cur->_left;_node = cur;}else if(cur) //右子树不存在{Node* parent = cur->_parent;while (parent && parent->_right == cur){parent = parent->_parent;cur = cur->_parent;}_node = parent;}return *this;
}

迭代器–的实现

🚀与++的思路恰好相反,找到中序遍历的前一个结点的情况有两种:
1,当前结点有左子树 -> 左子树的最右结点
2,当前结点无左子树 -> 找到当前结点到根的路径上的某节点的孩子节点不是其左子树的结点。

self& operator--()
{Node* cur = _node;if (cur && cur->_left) //存在左结点{cur = cur->_left;while (cur->_right) cur = cur->_right;_node = cur;}else if (cur) //不存在左结点{Node* parent = cur->_parent;while (parent && parent->_left == cur){parent = parent->_parent;cur = cur->_parent;}_node = parent;}return *this;
}

库中红黑树模型

在这里插入图片描述
🚀在源码中多维护了一个header结点,其左子树指向最左结点,其parent结点指向根节点,最右结点的右子树指向header,根节点的parent结点指向header。

🚀库中最左结点的前一个结点和最右结点的下一个结点都是header,而本文中写的这种红黑树结构,最左结点的前一个是nullptr,最右结点的下一个是nullptr。

🚀begin和end的实现

iterator begin()
{node* cur = _root;if (_root == nullptr) return iterator(nullptr);while (cur->_left) cur = cur->_left;return iterator(cur);
}
iterator cbegin() 
{node* cur = _root;if (_root == nullptr) return const_iterator(nullptr);while (cur->_left) cur = cur->_left;return const_iterator(cur);
}
const_iterator end() { return iterator(nullptr); }
const_iterator cend() { return const_iterator(nullptr); }

对insert的改造

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
🚀库中insert函数返回值是一个pair,其中pair的first一个迭代器(如果插入成功则表示新插入位置的迭代器,如果失败则是冲突位置的迭代器),pair的second是bool类型表示是否插入成功。

🚀insert这样设计,主要是为了重载[ ]方便,[key]的作用是插入成功返回key对应的value,不成功返回已经存在的key对应的value。就是下面截图的意思。
在这里插入图片描述

pair<iterator,bool> insert(const T& data)
{//根节点为空if (_root == nullptr){_root = new node(data);_root->_col = BLACK;return make_pair(iterator(_root),true);}node* cur = _root;node* parent = nullptr;while (cur){if (_kot(cur->_data) > _kot(data)){parent = cur;cur = cur->_left;}else if (_kot(cur->_data) < _kot(data)){parent = cur;cur = cur->_right;}else{//已经存在了return make_pair(iterator(cur),false);}}//找到了属于它的位置,插入cur = new node(data);node* ret = cur;cur->_parent = parent;if (_kot(data) < _kot(parent->_data)){parent->_left = cur;}else{parent->_right = cur;}//只要parent结点存在,并且其结点为颜色为红色就需要调整while (parent && parent->_col == RED){node* grandfather = parent->_parent;if (parent == grandfather->_left){//1,第一种情况,存在叔叔结点,并且为红色if (grandfather->_right && grandfather->_right->_col == RED){//祖父结点变红,父亲结点和叔叔结点变黑,继续向上调整,直到根节点为止。grandfather->_col = RED;parent->_col = BLACK;grandfather->_right->_col = BLACK;cur = grandfather;parent = cur->_parent;}//2,叔叔结点不存在或者为黑色else{//2.1 cur为parent的左子树,那么只需要祖父为根的树右旋即可if (cur == parent->_left){grandfather->_col = RED;parent->_col = BLACK;RotateR(grandfather);}//2.2 cur为parent的右子树,那么先对parent左旋,在对祖父右旋else if (cur == parent->_right){grandfather->_col = RED;cur->_col = BLACK;RotateLR(grandfather);}break;}}else{if (grandfather->_left && grandfather->_left->_col == RED){grandfather->_col = RED;parent->_col = BLACK;grandfather->_left->_col = BLACK;cur = grandfather;parent = cur->_parent;}else{if (cur == parent->_right){grandfather->_col = RED;parent->_col = BLACK;RotateL(grandfather);}else if (cur == parent->_left){grandfather->_col = RED;cur->_col = BLACK;RotateRL(grandfather);}break;}}}_root->_col = BLACK;return make_pair(iterator(ret),true);
}

map中[ ]的重载

V& operator[](const K& key)
{pair<iterator, bool> res = insert(make_pair(key, V()));return ( * (res.first)).second;
}

🚀res.first表示结点的迭代器,对其解引用就是对应的结点,结点的second就是key对应的value。

反向迭代器的实现

🚀反向迭代器就是对正向迭代器的封装,正向迭代器的++就是反向迭代器的–,反之亦然。

//反向迭代器
template<typename T>
struct __rb_tree_reverse_iterator
{using Node = T;Node _it;using Ref = typename T::_Ref;using Ptr = typename T::_Ptr;__rb_tree_reverse_iterator(T it) :_it(it) {};using self = __rb_tree_reverse_iterator<T>;bool operator==(const self& it) { return _it == it._it; }bool operator!=(const self& it) { return _it != it._it; }Ref operator*() { return *_it; }Ptr operator->() { return &operator*(); }self& operator++() { --_it; return *this; }self& operator--() { ++_it; return *this; }self operator++(int) { self tmp(*this);++tmp;return tmp;}self operator--(int){self tmp(*this);--tmp;return tmp;}
};

实现map和set

map.h

namespace gy
{template<typename K, typename V>class map{private:struct Map_KeyOfT{const K& operator()(const pair<const K, V>& kv){return kv.first;}};public:using tree = gy_rbtree::rbtree<K, pair<const K, V>, Map_KeyOfT>;using iterator = typename tree::iterator;using const_iterator = typename tree::const_iterator;using reverse_iterator = typename tree::reverse_iterator;using const_reverse_iterator = typename tree::const_reverse_iterator;public:iterator begin() { return _t.begin(); };const_iterator cbegin() { return _t.cbegin(); };iterator end() { return _t.end(); };const_iterator cend() { return _t.cend(); };reverse_iterator rbegin() { return _t.rbegin(); };const_reverse_iterator crbegin() { return _t.crbegin(); };reverse_iterator rend() { return _t.rend(); };const_reverse_iterator crend() { return _t.crend(); };pair<iterator,bool> insert(const pair<K, V>& kv) { return _t.insert(kv); }V& operator[](const K& key){pair<iterator, bool> res = insert(make_pair(key, V()));return ( * (res.first)).second;}private:tree _t;};
}

set.h

namespace gy
{template<typename K>class set{private:struct Set_KeyOfT{const K& operator()(const K& key){return key;}};public:using tree = gy_rbtree::rbtree<K, K, Set_KeyOfT>;using iterator = typename tree::iterator;using const_iterator = typename tree::const_iterator;using reverse_iterator = typename tree::reverse_iterator;using const_reverse_iterator = typename tree::const_reverse_iterator;public:iterator begin() { return _t.begin(); };const_iterator cbegin() { return _t.cbegin(); };iterator end() { return _t.end(); };const_iterator cend() { return _t.cend(); };reverse_iterator rbegin() { return _t.rbegin(); };const_reverse_iterator crbegin() { return _t.crbegin(); };reverse_iterator rend() { return _t.rend(); };const_reverse_iterator crend() { return _t.crend(); };pair<iterator,bool> insert(const K& k) { return _t.insert(k); }private:tree _t;};
}

RBTree.h

#pragma once
#include <time.h>
#include <stdlib.h>
namespace gy_rbtree
{typedef enum color{BLACK,RED}color;//红黑树结点template<typename T>struct TreeNode{public:TreeNode<T>(const T& data):_data(data), _col(RED){}public:color _col;T _data;TreeNode<T>* _left = nullptr;TreeNode<T>* _right = nullptr;TreeNode<T>* _parent = nullptr;};//正向迭代器template<typename T,typename Ref,typename Ptr>struct __rb_tree_iterator{using _Ref = Ref;using _Ptr = Ptr;using Node = TreeNode<T>;__rb_tree_iterator(Node* n) : _node(n) {};using self = __rb_tree_iterator<T, Ref, Ptr>;Node* _node;bool operator==(const self& it) { return _node == it._node; }bool operator!=(const self& it) { return _node != it._node; }Ref operator*() { return _node->_data; }Ptr operator->() { return &operator*(); }self& operator++(){Node* cur = _node;if (cur && cur->_right) // 右子树存在{cur = cur->_right;while (cur->_left) cur = cur->_left;_node = cur;}else if(cur) //右子树不存在{Node* parent = cur->_parent;while (parent && parent->_right == cur){parent = parent->_parent;cur = cur->_parent;}_node = parent;}return *this;}self& operator--(){Node* cur = _node;if (cur && cur->_left) //存在左结点{cur = cur->_left;while (cur->_right) cur = cur->_right;_node = cur;}else if (cur) //不存在左结点{Node* parent = cur->_parent;while (parent && parent->_left == cur){parent = parent->_parent;cur = cur->_parent;}_node = parent;}return *this;}self operator++(int){self tmp(*this);++tmp;return tmp;}self operator--(int){self tmp(*this);--tmp;return tmp;}};//反向迭代器template<typename T>struct __rb_tree_reverse_iterator{using Node = T;Node _it;using Ref = typename T::_Ref;using Ptr = typename T::_Ptr;__rb_tree_reverse_iterator(T it) :_it(it) {};using self = __rb_tree_reverse_iterator<T>;bool operator==(const self& it) { return _it == it._it; }bool operator!=(const self& it) { return _it != it._it; }Ref operator*() { return *_it; }Ptr operator->() { return &operator*(); }self& operator++() { --_it; return *this; }self& operator--() { ++_it; return *this; }self operator++(int) { self tmp(*this);++tmp;return tmp;}self operator--(int){self tmp(*this);--tmp;return tmp;}};//红黑树template<class K, class T,class KeyOfT>class rbtree{public:typedef TreeNode<T> node;KeyOfT _kot;using iterator = __rb_tree_iterator<T, T&, T*>;using reverse_iterator = __rb_tree_reverse_iterator<iterator>;using const_iterator = __rb_tree_iterator<T, const T&, const T*>;using const_reverse_iterator = __rb_tree_reverse_iterator<const_iterator>;public:rbtree() = default; //默认构造~rbtree() { Destroy(_root); _root = nullptr; } //析构rbtree(const rbtree& t) { _root = Copy(t._root); } //拷贝构造iterator begin(){node* cur = _root;if (_root == nullptr) return iterator(nullptr);while (cur->_left) cur = cur->_left;return iterator(cur);}const_iterator cbegin() {node* cur = _root;if (_root == nullptr) return const_iterator(nullptr);while (cur->_left) cur = cur->_left;return const_iterator(cur);}iterator end() { return iterator(nullptr); }const_iterator cend() { return const_iterator(nullptr); }reverse_iterator rbegin() {node* cur = _root;if (_root == nullptr) return reverse_iterator(iterator(nullptr));while (cur->_right) cur = cur->_right;return reverse_iterator(iterator(cur));}reverse_iterator rend() { return reverse_iterator(iterator(nullptr)); }const_reverse_iterator crbegin(){ node* cur = _root;if (_root == nullptr) return const_iterator(nullptr);while (cur->_right) cur = cur->_right;return const_reverse_iterator(const_iterator(cur));}const_reverse_iterator crend() { return const_reverse_iterator(const_iterator(nullptr)); }pair<iterator,bool> insert(const T& data){//根节点为空if (_root == nullptr){_root = new node(data);_root->_col = BLACK;return make_pair(iterator(_root),true);}node* cur = _root;node* parent = nullptr;while (cur){if (_kot(cur->_data) > _kot(data)){parent = cur;cur = cur->_left;}else if (_kot(cur->_data) < _kot(data)){parent = cur;cur = cur->_right;}else{//已经存在了return make_pair(iterator(cur),false);}}//找到了属于它的位置,插入cur = new node(data);node* ret = cur;cur->_parent = parent;if (_kot(data) < _kot(parent->_data)){parent->_left = cur;}else{parent->_right = cur;}//只要parent结点存在,并且其结点为颜色为红色就需要调整while (parent && parent->_col == RED){node* grandfather = parent->_parent;if (parent == grandfather->_left){//1,第一种情况,存在叔叔结点,并且为红色if (grandfather->_right && grandfather->_right->_col == RED){//祖父结点变红,父亲结点和叔叔结点变黑,继续向上调整,直到根节点为止。grandfather->_col = RED;parent->_col = BLACK;grandfather->_right->_col = BLACK;cur = grandfather;parent = cur->_parent;}//2,叔叔结点不存在或者为黑色else{//2.1 cur为parent的左子树,那么只需要祖父为根的树右旋即可if (cur == parent->_left){grandfather->_col = RED;parent->_col = BLACK;RotateR(grandfather);}//2.2 cur为parent的右子树,那么先对parent左旋,在对祖父右旋else if (cur == parent->_right){grandfather->_col = RED;cur->_col = BLACK;RotateLR(grandfather);}break;}}else{if (grandfather->_left && grandfather->_left->_col == RED){grandfather->_col = RED;parent->_col = BLACK;grandfather->_left->_col = BLACK;cur = grandfather;parent = cur->_parent;}else{if (cur == parent->_right){grandfather->_col = RED;parent->_col = BLACK;RotateL(grandfather);}else if (cur == parent->_left){grandfather->_col = RED;cur->_col = BLACK;RotateRL(grandfather);}break;}}}_root->_col = BLACK;return make_pair(iterator(ret),true);}private:void RotateL(node* parent){node* subR = parent->_right;node* subRL = subR->_left;parent->_right = subRL;if (subRL){subRL->_parent = parent;}subR->_left = parent;node* ppnode = parent->_parent;parent->_parent = subR;if (ppnode == nullptr){_root = subR;subR->_parent = nullptr;}else if (ppnode->_left == parent){ppnode->_left = subR;subR->_parent = ppnode;}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;}subL->_right = parent;node* ppnode = parent->_parent;parent->_parent = subL;if (ppnode == nullptr){_root = subL;subL->_parent = nullptr;}else if (ppnode->_left == parent){ppnode->_left = subL;subL->_parent = ppnode;}else{ppnode->_right = subL;subL->_parent = ppnode;}}void RotateLR(node* parent){node* subL = parent->_left;RotateL(subL);RotateR(parent);}void RotateRL(node* parent){node* subR = parent->_right;RotateR(subR);RotateL(parent);}void Destroy(node* root){if (root == nullptr) return;Destroy(root->_left);Destroy(root->_right);delete root;}node* Copy(node* root){if (root == nullptr) return;node* newroot = new node(root->_kv);newroot->_left = Copy(root->_left);newroot->_right = Copy(root->_right);return newroot;}private:node* _root = nullptr;};}

http://www.ppmy.cn/news/453425.html

相关文章

Flink 状态一致性

Flink 状态一致性 端到端精确一次输入端输出端预写日志两阶段提交 状态一致性有三种级别&#xff1a; 最多一次 (AT-MOST-ONCE) : 只处理一次 , 遇到故障就会丢失 , 优点 : 处理快至少一次 (AT-LEAST-ONCE) : 不会丢失数据 , 但存在重复数据精确一次&#xff08;EXACTLY-ONCE)…

练习题-123

6447. 给墙壁刷油漆 记忆化搜索 f[i][t] 表示为粉刷前 i 堵墙,总耗时为 t 的最小开销。 对于每堵墙 i,可以选择付费或者免费,如果付费,花费的时间为 time[i];如果免费,花费的时间视为 -1。那么,答案就是对于所有 t ≥ 0, f[n][t] 的最小值。采用 “刷表法” 进行状态…

CSS基础学习--20 提示工具(Tooltip)

一、定义 提示工具在鼠标移动到指定元素后触发&#xff0c;总共四种样式实例&#xff1a; 二、基础提示框(Tooltip) 提示框在鼠标移动到指定元素上显示 <!DOCTYPE html> <html> <head> <meta charset"utf-8"> <title>CSS基础学习-提…

键码值获取 设置按键复制粘贴

通过设置键盘事件指定复制粘贴按键 设置按下系统再饰键的同时&#xff0c;再按下然后松开其他按键触发事件&#xff08;与keyup使用&#xff09; 指定按下ctrla复制&#xff0c;ctrlb粘贴 <div class"app">复制<input type"text" keyup.ctrl.a&…

oppoa55按键音设置步骤方法分享

oppoa55是一款十分实用的手机。其销量以及口碑都很不错。最近有朋友问小编oppoa55的按键音该怎么设置呢&#xff1f;不要着急。换换为大家准备了详细的设置方法。快叫上你的小伙伴一起来看看吧&#xff01; oppoa55按键音设置步骤分享 1、打开手机设置。点击【声音与振动】。…

苹果11键盘按键音设置方法(苹果11如何设置按键音)

如果你不喜欢在使用苹果11时有按键音&#xff0c;请通过以下方法进行设置&#xff1a; 进入苹果iPhone11的“设置”-“声音与触感”选项。在“声音”栏中点击“按键音”开关&#xff0c;即可开启或关闭按键音&#xff08;开启时为绿色&#xff0c;关闭为灰色&#xff09;。 如只…

计算机功能自定义,电脑鼠标自定义按键设置方法

设置鼠标的自定义按键&#xff0c;可以帮助我们更好地使用鼠标&#xff0c;提高使用效率&#xff0c; 下面是学习啦小编为大家介绍电脑鼠标自定义按键要怎么设置,欢迎大家阅读。 电脑鼠标自定义按键怎么设置 首先你应该有如上图一样的鼠标才可以更改&#xff0c;然后去官方下载…

安卓按键设置焦点

目录 前言 1.在XML中设置焦点属性 2.在代码中设置焦点 前言 在开发过程中会遇到为手机或电视设置按键可操作或者触屏可操作的需求。 1.在XML中设置焦点属性 <!-- View中控件先后获取焦点的属性 -->android:descendantFocusability"beforeDescendants" 1. …