C++进阶: 红黑树及map与set封装

红黑树总结整理

红黑色概述:

红黑树整理与AVL树类似,但在对树的平衡做控制时,AVL树会比红黑树更严格。

AVL树是通过引入平衡因子的概念进行对树高度控制。

红黑树则是对每个节点标记颜色,对颜色进行控制。

红黑树控制规则:

1.每个节点不是红色就是黑色

2.根节点必须是黑色

3.如果一个节点是红色,那么此节点的左右孩子节点必须是黑色。也就是说在一条路径上,没有连续的红色节点。

4.对任意一个节点,从该节点到其所有的NULL节点上路径的黑色节点数量均相等。

这样组合的特性是:树的最长路径不会超过最短路径的两倍。

最长路径<=2*最短路径

红黑树保证效率的方式就是遵守红黑树的4条规则。

已知红黑树从根节点到任意路径上的NULL节点中都拥有相同数量的黑色节点。

在极端情况下,一条路径全色黑色节点,我们称之为最短路径用bh表示。

又根据规则3,一个红节点它的左右两个孩子就必须是黑色节点,可以反推,一个红色节点的父亲节点是一个黑色节点。所以最长路径的组成就变成了间隔相邻的红黑节点,共有2bh个。

所以结论就为:

        红黑树的高度差控制在bh<=h<=2bh

红黑树的效率:

红黑树的效率与AVL树相差无几,假设 N为红黑树的节点数量,h为最短路径。

        所以求h就等于log以2为底的N。效率也就是logN级别

红黑树的插入:

红黑树的插入必须遵守四个规则。在插入空树时,根节点为黑色。而在插入非空树时,新增节点的初始颜色必须为红色。这是因为若初始颜色为黑色,会导致某一路径的黑色节点数量多于其他路径,违反规则4,并且很难维护。因此,插入时新节点的颜色应始终为红色。

当我们新插入节点为红色时,我们检查父节点,如果父节点为黑色,那么直接完成插入,如果父节点为红色,就违反了规则3,需要进行特殊处理。

先将新插入的节点标记为 c(cur),那么它的父亲为 p(parent) ,它的爷爷为g(grandfather)它的爷爷绝对是黑色节点,如果不为黑色,那么这颗红黑树早就违反了规则3.

并且还需要g节点找到并标记u(uncle)节点,也就是和p同级的兄弟节点。

情况一:变色

情况二:单旋+变色

c,p节点都为红色,那么u节点除了红色就只有存在且为黑色或不存在两种情况。

如果u节点不存在,那么c是新增节点

如果u存在且为黑色,那么c节点绝对不是叶子节点,c节点下还有子树。只不过是c节点的子树变化导致c变成了红色。

那么如何验证这个结论呢?

观察上图,如果u有节点且为黑色的话,此时就以及违反了规则4,所有路径的黑色节点数量不对等。

当u存在且为黑色的时候,C必然不是新增节点,它只可能是a或b子树里进行变换后当值c变成了红色。所以当我们在进行颜色变化的时候,需要循环向上变化。只有当p节点为黑色才停止。

u不存在或u存在且为黑色时候,我们单纯变色是不行的

会发现,当我们仅仅只是变色的话,就会违反规则4,根到左路径会比根到右路径的黑色节点个数多一。

当我们u为空时候,就需要进行一次右旋,让子树达到平衡后更新p节点为黑色,g节点为红色就能确保4条规则都不触犯,并且因为p已经变成了黑色,就不需要再往上跟新节点。

当u不为空时,c子树必然有黑色节点。同样的我们进行右旋后,将p变为黑色,g变成红色后还是遵守着4条规则。

这里也仅仅介绍右旋的情况,如果是在左边的话就同理进行左旋加变色,

情况三:双选+变色

c节点也不会一直如我们所愿,只插入在绝对的左边或绝对的右边,这时候就需要经过两次旋转,才能完成红黑树的变化。

当c在p的右边的时候就必须要通过双选,当c转完两次的时候,c来到最上层,此时将c节点变为黑色,将g节点变为红色,c又恰好为上层节点,就可以直接退出循环。

当u不为空且为黑色时

同样的,u为黑色那么c节点肯定不是新插入的节点,c原本为黑色节点。因为新插入的节点在c的子树内,导致子树的变化影响了C节点从黑色变为红色才发生的双选+变色。

        最后戳了c在左子树,c的情况也会在右子树出现,所实现的方法也是也要的,并不需要特别说明举例。

        以下是红黑树的代码

#pragma once
#include<iostream>
using namespace std;
enum Color { red ,black };

template<class K, class V>
struct TreeNode
{
	pair<K, V> _val;
	TreeNode* _left;
	TreeNode* _right;
	TreeNode* _parent;
	Color _col;
	TreeNode(const pair<K, V>& val)
		:_val(val)
		,_left(nullptr)
		,_right(nullptr)
		,_parent(nullptr)
		,_col(red)
	{}
};


template<class K,class V>
class RBTree
{
	typedef TreeNode<K, V> Node;
public:
	bool Insert(const pair<K,V> &val)
	{
		if(_root==nullptr)
		{
			_root = new Node(val);
			_root->_col = black;
			return true;
		}

		Node* parent = nullptr;
		Node* cur = _root;
		while (cur)
		{
			parent = cur;
			if(cur->_val.first>val.first)
			{
				cur = cur->_left;
			}
			else if (cur->_val.first < val.first)
			{
				cur = cur->_right;
			}
			else
			{
				return false;
			}
		}
		
		cur = new Node(val);
		if (parent->_val.first>cur->_val.first)
		{
			parent->_left = cur;
		}
		else
		{
			parent->_right = cur;
		}
		cur->_parent = parent;

		//判断红黑树是否要更新
		while (parent&&parent->_col!=black)
		{
			Node* grandfather = parent->_parent;
			Node* uncle;
			if (grandfather->_left==parent)
			{
				uncle = grandfather->_right;
				if (uncle&&uncle->_col==red)//情况一:只变色
				{
					grandfather->_col = red;
					uncle->_col = parent->_col = black;
					cur = grandfather;
					parent = cur->_parent;
				}
				else
				{
					if (parent->_left==cur)//情况二:单旋+变色
					{
						TurnR(grandfather);
						grandfather->_col = red;
						parent->_col = black;
						break;
					}
					else//情况三:双循+变色
					{
						TurnL(parent);
						TurnR(grandfather);
						cur->_col = black;
						grandfather->_col = red;
						break;
					}

				}

			}
			else
			{
				uncle = grandfather->_left;
				if (uncle && uncle->_col == red)//情况一:只变色
				{
					grandfather->_col = red;
					uncle->_col = parent->_col = black;
					cur = grandfather;
					parent = cur->_parent;
				}
				else
				{
					if (parent->_right == cur)//情况二:单旋+变色
					{
						TurnL(grandfather);
						grandfather->_col = red;
						parent->_col = black;
						break;
					}
					else//情况三:双循+变色
					{
						TurnR(parent);
						TurnL(grandfather);
						cur->_col = black;
						grandfather->_col = red;
						break;
					}

				}

			}

		}
		_root->_col = black;

	}

	bool Check(Node*_root,int ibnum,const int rnum)
	{
		if (_root==nullptr)
		{
			if (ibnum != rnum) 
			{
				cout << "存在黑色结点的数量不相等的路径" << endl;
				return false;
			}
	
		return true;
		}
	
		if (_root->_col==red&&_root->_parent->_col==red)
		{
			cout << _root->_val.first<< "存在连续的红色结点" << endl;
			return false;
		}
	
		if (_root->_col==black)
		{
			++ibnum;
		}
		return Check(_root->_left, ibnum, rnum) && Check(_root->_right, ibnum, rnum);
	}
	
	
	bool IsBalance()
	{
	
		if (_root==nullptr||_root->_col==red)
		{
			return false;
		}
	
		int rnum = 0;
		Node* cur = _root;
		while (cur)
		{
			if (cur->_col == black)
				rnum++;
			cur = cur->_left;
		}
	
		return Check(_root, 0, rnum);
	}


		// blackNum 根到当前结点的黑色结点的数量
	//bool Check(Node* root, int blackNum, const int refNum)
	//{
	//	if (root == nullptr)
	//	{
	//		// 前序遍历走到空时,意味着一条路径走完了
	//		//cout << blackNum << endl;
	//		if (refNum != blackNum)
	//		{
	//			cout << "存在黑色结点的数量不相等的路径" << endl;
	//			return false;
	//		}

	//		return true;
	//	}

	//	// 检查孩子不太方便,因为孩子有两个,且不一定存在,反过来检查父亲就方便多了
	//	if (root->_col == red && root->_parent->_col == red)
	//	{
	//		cout << root->_val.first << "存在连续的红色结点" << endl;
	//		return false;
	//	}

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

	//	return Check(root->_left, blackNum, refNum)
	//		&& Check(root->_right, blackNum, refNum);
	//}

	//bool IsBalance()
	//{
	//	if (_root == nullptr)
	//		return true;

	//	if (_root->_col == red)
	//		return false;

	//	// 参考值
	//	int refNum = 0;
	//	Node* cur = _root;
	//	while (cur)
	//	{
	//		if (cur->_col == black)
	//		{
	//			++refNum;
	//		}
	//		cur = cur->_left;
	//	}

	//	return Check(_root, 0, refNum);
	//}


	void InOrder()
	{
		_InOrder(_root);
	}

protected:

	void _InOrder(Node*cur)
	{
		if (cur==nullptr)
		{
			return;
		}

		_InOrder(cur->_left);
		cout << "first:" << cur->_val.first << " second:" << cur->_val.second << endl;
		_InOrder(cur->_right);
	}

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

		parent->_left = subLR;
		subL->_right = parent;

		if (subLR)
		{
			subLR->_parent = parent;
		}

		Node* Pparent = parent->_parent;
		parent->_parent = subL;
		if (Pparent)
		{
			if (Pparent->_left == parent)
			{
				Pparent->_left = subL;
			}
			else
			{
				Pparent->_right = subL;
			}
		}
		subL->_parent = parent;
	}

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

		parent->_right = subRL;
		subR->_left = parent;

		if (subRL) { subRL->_parent = parent; }

		Node* Pparent = parent->_parent;
		parent->_parent = subR;
		if (Pparent)
		{
			if (Pparent->_left == parent)
			{
				Pparent->_left = subR;
			}
			else
			{
				Pparent->_right = subR;
			}
		}
		subR->_parent = parent;
	}

private:
	Node* _root=nullptr;
};

封装map与set:

        在map与set中都是对底层的红黑树进行上层的封装。

        封装后的红黑树:

#pragma once
#include<iostream>
using namespace std;

enum color
{
	red,
	black
};

template<class T>
struct TreeNode
{
	TreeNode(const T&ky)
		:_ky(ky)
		,_col(red)
		,_parent(nullptr)
		,_left(nullptr)
		,_right(nullptr)
	{}
	T _ky;
	color _col;
	TreeNode* _parent;
	TreeNode* _left;
	TreeNode* _right;
};


template<class T, class Ref,class Ptr >
struct Tree_Iterator
{
	typedef TreeNode<T> Node;
	typedef Tree_Iterator<T,Ref,Ptr> Self;

	Tree_Iterator(Node* node, Node* root)
		:_node(node)
		, _root(root)
	{}

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

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

	Self&operator++()
	{
		if (_node->_right)
		{
			Node* minleft = _node->_right;
			while (minleft->_left)
			{
				minleft = minleft->_left;
			}
			_node = minleft;
		}
		else
		{
			Node* cur = _node;
			Node* parent = cur->_parent;
			while (parent&&parent->_right==cur)
			{
				cur = parent;
				parent = parent->_parent;
			}
			_node = parent;
		}
		return *this;
	}

	Self &operator --()
	{
		if (_node==nullptr)
		{
			Node* cur = _root;
			while (cur&&cur->_right)
			{
				cur = cur->_right;
			}
			_node = cur;
		}
		else if (_node->_left)
		{
			Node* maxright = _node->_left;
			while (maxright->_right)
			{
				maxright = maxright->_right;
			}
			_node = maxright;
		}
		else
		{
			Node* cur = _node;
			Node* parent = cur->_parent;
			while (parent && parent->_left == cur)
			{
				cur = parent;
				parent = parent->_parent;
			}
			_node = parent;
		}
		return *this;
	
	}

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

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

	Node* _node;
	Node* _root;

};


template<class K,class T,class KeyOFT>
class RBTree
{
	typedef TreeNode<T> Node;
public:
	typedef Tree_Iterator<T,T&,T*> Iterator;
	typedef Tree_Iterator<T, const T&, const T*> Const_Iterator;

	Iterator Begin()
	{
		Node* cur = _root;
		while (cur&&cur->_left)
		{
			cur = cur->_left;
		}
		return Iterator(cur, _root);
	}

	Iterator End()
	{
		return Iterator(nullptr, _root);
	}

	Const_Iterator CBegin()const 
	{
		Node* cur = _root;
		while (cur && cur->_left)
		{
			cur = cur->_left;
		}
		return Iterator(cur, _root);
	}

	Const_Iterator CEnd()const 
	{
		return Iterator(nullptr, _root);
	}


	pair<Iterator,bool> Insert(const T&ky)
	{
		if(!_root)
		{
			_root = new Node(ky);
			_root->_col = black;
			return make_pair(Iterator(_root,_root),true);
		}

		Node* cur = _root;
		Node* parent = nullptr;
		KeyOFT kot;

		while (cur)
		{
			if (kot(cur->_ky) > kot(ky))
			{
				parent = cur;
				cur = cur->_left;
			}
			else if (kot(cur->_ky) < kot(ky))
			{
				parent = cur;
				cur = cur->_right;
			}
			else
			{
				return make_pair(Iterator(cur, _root), false);;
			}
		}

		cur = new Node(ky);
		Node* node = cur;

		if (kot(parent->_ky)>kot(cur->_ky))
		{
			parent->_left = cur;
		}
		else
		{
			parent->_right = cur;
		}

		cur->_parent = parent;

		while (parent&&parent->_col==red)
		{
			Node* garendparent = parent->_parent;
			Node* uncle;
			if (garendparent->_left==parent)//只变色
			{
				uncle = garendparent->_right;
				if (uncle&&uncle->_col==red)
				{
					uncle->_col = parent->_col = black;
					garendparent->_col = red;
					cur = garendparent;
					parent = cur->_parent;
				}
				else 
				{
					if (parent->_left==cur)//单选变色
					{
						TurnR(garendparent);
						parent->_col = black;
						cur->_col = garendparent->_col = red;
						break;
					}
					else//双旋变色
					{
						TurnL(parent);
						TurnR(garendparent);
						cur->_col = black;
						parent->_col = garendparent->_col = red;
						break;
					}
				}
			}
			else
			{
				uncle = garendparent->_left;
				if (uncle && uncle->_col == red)
				{
					uncle->_col = parent->_col = black;
					garendparent->_col = red;
					cur = garendparent;
					parent = cur->_parent;
				}
				else
				{
					if (parent->_right == cur)//单选变色
					{
						TurnL(garendparent);
						parent->_col = black;
						cur->_col = garendparent->_col = red;
						break;
					}
					else//双旋变色
					{
						TurnR(parent);
						TurnL(garendparent);
						cur->_col = black;
						parent->_col = garendparent->_col = red;
						break;
					}
				}
			}
		}
		_root->_col = black;
		return make_pair(Iterator(node, _root), true);
	}

	Node*Find(const K &val)
	{
		Node* cur = _root;
		KeyOFT kot;
		while (cur)
		{
			if (kot(cur->_ky) > val)
			{
				cur = cur->_left;
			}
			else if (kot(cur->_ky) < val)
			{
				cur = cur->_right;
			}
			else
			{
				return cur;
			}
		}
		return nullptr;
	}

private:
	void TurnR(Node*parent)
	{
		KeyOFT kot;
		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 (!Pparent)
		{
			subL->_parent = nullptr;
			_root = subL;
		}
		else
		{
			if (kot(Pparent->_ky)> kot(subL->_ky))
			{
				Pparent->_left = subL;
			}
			else
			{
				Pparent->_right = subL;
			}
			subL->_parent = Pparent;
		}
		
	}

	void TurnL(Node*parent)
	{
		KeyOFT kot;
		Node* subR = parent->_right;
		Node* subRL = subR->_left;
		parent->_right = subRL;
		if (subRL)
		{
			subRL->_parent = parent;
		}

		subR->_left = parent;
		Node* Pparent = parent->_parent;
		parent->_parent = subR;

		if (!Pparent)
		{
			subR->_parent = nullptr;
			_root = subR;
		}
		else
		{
			if (kot(Pparent->_ky) > kot(subR->_ky))
			{
				Pparent->_left = subR;
			}
			else
			{
				Pparent->_right = subR;
			}
			subR->_parent = Pparent;
		}
	
	}


	Node* _root = nullptr;
};

        对于map与set在底层用的都是同一颗红黑树,只是通过类模板进行了实例化两份,所以在红黑树的自身的封装中需要改动以下措施:

        1.树节点存储的值只用一个T类,因为编译器不知道要存的是Key模型,还是pair模型。

        2.为红黑树添加迭代器,map与set都支持迭代器访问数据。需要重点注意的是迭代器的++与迭代器的--,这也是封装后的红黑树的重点

                

Self& operator++()(前置递增)

  1. 当前节点有右子树

    • 找到右子树中的最左节点(即右子树中的最小节点),该节点即为当前节点的后继。

    • 例如,若当前节点为 A,右子树为 C,则后继是 C 的最左子节点(假设为 D

    if (_node->_right) {
        Node* minleft = _node->_right;
        while (minleft->_left) {
            minleft = minleft->_left;
        }
        _node = minleft;
    }
  2. 当前节点无右子树

    • 从当前节点向上回溯,找到第一个祖先节点,使得当前节点位于该祖先节点的左子树中。

    • 例如,若当前节点为 E,其父节点为 B,祖父节点为 A,则 A 是 E 的后继。

    else {
        Node* cur = _node;
        Node* parent = cur->_parent;
        while (parent && parent->_right == cur) {
            cur = parent;
            parent = parent->_parent;
        }
        _node = parent; // 找到的祖先节点即为后继

Self& operator--()(前置递减)

  1. 当前节点为 nullptr(如 end() 迭代器)

    • 找到树中的最右节点(即最大值节点),作为前驱。

    • 例如,若树为 A(B(D, E), C),则最右节点为 C

    if (_node == nullptr) {
        Node* cur = _root;
        while (cur && cur->_right) {
            cur = cur->_right;
        }
        _node = cur; // 指向最大值节点
    }
  2. 当前节点有左子树

    • 找到左子树中的最右节点(即左子树中的最大节点),该节点即为当前节点的前驱。

    • 例如,若当前节点为 A,左子树为 B,则前驱是 B 的最右子节点 E

    else if (_node->_left) {
        Node* maxright = _node->_left;
        while (maxright->_right) {
            maxright = maxright->_right;
        }
        _node = maxright;
    }

        还需要注意对于为什么有KeyOFT,是因为我们红黑树在插入时需要进行比较大小,而map插入的是一个pair,而set插入的就只是Key。红黑树本身是并不知道该拿什么进行比较。所以这里传入一个KeyOFT,是上层map或set传入的一个函数模板,map就通过类模板过滤进行pair的first进行比较,set通过自身类模板比较的还是Key。

        封装map:

                map的封装也并没有太大难度,上述写道的类模板比较需要自己写,并且在定义成员变量红黑树的时,第二个类模板应传入的是pair,而不是V。并且map是支持运算符重载[ ],所以我们这里也同样支持,其余的也只是对红黑树的再一次调用。

#pragma once
#include"RBTree.h"

namespace cat
{
	template<class K,class V >
	class mymap
	{
		struct MapOFT
		{
			const K &operator ()(const pair<K, V>& kv)
			{
				return kv.first;
			}
		};
	public:
		typedef typename RBTree<K, pair<const K, V>, MapOFT>::Iterator iterator;
		typedef typename RBTree<K, pair<const K,const V>, MapOFT>::Const_Iterator const_iterator;
		pair<iterator, bool> insert(const pair<K,V> &kv)
		{
			return _rbt.Insert(kv);
		}

		iterator begin()
		{
			return _rbt.Begin();
		}

		iterator end()
		{
			return _rbt.End();
		}


		const_iterator cbegin() const 
		{
			return _rbt.CBegin();
		}

		const_iterator cend()const 
		{
			return _rbt.CEnd();
		}

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


	private:
		RBTree< K, pair<const K,V>, MapOFT> _rbt;
	};

	void test_map()
	{
		mymap<int, int> m;
		m.insert({ 1,1 });
		m.insert({ 5,5 });
		m.insert({ 2,2 });
		m.insert({ 6,6 });

		mymap<int, int>::iterator it = m.begin();
		while (it != m.end())
		{
			//it->first += 1;
			it->second += 1;

			cout << it->first << ":" << it->second << endl;
			++it;
		}

		mymap<string, string> dict;
		dict.insert({ "sort", "排序" });
		dict.insert({ "left", "左边" });
		dict.insert({ "right", "右边" });
		dict["left"] = "左边,剩余";
		dict["insert"] = "插入";
		dict["string"];

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

		string arr[] = { "苹果", "西瓜", "苹果", "西瓜", "苹果", "苹果", "西瓜",
	"苹果", "香蕉", "苹果", "香蕉" };

		mymap<string, int> countMap;
		for (const auto& str : arr)
		{
			countMap[str]++;
		}

		for (const auto& e : countMap)
		{
			cout << e.first << ":" << e.second << endl;
		}
		cout << endl;
	}

}

        封装Set

#pragma once
#include"RBTree.h"

namespace cat
{
	template<class K>
	class myset
	{
		struct SetOFT
		{
			const K& operator()(const K& ky)
			{
				return ky;
			}
		};
	public:
		typedef typename RBTree<K, K, SetOFT>::Iterator iterator;
		typedef typename RBTree<K, K, SetOFT>::Const_Iterator const_iterator;

		pair<iterator,bool> insert(const K& ky)
		{
			return _rbt.Insert(ky);
		}

		iterator begin()
		{
			return _rbt.Begin();
		}

		iterator end()
		{
			return _rbt.End();
		}

		const_iterator begin() const 
		{
			return _rbt.CBegin();
		}

		const_iterator end() const
		{
			return _rbt.CEnd();
		}

	private:
		RBTree<K, K, SetOFT> _rbt;

	};

	void test_set()
	{
		myset<int> s;
		/*s.insert(4);
		s.insert(1);
		s.insert(5);
		s.insert(3);*/

		int a[] = { 4, 2, 6, 1, 3, 5, 15, 7, 16, 14 };
		for (auto e : a)
		{
			if(e==14)
			{
				int x = 1;
			}
			s.insert(e);
		}

		myset<int>::iterator it = s.begin();
		// *it += 10;
		while (it != s.end())
		{
			cout << *it << " ";
			++it;
		}
		cout << endl;

		it = s.end();
		while (it != s.begin())
		{
			--it;
		
			cout << *it << " ";
		}
		cout << endl;
	}
}

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

BUUCTF_[网鼎杯 2020 朱雀组]phpweb(反序列化绕过命令)

打开靶场&#xff0c;,弹出上面的提示,是一个警告warning,而且页面每隔几秒就会刷新一次,根据warning中的信息以及信息中的时间一直在变,可以猜测是date()函数一直在被调用 查看页面源代码&#xff0c;没有什么有用的信息 Burp抓包一下 调用了date()函数并回显在页面上,参数fu…

pandas(二)读取数据

一、读取数据 示例代码 import pandaspeople pandas.read_excel(../002/People.xlsx) #读取People数据 print(people.shape) # 打印people表的行数、列数 print(people.head(3)) # 默认打印前5行,当前打印前3行 print("") print(people.tail(3)) # 默…

智慧物业管理系统实现社区管理智能化提升居民生活体验与满意度

内容概要 智慧物业管理系统&#xff0c;顾名思义&#xff0c;是一种将智能化技术融入社区管理的系统&#xff0c;它通过高效的手段帮助物业公司和居民更好地互动与沟通。首先&#xff0c;这个系统整合了在线收费、停车管理等功能&#xff0c;让居民能够方便快捷地完成日常支付…

二十三、集合类

Ⅰ . Set 类 01 Set 介绍 template < class T, // set::key_type/value_typeclass Compare less<T>, // set::key_compare/value_compareclass Alloc allocator<T> // set::allocator_type> class set; 通过插入新的元素来扩…

5.5.1 面向对象的基本概念

文章目录 基本概念面向对象的5个原则 基本概念 面向对象的方法&#xff0c;特点时其分析与设计无明显界限。虽然在软件开发过程中&#xff0c;用户的需求会经常变化&#xff0c;但客观世界对象间的关系是相对稳定的。对象是基本的运行实体&#xff0c;由数据、操作、对象名组成…

在线免费快速无痕去除照片海报中的文字logo

上期和大家分享了用photoshop快速无痕去除照片海报中的文字logo的方法&#xff0c;有的同学觉得安装PS太麻烦&#xff0c;有那下载安装时间早都日落西山了&#xff0c;问有没有合适的在线方法可以快速去除&#xff1b;达芬奇上网也尝试了几个网站&#xff0c;今天分享一个对国人…

Linux网络 | 网络层IP报文解析、认识网段划分与IP地址

前言&#xff1a;本节内容为网络层。 主要讲解IP协议报文字段以及分离有效载荷。 另外&#xff0c; 本节也会带领友友认识一下IP地址的划分。 那么现在废话不多说&#xff0c; 开始我们的学习吧&#xff01;&#xff01; ps&#xff1a;本节正式进入网络层喽&#xff0c; 友友们…

【深度学习】DeepSeek模型介绍与部署

原文链接&#xff1a;DeepSeek-V3 1. 介绍 DeepSeek-V3&#xff0c;一个强大的混合专家 (MoE) 语言模型&#xff0c;拥有 671B 总参数&#xff0c;其中每个 token 激活 37B 参数。 为了实现高效推理和成本效益的训练&#xff0c;DeepSeek-V3 采用了多头潜在注意力 (MLA) 和 De…

STM32 PWM驱动舵机

接线图&#xff1a; 这里将信号线连接到了开发板的PA1上 代码配置&#xff1a; 这里的PWM配置与呼吸灯一样&#xff0c;呼吸灯连接的是PA0引脚&#xff0c;输出比较单元用的是OC1通道&#xff0c;这里只需改为OC2通道即可。 完整代码&#xff1a; #include "servo.h&quo…

51单片机 02 独立按键

一、独立按键控制LED亮灭 轻触按键&#xff1a;相当于是一种电子开关&#xff0c;按下时开关接通&#xff0c;松开时开关断开&#xff0c;实现原理是通过轻触按键内部的金属弹片受力弹动来实现接通和断开。 #include <STC89C5xRC.H> void main() { // P20xFE;while(1){…

本地部署 DeepSeek-R1:简单易上手,AI 随时可用!

&#x1f3af; 先看看本地部署的运行效果 为了测试本地部署的 DeepSeek-R1 是否真的够强&#xff0c;我们随便问了一道经典的“鸡兔同笼”问题&#xff0c;考察它的推理能力。 &#x1f4cc; 问题示例&#xff1a; 笼子里有鸡和兔&#xff0c;总共有 35 只头&#xff0c;94 只…

[EAI-027] RDT-1B,目前最大的用于机器人双臂操作的机器人基础模型

Paper Card 论文标题&#xff1a;RDT-1B: a Diffusion Foundation Model for Bimanual Manipulation 论文作者&#xff1a;Songming Liu, Lingxuan Wu, Bangguo Li, Hengkai Tan, Huayu Chen, Zhengyi Wang, Ke Xu, Hang Su, Jun Zhu 论文链接&#xff1a;https://arxiv.org/ab…

DeepSeek为什么超越了OpenAI?从“存在主义之问”看AI的觉醒

悉尼大学学者Teodor Mitew向DeepSeek提出的问题&#xff0c;在推特上掀起了一场关于AI与人类意识的大讨论。当被问及"你最想问人类什么问题"时&#xff0c;DeepSeek的回答直指人类存在的本质&#xff1a;"如果意识是进化的偶然&#xff0c;宇宙没有内在的意义&a…

在 crag 中用 LangGraph 进行评分知识精炼-下

在上一次给大家展示了基本的 Rag 检索过程&#xff0c;着重描述了增强检索中的知识精炼和补充检索&#xff0c;这些都是 crag 的一部分&#xff0c;这篇内容结合 langgraph 给大家展示通过检索增强生成&#xff08;Retrieval-Augmented Generation, RAG&#xff09;的工作流&am…

UE5.3 C++ CDO的初步理解

一.UObject UObject是所有对象的基类&#xff0c;往上还有UObjectBaseUtility。 注释&#xff1a;所有虚幻引擎对象的基类。对象的类型由基于 UClass 类来定义。 这为创建和使用UObject的对象提供了 函数&#xff0c;并且提供了应在子类中重写的虚函数。 /** * The base cla…

知识库管理在提升企业决策效率与知识共享中的应用探讨

内容概要 知识库管理是指企业对内部知识、信息进行系统化整理和管理的过程&#xff0c;其重要性在于为企业决策提供了坚实的数据支持与参考依据。知识库管理不仅能够提高信息的获取速度&#xff0c;还能有效减少重复劳动&#xff0c;提升工作效率。在如今快速变化的商业环境中…

Linux:线程池和单例模式

一、普通线程池 1.1 线程池概念 线程池&#xff1a;一种线程使用模式。线程过多会带来调度开销&#xff0c;进而影响缓存局部性和整体性能。而线程池维护着多个线程&#xff0c;等待着监督管理者分配可并发执行的任务。这避免了在处理短时间任务时创建与销毁线程的代价&…

AJAX笔记原理篇

黑马程序员视频地址&#xff1a; AJAX-Day03-01.XMLHttpRequest_基本使用https://www.bilibili.com/video/BV1MN411y7pw?vd_source0a2d366696f87e241adc64419bf12cab&spm_id_from333.788.videopod.episodes&p33https://www.bilibili.com/video/BV1MN411y7pw?vd_sour…

ComfyUI安装调用DeepSeek——DeepSeek多模态之图形模型安装问题解决(ComfyUI-Janus-Pro)

ComfyUI 的 Janus-Pro 节点&#xff0c;一个统一的多模态理解和生成框架。 试用&#xff1a; https://huggingface.co/spaces/deepseek-ai/Janus-1.3B https://huggingface.co/spaces/deepseek-ai/Janus-Pro-7B https://huggingface.co/spaces/deepseek-ai/JanusFlow-1.3B 安装…

3D图形学与可视化大屏:什么是材质属性,有什么作用?

一、颜色属性 漫反射颜色 漫反射颜色决定了物体表面对入射光进行漫反射后的颜色。当光线照射到物体表面时&#xff0c;一部分光被均匀地向各个方向散射&#xff0c;形成漫反射。漫反射颜色的选择会直接影响物体在光照下的外观。例如&#xff0c;一个红色的漫反射颜色会使物体在…