【C++/STL】手撕红黑树

2023-11-12


在这里插入图片描述

1.红黑树的概念

红黑树,是一种二叉搜索树,但在每个结点上增加一个存储位表示结点的颜色,可以是Red或Black。

通过对任何一条从根到叶子的路径上各个结点着色方式的限制,红黑树确保没有一条路径会比其他路径长出俩倍,因而是接近平衡的。

image-20220803172623292

1.1红黑树的性质和规则
  • 1.每个结点不是红色就是黑色
  • 2.规定根节点是黑色
  • 3.如果一共节点是红色,则它的两个孩子节点是黑色的
  • 4.对于每一个节点,从该节点到其所有后代叶节点的简单路径上,均包含相同数目的黑色结点
  • 5.每个叶子节点都是黑色(这里的叶子结点指的是nullptr结点,在统计黑色结点数量时可以忽略)

为什么满足上面的性质就可以让最长路径不超过最短路径的2倍?

1.)上面的性质三说明了红黑树一定不存在连续的红色结点。

2.)每一条简单路径上的黑色结点数相等。

因此最短的路径一定由全是黑色结点组成(或者这条路径中红色结点最少),最长的路径一定是一红一黑的组合。(或者一红一黑的组合最多) 而两条路径的黑色结点数相等,那么最长路径最多只能为全黑路径的2倍。

在这里插入图片描述

2.红黑树的模拟实现

2.1节点的定义
enum Colour
{
	BLACK,
	RED
};
//三叉链
template <class K,class V>
struct  RBTreeNode
{
	RBTreeNode<K, V>* _left;
	RBTreeNode<K, V>* _right;
	RBTreeNode<K, V>* _parent;
	pair<K, V> _kv;
	Colour _col;
	RBTreeNode(const pair<K, V>& kv)
		:_left(nullptr), _right(nullptr), _parent(nullptr), _kv(kv), _col(RED)
	{}
};

红黑树的框架

template <class K,class V>
class RBTree
{
public:
	typedef RBTreeNode<K, V> Node;
public:
 	/*
 		对外的接口
 		insert()..
 		inOrder()...
 		.....
    */
private:
    /*
    	内部函数
    */
private:
    Node* _root
}
2.2节点的插入

步骤一:红黑树也是一种二叉搜索树,因此先按照二叉搜索树的规则找到插入数据的位置。

bool insert(const pair<K, V>& kv)
{
	//按照普通的搜索二叉树的规则插入
	if (_root == nullptr)
	{
		_root = new Node(kv);
		_root->_col = BLACK;
		return true;
	}
	Node* parent = nullptr;
	Node* cur = _root;
	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->_right = cur;
	}
	else
	{
		parent->_left = cur;
	}
    //新插入的结点默认为红色?
    cur->_col=RED;
    /*
    ................................
    ................................
    改变颜色,旋转等操作。
    */
}

为什么新插入的结点设置为红色?

如果我们设置为黑色,那么路径中的黑色结点数就发送了改变,由于规则4,其影响了一整棵红黑树的结构。 如果我们设置为红色,如果它的parent结点是黑色,那么对红黑树的规则没有影响,当parent结点为红色时,也只是影响力父子两个结点,违反了规则3。

对下面的图,我们有下面的约定: 约定:cur为当前节点,p为父节点,g为祖父节点,u为叔叔节点 ;对于红黑树来说,调整其结构,主要看u节点的情况

1.)情况一:u存在且为红色

image-20220803175536399

调整方式:将p和u变为黑色,g变为红色。

image-20220803175710144

还需要注意曾祖父s节点的情况。

image-20220803175903725

即将cur赋值为g,p赋值为g。进行下一步循环

cur=grandfaher;
parent=cur->_parent;

如果g是根节点,调试完成后,退出循环,并要将根的颜色改为黑色。

_root->_col=BLACK;
2.)情况二:u不存在/u存在且为黑(并且g、p、cur为一条直线)

对于u有两种情况情况:

1.u不存在,那么cur一定是新插入的节点。因为如果cur不是新插入的节点,那么原来cur和p一定有一个是黑色节点,否则违反规则四:每条路径的黑色节点数相等。 2.u存在。则u一定是黑色节点。cur原来一定是黑色结点,现在看到的红色是因为调整后有黑色变为的红色。

2:

image-20220803181248853

情况2.)分为两种情况: 可以简记为左左右右右左

左左右:p为g的左孩子,cur为p的左孩子,进行右单旋;

右右左:p为g的右孩子,cur为p的右孩子,进行左单旋

(1.)p为g的左孩子,cur为p的左孩子,则g进行右单旋转

image-20220803181536605

(2.)p为g的右孩子,cur为p的右孩子,则g进行左单旋转

image-20220803181601885

旋转结束后变色:p、g变色–p变黑,g变红

3.)情况三:u不存在/u存在且为黑(并且g、p、cur为一条折线)

和情况2.)类似,只不过g,p,cur的关系变为了一条折线。

同样分为两种情况:可以简记为左右 左右双旋;右左 右左双旋转

p为g的左孩子,cur为p的右孩子,先对p进行左单旋,再对g进行右单旋;

p为g的右孩子,cur为左的右孩子,先对p进行右单旋,再对g进行左单旋;

(1.)p为g的左孩子,cur为p的右孩子,先对p进行左单旋,再对g进行右单旋;

image-20220803194058698

(2.)p为g的右孩子,cur为左的右孩子,先对p进行右单旋,再对g进行左单旋

image-20220803194317071

2.3旋转代码实现
//存在连续的红色节点,就需要进行调整
while (parent && parent->_col == RED)
{
	Node* grandfather = parent->_parent;
    //如果祖父结点不存在,那么就直接跳过
	if (!grandfather)
	{
		break;
	}
	Node* uncle = nullptr;
	if (parent == grandfather->_left)
	{
		uncle = grandfather->_right;
        //情况一:u存在且为红
		if (uncle && uncle->_col == RED)
		{
			parent->_col = BLACK;
			uncle->_col = BLACK;
			grandfather->_col = RED;
			cur = grandfather;
			parent = cur->_parent;
		}
		else
		{
            //情况二:u不存在或者u存在且为黑
            //左左右的情况
			if (cur == parent->_left)
			{
				//右单旋
				RouteR(grandfather);
				grandfather->_col = RED;
				parent->_col = BLACK;
				break;
			}
            //情况三:u不存在或者存在且为黑
            //左右左右的情况
			else
			{
				RouteL(parent);
				RouteR(grandfather);
				grandfather->_col = RED;
				cur->_col = BLACK;
				break;
			}
		}
	}
	else
	{
		uncle = grandfather->_left;
        //情况一:u存在且为红
		if (uncle && uncle->_col == RED)
		{
			parent->_col = BLACK;
			uncle->_col = BLACK;
			grandfather->_col = RED;
			cur = grandfather;
			parent = cur->_parent;
		}
		else
		{
            //情况二:u不存在或者u存在且为黑
            //右右左的情况
			if (cur == parent->_right)
			{
				//左单旋
				RouteL(grandfather);
				grandfather->_col = RED;
				parent->_col = BLACK;
				break;
			}
            //情况三:u不存在或者存在且为黑
            //右左右左的情况
			else
			{
				RouteR(parent);
				RouteL(grandfather);
				grandfather->_col = RED;
				cur->_col = BLACK;
				break;
			}
		}
	}
	_root->_col = BLACK;
}
2.3.1右旋和左旋
//左单旋
void RouteL(Node* parent)
{
	Node* subR = parent->_right;
	Node* subRL = subR->_left;
	Node* pparent = parent->_parent;
	parent->_right = subRL;
	if (subRL)
	{
		subRL->_parent = parent;
	}
	subR->_left = parent;
	parent->_parent = subR;
	//如果root为根
	if (parent == _root)
	{
		_root = subR;
		subR->_parent = nullptr;
	}
	else
	{
		subR->_parent = pparent;
		if (parent == pparent->_left)
		{
			pparent->_left = subR;
		}
		else
		{
			pparent->_right = subR;
		}
	}
}

//右单旋
void RouteR(Node* parent)
{
	Node* subL = parent->_left;
	Node* subLR = subL->_right;
	parent->_left = subLR;
	if (subLR)
	{
		subLR->_parent = parent;
	}
	subL->_right = parent;
	Node* pparent = parent->_parent;
	parent->_parent = subL;
	if (parent == _root)
	{
		_root = subL;
		subL->_parent = nullptr;
	}
	else
	{
		if (pparent->_left == parent)
		{
			pparent->_left = subL;
		}
		else
		{
			pparent->_right = subL;
		}
		subL->_parent = pparent;
	}
}
2.4中序遍历

中序遍历和一般的二叉搜索树方式一样。

void _inorder(Node* root)
{
	if (root == nullptr)
	{
		return;
	}
	_inorder(root->_left);
	cout << root->_kv.first << " ";
	_inorder(root->_right);
}
	//中序遍历
void inorder()
{
	_inorder(_root);
}
2.5验证是否为红黑树

红黑树的检测分为两步:

  1. 检测其是否满足二叉搜索树(中序遍历是否为有序序列)
  2. 检测其是否满足红黑树的性质 ,验证是否满足红黑树的5条规则。
2.5.1求最长路径和最短路径
int _maxHeight(Node* root)
{
	if (root == nullptr)
	{
		return 0;
	}
	int left = _maxHeight(root->_left);
	int right = _maxHeight(root->_right);
	return max(left, right) + 1;
}
int _minHeight(Node* root)
{
	if (root == nullptr)
	{
		return 0;
	}
	int left = _minHeight(root->_left);
	int right = _minHeight(root->_right);
	return min(left, right) + 1;
}

void Hight()
{
	cout << "最长路径:" << _maxHeight(_root) << endl;
	cout << "最短路径:" << _minHeight(_root) << endl;
}
2.5.2 isRBTree()函数
bool isRBTree()
{
	//记录最左边一条路径的黑色节点个数
	int blacksize = 0;
	Node* cur = _root;
	if (cur == nullptr)
	{
		return true;
	}
	if (_root->_col == RED)
	{
		cout << "违反规则二:红黑树的根必须是黑色" << endl;
		return false;
	}
	int maxlength = _maxHeight(_root);
	int minlength = _minHeight(_root);
	Hight();
	if (maxlength > 2 * minlength)
	{
		return false;
	}
	while (cur)
	{
		if (cur->_col == BLACK)
		{
		blacksize++;
		}
		cur = cur->_left;
	}
	int k = 0;
	return _isRBTree(_root,k,blacksize);
}

_isRBTree()

bool _isRBTree(Node* root,int k,int blacksize)
{
	//比较该路径和最左边路径的黑色节点数是否相等
	if (root == nullptr)
	{
		if (k != blacksize)
		{
			cout << "违反规则四:每条路径的黑色结点数相等" << endl;
			return false;
		}
			return true;
	}
	//判断是否右联系的红色结点
	if (root->_col == RED && root->_parent && root->_parent->_col == RED)
	{
		cout << "违反规则三:没有连续的红色结点" << endl;
		return false;
	}
	if (root->_col == BLACK)
	{
		k++;
	}
	return _isRBTree(root->_left, k, blacksize) && _isRBTree(root->_right, k, blacksize);
}
2.6实现结果
int main()
{
	srand(time(0));
	RBTree<int, int>rbt;
	int N = 100;
	int n = 0;
	for (int i = 0;i < N;i++)
	{
		n = rand();
		rbt.insert(make_pair(i, n));
	}
	bool ret=rbt.isRBTree();
	cout << ret << endl;
	//rbt.inorder();
	return 0;
}

image-20220803200413501
在这里插入图片描述

3.红黑树迭代器的封装

红黑树的迭代器,本质上是对红黑树结点指针的封装。
实现代码:

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
{
public:
	typedef RBTreeNode<T> Node;
	typedef _RBTreeIterator<T, Ref, Ptr> Self;
	Node* _node;
	//构造函数
	_RBTreeIterator(Node* node)
		:_node(node)
	{}
	Ref operator*()
	{
		return _node->_data;
	}
	Ptr operator->()
	{
		return &(_node->_data);
	}
	Self operator++(int)
	{
		Self tmp(*this);
		++(*this);
			return tmp;
	}
	Self& operator++()
	{

		if (_node->_right == nullptr)
		{
			Node* cur = _node;
			Node* parent = cur->_parent;
			while (parent && parent->_right == cur)
			{
				cur = parent;
				parent = cur->_parent;
			}
			_node = parent;
		}
		//在右子树的最左结点
		else
		{
			Node* cur = _node->_right;
			while (cur->_left)
			{
				cur = cur->_left;
			}
			_node = cur;
		}
		return *this;
	}
	Self operator--(int)
	{
		Self tmp(*this);
		--(*this);
		return tmp;
	}
	Self& operator--()
	{
		if (_node->_left == nullptr)
		{
			Node* cur = _node;
			Node* parent = cur->_parent;
			while (parent && cur == parent->_left)
			{
				cur = parent;
				parent = cur->_parent;
			}
			_node = parent;
		}
		//左子树的最右子树
		else
		{
			Node* subLR = _node->_left;
			while (subLR->_right)
			{
				subLR = subLR->_right;
			}
			_node = subLR;
		}
		return *this;
	}
	bool operator==(const Self& s) const
	{
		return _node == s._node;
	}
	bool operator!= (const Self& s) const
	{
		return _node != s._node;
	}
};

在这里插入图片描述

4.红黑树的补充接口和insert()的改造

在STL库中,insert()的返回值是返回的一个pair<iteraror,bool>的类型,在自己模拟实现的时候,insert()也返回该类型。
同时,为了配合迭代器的使用,还需要加上begin(),end()等API


template<class K,class T,class compare>
class RBTree
	{
	public:
		typedef _RBTreeIterator<T, T&, T*> iterator;
		typedef _RBTreeIterator<T, const T&, const T*> const_iterator;
		Compare _com;
		typedef RBTreeNode<T> Node;
		iterator begin()
		{
			Node* cur = _root;
			while (cur->_left)
			{
				cur = cur->_left;
			}
			return iterator(cur);
		}
		iterator end()
		{
			return iterator(nullptr);
		}
		const_iterator begin()const
		{
			Node* cur = _root;
			while (cur->_left)
			{
				cur = cur->_left;
			}
			return const_iterator(cur);
		}
		const_iterator end()const
		{
			return const_iterator(nullptr);
		}

		iterator find(const K&key)
		{
			Node* cur = _root;
			Compare _com;
			while (cur)
			{
				if (_com(_root->_data) == key)
				{
					return iterator(cur);
				}
				else if (_com(_root->_data) < key)
				{
					cur = cur->_right;
				}
				else
				{
					cur = cur->_left;
				}
			}
			return 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* parent = nullptr;
			Node* cur = _root;
			//比较K
			while (cur)
			{
				if (_com(cur->_data) > _com(data))
				{
					parent = cur;
					cur = cur->_left;
				}
				else if (_com(cur->_data) < _com(data))
				{
					parent = cur;
					cur = cur->_right;
				}
				else
				{
					return  make_pair(iterator(cur),false);
				}
			}
			cur = new Node(data);
			Node* newnode = cur;
			cur->_parent = parent;
			if (_com(data) > _com(parent->_data))
			{
				parent->_right = cur;
			}
			else
			{
				parent->_left = cur;
			}
			//cur是插入的节点
			cur->_col = RED;
			//存在连续的红色节点,就需要进行调整
			while (parent && parent->_col == RED)
			{
				Node* grandfather = parent->_parent;
				if (!grandfather)
				{
					break;
				}
				Node* uncle = nullptr;
				if (parent == grandfather->_left)
				{
					uncle = grandfather->_right;
					if (uncle && uncle->_col == RED)
					{
						parent->_col = BLACK;
						uncle->_col = BLACK;
						grandfather->_col = RED;
						cur = grandfather;
						parent = cur->_parent;
					}
					else
					{
						if (cur == parent->_left)
						{
							//右单旋
							RouteR(grandfather);
							grandfather->_col = RED;
							parent->_col = BLACK;
							break;
						}
						else
						{
							RouteL(parent);
							RouteR(grandfather);
							grandfather->_col = RED;
							cur->_col = BLACK;
							break;
						}
					}
				}
				else
				{
					uncle = grandfather->_left;
					if (uncle && uncle->_col == RED)
					{
						parent->_col = BLACK;
						uncle->_col = BLACK;
						grandfather->_col = RED;
						cur = grandfather;
						parent = cur->_parent;
					}
					else
					{
						if (cur == parent->_right)
						{
							//左单旋
							RouteL(grandfather);
							grandfather->_col = RED;
							parent->_col = BLACK;
							break;
						}
						else
						{
							RouteR(parent);
							RouteL(grandfather);
							grandfather->_col = RED;
							cur->_col = BLACK;
							break;
						}
					}
				}
				_root->_col = BLACK;
			}
			return make_pair(iterator(newnode), true);
		}
		/*
		上面的其余接口实现
		.............................
		.............................
		.............................
		*/
};

在这里插入图片描述

5.封装Map

Map的底层是一棵红黑树,只是在传递红黑树存储类型的时候需要传递pair<K,V>类型。

template <class K,class V>
class Map
{
public:
	struct keyof
	{
		const K& operator()(const pair<K, V>& kv)
		{
			return kv.first;
		}
	};
	typedef typename RBTree<K, pair<K, V>, keyof>::iterator iterator;
	typedef typename RBTree<K, pair<K, V>, keyof>::const_iterator const_iterator;
	iterator begin()
	{
		return _t.begin();
	}
	iterator end()
	{
		return _t.end();
	}
	const_iterator begin() const
	{
		return _t.begin();
	}
	const_iterator end() const
	{
		return _t.end();
	}
	iterator find()
	{
		return _t.find();
	}
	pair<iterator, bool>insert(const pair<K, V>& kv)
	{
		return _t.insert(kv);
	}
	V& operator[](const K& key)
	{
		auto it = _t.insert();
		return it.first->second;
	}
private:
	RBTree<K, pair<K, V>, keyof> _t;
};

6.封装Set

Set类型的底层也是一棵红黑树,只是存储的类型就是K

template<class K>
class Set
{
	//一个用于提取K的仿函数
public:
	struct  Keyof
	{
		const K& operator()(const K& key)
		{
			return key;
		}
	};
	typedef typename RBTree<K, K, Keyof>::const_iterator iterator;
	typedef typename RBTree<K, K, Keyof>::const_iterator const_iteraotr;
	iterator begin()const
	{
		return _t.begin();
	}
	iterator end() const
	{
		return _t.end();
	}
	pair<iterator, bool>insert(const K& key)
	{
		auto it=_t.insert(key);
		return pair<iterator, bool>(iterator(it.first._node), it.second);
	}
	iterator find(const K& key)
	{
		return _t.find(key);
	}
	//set的底层是一棵红黑树
private:
	RBTree<K, K, Keyof> _t;
};

7.实现结果调试

void testset()
{
	cout << "testset:" << endl;
	Set<int> st;
	st.insert(1);
	st.insert(2);
	Set<int>::iterator it = st.begin();
	while (it != st.end())
	{
		cout << *it << endl;
		it++;
	}
	cout << endl;
	st.insert(3);
	for (auto i : st)
	{
		cout << i << " " << endl;
	}
}
void testmap()
{
	cout << "testmap:" << endl;
	Map<int, int>mp;
	mp.insert(make_pair(1, 1));
	mp.insert(make_pair(4, 4));
	mp.insert(make_pair(7, 7));
	mp.insert(make_pair(8, 8));
	mp.insert(make_pair(2, 2));
	mp.insert(make_pair(3, 3));
	Map<int, int>::iterator it = mp.begin();
	while (it != mp.end())
	{
		cout << it->first << ":" << it->second << endl;
		it++;
	}
	cout << endl;
	for (auto i : mp)
	{
		cout << i.first << ":" << i.second << endl;
	}
	cout << endl;
}

在这里插入图片描述

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

【C++/STL】手撕红黑树 的相关文章

  • X11 模式对话框

    如何使用 Xlib 在 X11 中创建模式对话框 模态对话框是一个位于应用程序其他窗口之上的窗口 就像瞬态窗口一样 并且拒绝将焦点给予应用程序的其他窗口 在 Windows 中 当试图从模态窗口夺取焦点时 模态也会通过闪 烁模态窗口的标题栏
  • fopen_s 怎么会比 fopen 更安全呢?

    我正在处理遗留代码Windows平台 当我编译代码时VS2013 它给出以下警告 错误 C4996 fopen 该函数或变量可能不安全 考虑使用fopen s反而 要禁用弃用 请使用 CRT SECURE NO WARNINGS 详情请参见
  • 为什么我会收到未找到分析器的警告?

    我创建了一个玩具项目来检查最新的 NET 7 预览版 5 和正则表达式代码生成 它效果很好 所以我对现有项目应用了相同的更改 不是为了生产 而是为了个人生产力 由于某种原因 我收到这些警告 CS8032 An instance of ana
  • 平滑手绘曲线

    我有一个允许用户绘制曲线的程序 但这些曲线看起来不太好 它们看起来摇摇欲坠 而且是手绘的 所以我想要一种能够自动平滑它们的算法 我知道平滑过程中存在固有的模糊性 因此它不会每次都完美 但这种算法似乎确实存在于多个绘图包中 并且它们工作得很好
  • 为什么子函数不销毁GtkWindow?

    这是我的代码 void window first void enter window2 GtkWidget w gpointer data void quit GtkWidget w gpointer data void quit int
  • C# 无法捕获 SerializationException

    我的程序在加载序列化文件的部分遇到问题 如果文件无法反序列化 我希望很好地失败 但由于某种原因 我的程序将中断而不是进入 catch 子句 这是我的代码 using FileStream fs new FileStream openFile
  • 如何在C中同时运行两个子进程?

    所以我开始学习并发编程 但由于某种原因我什至无法掌握基础知识 我有一个名为 fork c 的文件 其中包含一个 main 方法 在此方法中 我将 main 分叉两次 分别进入子进程 1 和 2 在孩子 1 中 我打印了字符 A 50 次 在
  • Monotouch全局异常处理

    我在野外发现了一只令人讨厌的虫子 但我无法确定它的具体情况 有没有办法拥有全局 Try Catch 块 或者有办法处理 Monotouch 中未处理的任何异常 我可以包起来吗UIApplication Main args 在 try cat
  • Visual Studio 中列表框的上移、下移按钮[重复]

    这个问题在这里已经有答案了 我正在尝试制作一个上移按钮和一个下移按钮 以移动 Microsoft Visual Studio 2012 中列表框中的选定项目 我已经在 WDF jquery winforms 和其他一些表单中看到了其他示例
  • 对作为函数参数传递的指针使用删除

    删除作为函数参数传递的指针是否可以 并且合法 如下所示 include
  • 在c#中获取没有时间的日期

    我的表上有一列 缺勤日期时间 日期 当我想要获取包含日期的行时 它返回 0 行 这是我的 C 代码 DateTime ClassDate DateTime Parse lblDate Content ToString var Abs dbs
  • 处理“未找到细胞”。 Excel 中的错误

    我正在使用 Excel VSTO 应用程序并使用以下代码在工作表中查找错误单元格 Excel Range rngTemp Excel Range rngErrorRange Excel Worksheet Sheet1 Excel Work
  • 配置:错误:无法运行C编译的程序

    我正在尝试使用 Debian Wheezy 操作系统在我的 Raspberry Pi 上安装不同的软件 当我运行尝试配置软件时 我尝试安装我得到此输出 checking for C compiler default output file
  • C#:自定义转换为值类型

    是否可以将自定义类转换为值类型 这是一个例子 var x new Foo var y int x Does not compile 是否有可能实现上述情况 我需要超载一些东西吗Foo 您将必须重载强制转换运算符 public class F
  • 如何在 C# 中更改公共 IP 地址

    我正在创建一个 C winform 应用程序 我想在其中更改公共 IP 地址 而不是像 Hotspot Shield ZenMate OpenVPN 等那样更改 IPv4 地址 我已经检查了以下链接 但没有找到足够的帮助 所以我发布了这个问
  • 从 DataRow 单元格解析 int [关闭]

    Closed 这个问题是基于意见的 help closed questions 目前不接受答案 如何从 DataRow 单元格解析 int 值 Int32 Parse item QuestionId ToString 这段代码可以工作 但看
  • Type.GetInterfaces() 仅适用于声明的接口

    首先 像这样的问题有很多 也许有些OP甚至在问同样的问题 问题是这些问题的答案 无论是否接受 都没有真正回答这个问题 至少我找不到 如何确定类直接声明的接口 而不是由父级或声明的接口继承的接口 e g interface I interfa
  • 当我的进程被终止时到底会发生什么?

    我有一个包含本机代码和托管代码的混合进程 在 Windows Server 2003 上运行 当我从进程资源管理器中终止进程时 它会进入 100 cpu 的状态 并在消失之前保持这种状态一段时间 有时甚至 10 分钟 在此期间我无法 杀死
  • 从对列表创建邻接列表类型结构

    在 C 中 我有 class Pair int val1 int val2 我有一个来自以下来源的配对列表 List
  • Boost.asio和异步链,unique_ptr?

    我对异步编程不太熟悉 我有一个问题 我的问题如下 给出 boost asio 中 C 11 的 echo server 示例 http www boost org doc libs 1 60 0 doc html boost asio ex

随机推荐

  • git快速创建多个文件夹和文件

    小目标 创建多个文件夹 1 首先切换到想要创建文件夹的位置 点击右键 git Bash Here打开终端 输入下面一串 mkdir departments employees setting salarys social attendanc
  • C++ this指针详解

    this指针 this指针介绍 成员函数的参数和成员变量重名 this区分 在类的普通成员函数中返回对象本身 this指针介绍 1 this 指针是 C 实现封装的一 种机制 它将对象和该对象调用的成员函数连接在一起 在外部看来 每一个对象
  • jar包classes文件进阶版替换

    我相信大家在做项目的时候有时候看源码 或者想改源码代码的时候 一定是修改不了滴 今天教大家一个进阶版的修改jar源码包的方式 80 的人不知道怎么处理 因为通过解压方式 java zcvf 方式会导致清单文件不存在 从而缺少信息而不能打包
  • 全概率公式习题

    简单的记录下关于全概率公式的习题 我们可以先看下抓阄不分先后的一个例子 设袋中装有10个阄 其中8个是白阄 2个是有物之阄 甲 乙二人依次抓取一个 求没人抓得有物之阄的概率 设A B分别为甲 乙抓得有物之阄的事件 显然P A 2 10 下面
  • 【Git】OpenSSL SSL_read: Connection was aborted, errno 10053

    OpenSSL SSL read Connection was aborted errno 10053报错 解决办法 1 Git默认限制推送的大小 运行命令更改限制大小即可 增加缓冲 git config global http postB
  • 用axure给按钮图片等添加点击事件跳转页面

    由于公司产品离职 只能自己做了 汗 整体过程挺好理解的 拖拽一个图片 双击能设置背景图片 然后选中按钮 并双击OnClick 2 在弹出的视图中可以修改点击事件的名字 3 由于我这个是在当前页面弹出二级页面 所以点击Current Wind
  • python tk库

    tk库是 Python 的一个图形用户界面 GUI 库 可以用来创建窗口 菜单 按钮 文本输入框等 GUI 元素 可以通过它在 Python 中创建复杂的图形界面 使用 tk库时 需要先导入 tkinter 模块 然后使用 tkinter
  • python与vb可以互换吗_vb和python混合编程

    展开全部 解题思路 把 Python 设计成带命令行参数调用的形式 在 VB6 中借助 IWshRuntimeLibrary WshShell Exec 方法进行命令行参数调用 通过 IWshRuntimeLibrary WshExec S
  • 闲谈开闭原则——基于UI动画框架

    本文继续聊另外一个设计原则 开闭原则 在UI动画框架中 开闭原则在 动画策略 和 移动算法 这两个类体系中均有所体现 照旧 先看一下开闭原则的定义 1 开闭原则 一个软件实体如类 模块和函数应该对扩展开放 对修改关闭 有人说过 唯一不变的就
  • No artifacts marked for deployment 解决方法(ideaweb项目配置tomcat步骤)

    No artifacts marked for deployment 出现这种情况的原因 创建新项目的时候要先添加freework support 步骤如下 添加完成后点击添加tomcat的位置 有main的情况也不影响 接下来按下面步骤就
  • 【airtest架构】pytest+pocoui+airtest+allure 完成安卓UI自动化框架

    一 背景 为了做app的自动化 由于app元素定位麻烦或者定位不稳定 又或者使用驱动版本等原因 不想使用启动appnium服务的方式 本文采用网易的airtest框架图像识别作为基础 配合pytest进行简单的二次开发形成一套对安卓app
  • 操作系统内存管理4.21

    离散内存管理方案 为了提高内存的利用率 缺点 访问效率下降 分页式内存管理方案 现代操作系统常用方案 分段式内存管理方案 段页式内存管理方案
  • 【Vue】/deep/、>>>、v::deep 三种深度选择器

    在 Vue项目中 当一个组件使用了 scoped 特性时 组件内的样式只会应用于组件自身 但是有时候我们需要在 父组件中改变子组件中的样式 这时就需要用到深度选择器 一 deep 二 gt gt gt search input gt gt
  • uni-app 使用Weex/nvue的注意事项

    介绍 uni app App端内置了一个基于 weex 改进的原生渲染引擎 提供了原生渲染能力 在App端 如果使用vue页面 则使用webview渲染 如果使用nvue页面 native vue的缩写 则使用原生渲染 一个App中可以同时
  • java 日历 获取月份_使用Java日历获取月份和年份的星期

    要使用Calendar类 请导入以下程序包 import java util Calendar 创建一个Calendar类对象 Calendar cal Calendar getInstance 现在 使用以下字段获取月份和年份的星期 Ca
  • 浅析:Spring框架中IOC容器和Bean的配置

    一 IOC和DI的解释 1 IOC Inversion of Control 反转控制 在应用程序中的组件需要获取资源时 传统的方式是组件主动的从容器中获取所需要的资源 在这样的模式下开发人员往往需要知道在具体容器中特定资源的获取方式 增加
  • 深度学习在情感分类中的应用

    简介与背景 情感分类及其作用 情感分类是情感分析的重要组成部分 情感分类是针对文本的情感倾向进行极性分类 分类数量可以是二分类 积极或消极 也可以是多分类 按情感表达的不同程度 情感分析在影音评论 商品评价 舆情分析 股民基金情感分析等都有
  • csgo显示服务器ip,CSGO所有服务器IP段/地理位置

    该楼层疑似违规已被系统折叠 隐藏此楼查看此楼 SDR服务器IP段 atl addresses 162 254 199 170 27015 27050 162 254 199 171 27015 27050 ams addresses 155
  • 【构建ML驱动的应用程序】第 7 章 :使用分类器编写推荐

    大家好 我是Sonhhxg 柒 希望你看完之后 能对你有所帮助 不足请指正 共同学习交流 个人主页 Sonhhxg 柒的博客 CSDN博客 欢迎各位 点赞 收藏 留言 系列专栏 机器学习 ML 自然语言处理 NLP 深度学习 DL fore
  • 【C++/STL】手撕红黑树

    文章目录 1 红黑树的概念 1 1红黑树的性质和规则 2 红黑树的模拟实现 2 1节点的定义 2 2节点的插入 1 情况一 u存在且为红色 2 情况二 u不存在 u存在且为黑 并且g p cur为一条直线 3 情况三 u不存在 u存在且为黑