移情别恋c++ ദ്ദി˶ー̀֊ー́ ) ——13.mapset(模拟实现)

news2024/11/28 9:23:55

1.对红黑树进行改造

1.1treenode模板参数改变

之前构建treenode模板参数传的是class k,class v(set为k,k;map是k,v),现在直接用T代替

template<class T>  //这里直接传了T作为模板参数,T可能是pair<k,t>,也可能是k
struct RBTtreenode
{
	RBTtreenode<T>* _left;
	RBTtreenode<T>* _right;
	RBTtreenode<T>* _parent;

	//pair<K, V> kv;
	T data;
	color col;


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

 

2.构建红黑树的迭代器 

因为要构建const_iterator(不可修改内容) 和iterator(可修改内容)所以需要三个模板参数

//<T,T&,T*> iterator;//普通迭代器
//<T, const T&, const T*> const_iterator;//指向的东西不能改变

template<class T,class Ref,class Ptr>

 iterator内存的是node*类型的数据!!!!

2.1 重载operator*()   (set)

因为set传模板参数只传K,没有Vdata类型是K,

所以用*直接取得data即可

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

 2.2 重载operator->()   (map)

因为map模板参数传的是K,pair<const K,T>,data类型是pair<const K,T>

想取到K,则需要传回&data,再用->first取得K

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

 2.3operator++()与operator--()

 这里以operator++()做解释:

分三种情况:

1.如果右子树不为空,则找到右子树的最左节点

2.//如果右子树为空,且cur是parent的右子树,则先parent回溯至parent->_parent,再_node变为parent
3.//如果右子树为空,且cur是parent的左子树,则_node变为parent

 

iterator& operator++()
{
	if (_node->_right)
	{
		//如果右子树不为空,则找到右子树的最左节点
		node* cur = _node->_right;
		while (cur && cur->_left)
		{
			cur = cur->_left;
		}
		_node = cur;
	}
	else
	{
		//如果右子树为空,且cur是parent的右子树,则先parent回溯至parent->_parent,再_node变为parent
		//如果右子树为空,且cur是parent的左子树,则_node变为parent
		node* cur = _node;
		node* parent = cur->_parent;
		while (parent && cur == parent->_right)
		{
			cur = parent;
			parent = parent->_parent;
		}
		_node = parent;
	}

	return *this;
}

2.4.begin()&&end() 

iterator begin()
{
	node* flag = root;
	while (flag&&flag->_left)//flag可能为nullptr
	{
		flag = flag->_left;
	}
	return iterator(flag);
}


iterator end()
{
	return iterator(nullptr); //end用nullptr去构造!!!!!!!!
}

const_iterator begin() const
{
	node* flag = root;
	while (flag && flag->_left)//flag可能为nullptr
	{
		flag = flag->_left;
	}
	return const_iterator(flag);
}


const_iterator end() const
{
	return const_iterator(nullptr); //end用nullptr去构造!!!!!!!!
}

3.set.h封装

https://cplusplus.com/reference/set/set/?kw=set

#include"rbt.h"
namespace zone
{
	template<class K>
	class set
	{
	public:
		struct setkeyoft //仿函数,用来取出红黑树节点data中的key
		{
			const K& operator()(const K& key)
			{
				return key;
			}
			
		};
		//set这里的迭代器本质都是const_iterator,因为k要求无法修改
		typedef typename RBTtree<K, K, setkeyoft>::const_iterator iterator;//记得要使用typename告诉编译器RBTtree<K, K, setkeyoft>::iterator这个是类型,不是函数
		typedef typename RBTtree<K, K, setkeyoft>::const_iterator const_iterator;

		iterator begin()const
		{
			return it.begin();
		}

		iterator end()const
		{
			return it.end();
		}

		pair<iterator,bool> insert(const K& key)
		{
			return it.insert(key);
		}

		void inorder()
		{
			it.inorder();
		}
	private:
		RBTtree<K,K,setkeyoft> it;
	};
}

3.1 仿函数setkeyoft

仿函数,用来取出红黑树节点data中的key,用于insert函数!!!!

3.2 iterator和const_iterator

//set这里的迭代器本质都是const_iterator,因为k要求无法修改
        typedef typename RBTtree<K, K, setkeyoft>::const_iterator iterator;
        typedef typename RBTtree<K, K, setkeyoft>::const_iterator const_iterator;

4.map.h封装 

https://cplusplus.com/reference/map/map/?kw=map

#include"rbt.h"
namespace zone
{
	template<class K,class T>
	class map
	{
	    public:
			struct setkeyoft
			{
				const K& operator()(const pair<K, T>& key)
				{
					return key.first;
				}
			};

			
			//map这里的迭代器则使用的是iterator,因为k要求无法修改,但v可以修改,所以可以直接初始化时用pair<const K, T>
			typedef typename RBTtree<K, pair<const K, T>, setkeyoft>::iterator iterator;
			typedef typename RBTtree<K, pair<const K, T>, setkeyoft>::const_iterator const_iterator;



			pair<iterator, bool> insert(const pair<K, T>& key)
			{
				return it.insert(key);
			}

			T& operator[](const K& key)
			{
				pair<iterator, bool>ret = insert(make_pair(key,T()));//insert返回一个pair,first是iterator,second是bool类型
				return ret.first->second;
			}

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

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

			void inorder()
			{
				it.inorder();
			}

	    private:
		RBTtree<K,pair<const K,T>, setkeyoft> it;
	};
}

5.insert函数 !!!!!!!

RBT.h里insert函数的返回值是 pair<node*, bool>

但封装过后的map.h,set.h里

pair<iterator,bool> insert(const K& key)
{
	return it.insert(key);
}

 返回值是pair<iterator,bool>

可见 pair<node*, bool>pair<iterator(这里的iterator已经重命名了,本质是const_iteratir),bool>并不是同一类型,该如何解决呢?

 

 

1.如果T1和U类型一致,T2和V类型一致,那么就是拷贝构造!!!

2.如果不一致,也可以进行普通构造前提是有可以用first来构建T1的函数!!!!!

回到刚才的问题:

可见 pair<node*, bool>pair<iterator(这里的iterator已经重命名了,本质是const_iteratir),bool>并不是同一类型,该如何解决呢?

bool类型肯定可以用bool类型初始化,

iterator可以用node*进行初始化吗?

答案是可以的

treeiterator(node* it)
	:_node(it)
{}

相当于使用了隐式类型转换

 6.杂谈

 

类比指针:

1.iterator 可修改指向的数据,也可改变自身

2.const iterator  可修改指向的数据,但不可改变自身

3.const_iterator 不可修改指向的数据,但能改变自身

 7.代码全览

RBT.h

#include<iostream>

using namespace std;

enum color
{
	RED,
	BLACK
};  //列举color的各种可能情况

template<class T>  //这里直接传了T作为模板参数,T可能是pair<k,t>,也可能是k
struct RBTtreenode
{
	RBTtreenode<T>* _left;
	RBTtreenode<T>* _right;
	RBTtreenode<T>* _parent;

	//pair<K, V> kv;
	T data;
	color col;


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

//<T,T&,T*> iterator;//普通迭代器
//<T, const T&, const T*> const_iterator;//指向的东西不能改变:const_iterator,本身不能改变:const iterator
template<class T,class Ref,class Ptr>
struct treeiterator
{
	typedef RBTtreenode<T> node;
	typedef treeiterator<T,Ref,Ptr> iterator;

	node* _node;

	treeiterator(node* it)
		:_node(it)
	{}


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

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

	iterator& operator++()
	{
		if (_node->_right)
		{
			//如果右子树不为空,则找到右子树的最左节点
			node* cur = _node->_right;
			while (cur && cur->_left)
			{
				cur = cur->_left;
			}
			_node = cur;
		}
		else
		{
			//如果右子树为空,且cur是parent的右子树,则先parent回溯至parent->_parent,再_node变为parent
			//如果右子树为空,且cur是parent的左子树,则_node变为parent
			node* cur = _node;
			node* parent = cur->_parent;
			while (parent && cur == parent->_right)
			{
				cur = parent;
				parent = parent->_parent;
			}
			_node = parent;
		}

		return *this;
	}



	iterator& operator--()   //和++反着来即可
	{
		if (_node->_left)
		{
			node* cur = _node->_left;
			while (cur && cur->_right)
			{
				cur = cur->_right;
			}
			_node = cur;
 }
		else
		{
			node* cur = _node;
			node* parent = cur->_parent;
			while (parent && cur == parent->_left)
			{
				cur = parent;
				parent = parent->_parent;
			}
			_node = parent;
		}
		return *this;
	}



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





template<class K, class T,class keyoft>
class RBTtree
{
public:
	typedef treeiterator<T,T&,T*> iterator;
	typedef treeiterator<T, const T&, const T*> const_iterator;//指向的东西不能改变

	typedef RBTtreenode<T> node;



	iterator begin()
	{
		node* flag = root;
		while (flag&&flag->_left)//flag可能为nullptr
		{
			flag = flag->_left;
		}
		return iterator(flag);
	}


	iterator end()
	{
		return iterator(nullptr); //end用nullptr去构造!!!!!!!!
	}

	const_iterator begin() const
	{
		node* flag = root;
		while (flag && flag->_left)//flag可能为nullptr
		{
			flag = flag->_left;
		}
		return const_iterator(flag);
	}


	const_iterator end() const
	{
		return const_iterator(nullptr); //end用nullptr去构造!!!!!!!!
	}



	 pair<node*, bool> insert(const T& _data)//!!!!!!!!!
	{
		if (root == nullptr)
		{
			root = new node(_data);
			root->col = BLACK;//规定根必须是黑的
			return make_pair(root, true);
		}
		node* parent = nullptr; //比bst多了一个parent
		node* cur = root;

		keyoft type;//取出data的K类型的数据

		while (cur)
		{
			parent = cur;
			if (type(cur->data) < type(_data)) //这里取出key再进行比较
			{
				cur = cur->_right;
			}
			else if (type(cur->data) > type(_data))
			{
				cur = cur->_left;
			}
			else
			{
				return make_pair(cur,false);
			}

		}

		cur = new node(_data);
		cur->col = RED;//因为如果插入黑色的会使很多节点的一条路径上的黑色节点增多(相当于得罪了所有人),而插入红色则有可能只得罪父亲(如果父亲是红色的话)
		if (type(parent->data) < type(_data))
		{
			parent->_right = cur;
		}
		else
		{
			parent->_left = cur;
		}
		cur->_parent = parent;

		node* newnode = cur;
		//开始调整
		while (parent && parent->col == RED)//parent为黑不需要调整,如果cur变成root,parent就不存在退出循环
		{
			node* grandparent = parent->_parent;//祖父一定存在,因为只有根节点是没有祖父的,而根节点一定是黑色的
			if (parent == grandparent->_left)
			{
				//      g
				//    p   u
				node* uncle = grandparent->_right;  //父亲在左则叔叔在右
				if (uncle && uncle->col == RED)     //情况一.如果叔叔存在且为红色
				{
					//变色
					parent->col = uncle->col = BLACK;
					grandparent->col = RED;
					//重置cur,parent,继续向上处理
					cur = grandparent;//变为祖父
					parent = cur->_parent;
				}
				else //叔叔不存在或为黑色,旋转加变色
				{
					//   g
					//  p
					// c

					if (cur == parent->_left)  //情况二.单旋
					{

						rotateR(grandparent);
						parent->col = BLACK;
						grandparent->col = RED;
					}

					//   g
					//  p
					//   c

					else      //情况三.cur==parent->_right,双旋
					{
						rotateL(parent);//经历一次左旋后变成情况二!!!!!!!!!!!(cur和parent换位置)
						rotateR(grandparent);
						cur->col = BLACK;
						grandparent->col = RED;
					}

					break;//调整一次就结束了,所以经历过旋转后不需要重置cur,parent,grandparent
				}
			}
			else
			{
				//      g
				//    u   p
				//
				node* uncle = grandparent->_left;  //父亲在右则叔叔在左
				if (uncle && uncle->col == RED)
				{
					parent->col = uncle->col = BLACK;
					grandparent->col = RED;
					//
					cur = grandparent;
					parent = cur->_parent;
				}
				else
				{
					//    g
					//  u   p
					//        c
					if (cur == parent->_right)
					{
						rotateL(grandparent);
						parent->col = BLACK;
						grandparent->col = RED;
					}
					else
					{
						//   g
						// u   p
						//    c
						rotateR(parent);
						rotateL(grandparent);
						cur->col = BLACK;
						grandparent->col = RED;

					}
					break;//调整一次就结束了,所以经历过旋转后不需要重置cur,parent,grandparent
				}
			}
		}


		//1.如果parent和uncle都为RED,则可以一起变黑
		// 2.parent为黑不处理
		// 3.uncle为黑或不存在,parent为红,旋转+变色


		root->col = BLACK;//最后以防万一让根变为黑
		return make_pair(newnode, true);
	}

	void rotateL(node* parent)//左旋,(新节点插入到较高右子树的右侧)//   1.右右
	{
		node* subr = parent->_right;
		node* subrl = subr->_left;

		parent->_right = subrl;
		subr->_left = parent;

		node* ppnode = parent->_parent;
		parent->_parent = subr;

		if (subrl) //subrl可能为空!!!!!!!
		{
			subrl->_parent = parent;
		}

		if (parent == root) //即如果parent->_parent==nullptr
		{
			root = subr;
			subr->_parent = nullptr;
		}

		else
		{
			if (ppnode->_left == parent)
			{
				ppnode->_left = subr;
			}
			else if (ppnode->_right == parent)
			{
				ppnode->_right = subr;
			}

			subr->_parent = ppnode;
		}
	}


	void rotateR(node* parent)//右旋,(新节点插入到较高左子树的左侧)//   2.左左
	{

		node* subl = parent->_left;
		node* sublr = subl->_right;
		parent->_left = sublr;


		if (sublr)               //sublr可能为空!!!!!!!
			sublr->_parent = parent;

		node* ppnode = parent->_parent;

		subl->_right = parent;
		parent->_parent = subl;

		if (root == parent)
		{
			root = subl;
			subl->_parent = nullptr;
		}

		else
		{
			if (ppnode->_left == parent)
			{
				ppnode->_left = subl;
			}
			else if (ppnode->_right == parent)
			{
				ppnode->_right = subl;
			}

			subl->_parent = ppnode;
		}

	}



	void inorder()
	{
		_inorder(root);
	}

	void _inorder(node* root)
	{
		keyoft type;
		if (root == nullptr)
			return;
		_inorder(root->_left);
		cout << type(root->data)<< " ";
		_inorder(root->_right);
	}


	bool check(node* it, int blacknum, int flag)
	{
		if (it == nullptr)
		{
			if (blacknum == flag)
				return true;
			else
				return false;
		}
		else if (it->col == RED && it->_parent->col == RED)//十分巧妙,因为孩子的情况有很多,但父亲不是红就是黑,所以判断父亲更合适
			return false;
		else if (it->col == BLACK)
			blacknum++;
		return check(it->_left, blacknum, flag) && check(it->_right, blacknum, flag);
	}



	bool isbalance()
	{
		return _isbalance(root);
	}

	bool _isbalance(node* root)
	{
		if (root == nullptr)
			return true;
		else if (root->col == RED)
			return false;

		int blacknum = 0;
		int flag = 0;
		node* k = root;
		while (k)
		{
			if (k->col == BLACK)
				flag++;
			k = k->_left;//这里十分巧妙,因为如果为红黑树,从某一节点到空的所有路径上的黑节点数量是一致的,所以可以先随便选一条路径,算出这一条路径上的黑节点数作为基准值,在由递归去和其他路径比较
		}
		return check(root, blacknum, flag);
	}


private:
	node* root = nullptr;
};

myset.h

#include"rbt.h"
namespace zone
{
	template<class K>
	class set
	{
	public:
		struct setkeyoft //仿函数,用来取出红黑树节点data中的key
		{
			const K& operator()(const K& key)
			{
				return key;
			}
			
		};
		//set这里的迭代器本质都是const_iterator,因为k要求无法修改
		typedef typename RBTtree<K, K, setkeyoft>::const_iterator iterator;//记得要使用typename告诉编译器RBTtree<K, K, setkeyoft>::iterator这个是类型,不是函数
		typedef typename RBTtree<K, K, setkeyoft>::const_iterator const_iterator;

		iterator begin()const
		{
			return it.begin();
		}

		iterator end()const
		{
			return it.end();
		}

		pair<iterator,bool> insert(const K& key)
		{
			return it.insert(key);
		}

		void inorder()
		{
			it.inorder();
		}
	private:
		RBTtree<K,K,setkeyoft> it;
	};
}

mymap.h

#include"rbt.h"
namespace zone
{
	template<class K,class T>
	class map
	{
	    public:
			struct setkeyoft
			{
				const K& operator()(const pair<K, T>& key)
				{
					return key.first;
				}
			};

			
			//map这里的迭代器则使用的是iterator,因为k要求无法修改,但v可以修改,所以可以直接初始化时用pair<const K, T>
			typedef typename RBTtree<K, pair<const K, T>, setkeyoft>::iterator iterator;
			typedef typename RBTtree<K, pair<const K, T>, setkeyoft>::const_iterator const_iterator;



			pair<iterator, bool> insert(const pair<K, T>& key)
			{
				return it.insert(key);
			}

			T& operator[](const K& key)
			{
				pair<iterator, bool>ret = insert(make_pair(key,T()));//insert返回一个pair,first是iterator,second是bool类型
				return ret.first->second;
			}

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

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

			void inorder()
			{
				it.inorder();
			}

	    private:
		RBTtree<K,pair<const K,T>, setkeyoft> it;
	};
}

test.cpp

#include<iostream>
#include<vector>
#include<string>

using namespace std;

#include"myset.h"
#include"mymap.h"

void test1()
{
	zone::set<int> it;
	it.insert(1);
	it.insert(3);
	it.insert(5);
	it.insert(2);
	it.insert(4);

	zone::set<int>::iterator arr = it.begin();
	while (arr!=it.end() )
	{
		cout << *arr << " ";
		++arr;
	}
	//it.inorder();

}

void test2()
{
	zone::map<string,string> it;
	it.insert(make_pair("sort","排序"));
	it.insert(make_pair("right", "右"));
	it.insert(make_pair("left", "左"));
	it.insert(make_pair("middle", "中"));
	zone::map<string,string>::iterator arr = it.begin();
	while (arr != it.end())
	{
		arr->second += 'x';//map的v可修改
		cout << arr->first << " ";
		++arr;
	}
	//it.inorder();

}

void test3()
{
	string arr[] = { "香蕉","苹果","西瓜","苹果","苹果","西瓜","苹果"};
	zone::map<string, int> it;
	for (auto e : arr)
	{
		it[e]++;
	}

	for (auto k : it)
	{
		++k.second;
		cout << k.first << ":" << k.second << endl;
	}
}
int main()
{

	test3();
	return 0;
}

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

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

相关文章

【高阶数据结构】深度探索二叉树进阶:二叉搜索树概念及其高效实现

高阶数据结构相关知识点可以通过点击以下链接进行学习一起加油&#xff01; 本章是高阶数据结构笔记的第一篇文章&#xff0c;将分享二叉搜索树的进阶概念及其高效实现的相关知识&#xff0c;欢迎大家阅读&#xff01; &#x1f308;个人主页&#xff1a;是店小二呀 &#x1f3…

五子棋双人对战项目(5)——对战模块

目录 一、需求分析 二、约定前后端交互接口 三、实现游戏房间页面&#xff08;前端代码&#xff09; game_room.html game_room.css srcipt.js 四、实现后端代码 GameAPI Room Mapper 五、线程安全问题 一、需求分析 在对局中&#xff0c;玩家需要知道实时对局情况&…

高阶数据结构-------图

文章目录 图图的基本概念图的存储结构邻接矩阵邻接表 图的遍历广度优先遍历深度优先遍历 最小生成树Kruskal算法Prim算法 最短路径单源最短路径-Dijkstra算法单源最短路径-Bellman-Ford算法多源最短路径-Floyd-Warshall算法 图 图的基本概念 图的基本概念 图是由顶点集合和边的…

【10】纯血鸿蒙HarmonyOS NEXT星河版开发0基础学习笔记-泛型基础全解(泛型函数、泛型接口、泛型类)及参数、接口补充

序言&#xff1a; 本文详细讲解了关于ArkTs语言中的泛型&#xff0c;其中包含泛型函数、泛型接口、泛型约束、泛型类及其中参数的使用方法&#xff0c;补充了一部分接口相关的知识&#xff0c;包括接口的继承和具体实现&#xff0c;也写到了一些边边角角的小知识&#xff0c;剩…

【Linux】进程替换、命令行参数及环境变量(超详解)

目录 进程替换 替换函数的含义 命令行参数 环境变量 PATH 进程替换 我们先看代码&#xff1a; 1 #include<stdio.h>2 #include<unistd.h>3 int main()4 {5 printf("process...begin!\n");6 7 execl("/usr/bin/ls","ls"…

前端面试如何说解vue项目性能优化,你确定不来看看吗?

文末有福利 面试时&#xff0c;很经常会说对某某项目进行了性能优化&#xff0c;使性能有很大的提高之类的话。如果面试官问&#xff0c;来讲讲做了那些优化&#xff0c;这时候你就要很清晰地把你做过的优化一一说出来。 本文谨以自己的Vue项目经验来教你怎么在面试中说优化&am…

【算法与图】通向高效解决方案的钥匙

文章目录 遍历算法BFS&#xff08;广度优先遍历&#xff09;1. 什么是 BFS&#xff1f;2. 特点和应用3. BFS 示例 DFS&#xff08;深度优先搜索&#xff09;1. 什么是 DFS&#xff1f;2. DFS 的基本步骤3. 特点4. DFS 的应用5. DFS 示例 最小生成树问题1. 什么是最小生成树&…

【算法笔记】双指针算法深度剖析

【算法笔记】双指针算法深度剖析 &#x1f525;个人主页&#xff1a;大白的编程日记 &#x1f525;专栏&#xff1a;算法笔记 文章目录 【算法笔记】双指针算法深度剖析前言一.移动零1.1题目1.2思路分析1.3代码实现二.复写零2.1题目2.2思路分析2.3代码实现 三.快乐数3.1题目3…

微服务实战——ElasticSearch(保存)

商品上架——ElasticSearch&#xff08;保存&#xff09; 0.商城架构图 1.商品Mapping 分析&#xff1a;商品上架在 es 中是存 sku 还是 spu &#xff1f; 检索的时候输入名字&#xff0c;是需要按照 sku 的 title 进行全文检索的检索使用商品规格&#xff0c;规格是 spu 的…

基于Springboot+Vue的小区停车场管理系统登录(含源码数据库)

1.开发环境 开发系统:Windows10/11 架构模式:MVC/前后端分离 JDK版本: Java JDK1.8 开发工具:IDEA 数据库版本: mysql5.7或8.0 数据库可视化工具: navicat 服务器: SpringBoot自带 apache tomcat 主要技术: Java,Springboot,mybatis,mysql,vue 2.视频演示地址 3.功能 在这个…

uniapp 微信发布注意事项

uniapp的微信播放不支持本地文件&#xff0c;起始微信原生语言是支持的 所以在编写uniapp代码时 要写两套逻辑 // #ifdef MP-WEIXIN 微信原封不变的自己写法 //#endif // #ifndef MP-WEIXIN 其他写法 //#endif 这样可实现 发布到微信后 微信原封不动的使用自己写…

初识算法 · 双指针(3)

目录 前言&#xff1a; 和为s的两数之和 题目解析&#xff1a; ​编辑 算法原理&#xff1a; 算法编写&#xff1a; 三数之和 题目解析 算法原理 算法编写 前言&#xff1a; 本文通过介绍和为S的两数之和&#xff0c;以及三数之和&#xff0c;对双指针算法进行深一步…

进度条(倒计时)Linux

\r回车(回到当前行开头) \n换行 行缓冲区概念 什么现象&#xff1f; 什么现象&#xff1f;&#xff1f; 什么现象&#xff1f;&#xff1f;&#xff1f; 自己总结&#xff1a; #pragma once 防止头文件被重复包含 倒计时 在main.c中&#xff0c;windows.h是不可以用的&…

Windows 环境搭建 CUDA 和 cuDNN 详细教程

CUDA CUDA&#xff08;Compute Unified Device Architecture&#xff09;是由NVIDIA公司推出的一个并行计算平台和编程模型&#xff0c;它允许开发者使用NVIDIA GPU进行通用计算&#xff08;即GPGPU&#xff09;&#xff0c;从而加速各种计算密集型任务。CUDA提供了一套基于C/C…

linux文件编程_线程

1. 基本概念 1.1. 进程与线程的概念 典型的UNIX/linux进程可以看成是只有一个控制线程&#xff0c;一个进程在同一时刻只做一件事情&#xff0c;有了多个控制线程后&#xff0c;在程序设计时可以把进程设计成在同一时刻做不止一件事&#xff0c;每个线程各自处理独立的任务。…

Web安全 - 文件上传漏洞(File Upload Vulnerability)

文章目录 OWASP 2023 TOP 10导图定义攻击场景1. 上传恶意脚本2. 目录遍历3. 覆盖现有文件4. 文件上传结合社会工程攻击 防御措施1. 文件类型验证2. 文件名限制3. 文件存储位置4. 文件权限设置5. 文件内容检测6. 访问控制7. 服务器配置 文件类型验证实现Hutool的FileTypeUtil使用…

STM32使用Keil5 在运行过程中不复位进入调试模式

一、选择Options for Target进入设置 二、选择所使用的调试器&#xff0c;这里以ST-Link为例。取消勾选Load Application at Startup 可以在进入调试模式的时候不会从新加载程序&#xff01;从而不破坏现场 三、点击Setting进入 四、取消勾选Reset after Connect 使得调试器连接…

探索 aMQTT:Python中的AI驱动MQTT库

文章目录 探索 aMQTT&#xff1a;Python中的AI驱动MQTT库背景介绍aMQTT是什么&#xff1f;如何安装aMQTT&#xff1f;简单库函数使用方法场景应用常见问题及解决方案总结 探索 aMQTT&#xff1a;Python中的AI驱动MQTT库 背景介绍 在物联网和微服务架构的浪潮中&#xff0c;MQ…

Redis:string类型

Redis&#xff1a;string类型 string命令设置与读取SETGETMSETMGET 数字操作INCRINCRBYDECRDECRBYINCRBYFLOAT 字符串操作APPENDSTRLENGETRANGESETRANGE 内部编码intembstrraw 在Redis中&#xff0c;字符串string存储的是二进制&#xff0c;以byte为单位&#xff0c;输入的二进…