STL之优先级队列(堆)的模拟实现与仿函数(8千字长文详解!)

news2024/11/8 8:58:58

STL之优先级队列(堆)的模拟实现与仿函数

文章目录

  • STL之优先级队列(堆)的模拟实现与仿函数
    • 优先级队列的概念
    • priority_queue的接口介绍
      • 优先级队列的构造函数
    • priority_queue模拟实现
      • 类成员
      • 构造函数
        • 向下调整算法——正常实现
      • push
        • 向上调整——正常实现
      • pop
    • 仿函数/函数对象
      • 仿函数的概念
      • 仿函数的作用
    • 使用仿函数来实现优先级队列的逻辑判断
      • 新增模板参数
      • 使用仿函数实现泛型的向下调整算法与向上调整算法
    • 优先级队列模拟——最终版代码
    • 仿函数的应用
        • STL中实现的比较
        • STL中的建堆算法

优先级队列的概念

  1. 优先队列是一种容器适配器,根据严格的弱排序标准,它的第一个元素总是它所包含的元素中最大的

    本质就是一个堆!

  2. 此上下文类似于堆,在堆中可以随时插入元素,并且只能检索最大堆元素(优先队列中位于顶部的元素)。

  3. . 优先队列被实现为容器适配器,容器适配器即将特定容器类封装作为其底层容器类,queue提供一组特定的成员函数来访问其元素。元素从特定容器的“尾部”弹出,其称为优先队列的顶部。

  4. 底层容器可以是任何标准容器类模板,也可以是其他特定设计的容器类。容器应该可以通过随机访问迭代器访问,并支持以下操作

    • empty():检测容器是否为空
    • size():返回容器中有效元素个数
    • front():返回容器中第一个元素的引用
    • push_back():在容器尾部插入元素
    • pop_back():删除容器尾部元素
  5. 标准容器类vector和deque满足这些需求默认情况下,如果没有为特定的priority_queue类实例化指定容器类,则使用vector

  6. 需要支持随机访问迭代器,以便始终在内部保持堆结构。容器适配器通过在需要时自动调用算法函数make_heap、push_heap和pop_heap来自动完成此操作
    list不支持随机访问!所以不能当做其标准容器类!
    image-20230404085935472

priority_queue的接口介绍

image-20230404091043943

pop:删除堆顶的数据

top:取出堆顶的数据

push:向堆插入数据

size:返回堆里面的元素个数

empty:判断堆是否为空

swap:交换两个堆

优先级队列的构造函数

image-20230404103606290

可以使用迭代器进行初始化!

但是这个迭代器是一个随机迭代器!

image-20230404093514444

//简单使用
#pragma once
#include <queue>
#include<iostream>
#include <functional>//less和greater的头文件
using namespace std;
int main()
{
	//大堆
	priority_queue <int> pq;
	pq.push(3);
	pq.push(1);
	pq.push(2);
	pq.push(5);
	while (!pq.empty())
	{
		cout << pq.top() << ' ';
		pq.pop();
	}
	cout << endl;

	//小堆
	priority_queue <int,vector<int>,greater<int>> pq1;
	pq1.push(3);
	pq1.push(1);
	pq1.push(2);
	pq1.push(5);
	while (!pq1.empty())
	{
		cout << pq1.top() << ' ';
		pq1.pop();
	}
	cout << endl;
	return 0;
} 

image-20230404101642622

priority_queue模拟实现

容器适配器是不需要迭代器的!如果有了迭代器是不能保证最大/最小的先出!

类成员

namespace MySTL
{
	template<class T,class Container = std::vector<T>>
	class priority_queue
	{
	private:
		Container _con;
	};
}

构造函数

namespace MySTL
{
	template<class T,class Container = std::vector<T>>
	class priority_queue
	{
	public:
		priority_queue()
		{}
		template<class InputIterator>
		priority_queue(InputIterator first,InputIterator last)
			:_con(first,last)//直接调用vector的构造
		{
			//此时还不是堆!
			//向下调整建堆!
			for (int i = (_con.size() - 2) / 2; i >= 0; i++)
			{
				adjust_down(i);
			}
		}
	private:
		Container _con;
	};
}

向下调整算法——正常实现

namespace MySTL
{
	template<class T,class Container = std::vector<T>>
	class priority_queue
	{
	private:
		void adjust_down(size_t parent)
		{
			size_t  child = parent*2 +1;
			while (child < _con.size())
			{
				if (child + 1 < _con.size() && _con[child] < _con[child + 1])
					child++;

				if (_con[parent] < _con[child])//建大堆
				{
					std::swap(_con[parent], _con[child]);
					parent = child;
					child = parent * 2 + 1;
				}
				else
				{
					break;//没有更大的了,退出
				}
			}
		}
	private:
		Container _con;
	};
}

向下调整算法的核心将父节点和自己的两个子节点对比,找到其中一个最大(大堆)的或者最小(小堆)的子节点!然后比较,如果是建大堆,若父节点比子节点还要小,那么交换!如果是建小堆,若父节点比子节点还要大,那么交换!,直到父节点比最大子节点还要大(大堆),或者是比最小的子节点还要小(小堆)则跳出循环!或者直接调整到最后一个节点!

push

namespace MySTL
{
	template<class T,class Container = std::vector<T>>
	class priority_queue
	{
	public:
		void push(const T& value)
		{
			//插入后要保持堆的结构!
			//要进行向上调整!
			_con.push_back(value);
			adjust_up(_con.size()-1);
		}
	private:
		Container _con;
	};
}

向上调整——正常实现

namespace MySTL
{
	template<class T,class Container = std::vector<T>>
	class priority_queue
	{
	private:
		void adjust_up(size_t child)
		{
			size_t parent = (child - 1) / 2;
			while (child > 0)
			{
				if (_con[child] > _con[parent])//建大堆!
				{
					std::swap(_con[child], _con[parent]);
					child = parent;
					parent = (child - 1) / 2;
				}
				else
				{
					break;
				}
			}
		}
	private:
		Container _con;
	};
}
//这里如果使用parent>0,那么因为子节点还在下一个位置,则无法调整根节点
//如果是parent>=0 因为parent =(0-1)/2 仍然为0,则就会出现死循环!

向上调整,就是将现在子节点和父节点对比一下!然后如果是建大堆,若子节点大于父节点那么进行交换!

如果是建小堆,若子节点小于父节点,那么也交换!

结束条件:直到根节点是孩子,或者建大堆的时候,孩子比父亲小(建小堆的时候,孩子比父亲大!)

向上调整算最好使用孩子作为循环借宿的条件!

image-20220911145454242.png

pop

namespace MySTL
{
	template<class T,class Container = std::vector<T>>
	class priority_queue
	{
	public:
		void pop()
		{
			//交换
			std::swap(_con[0], _con[_con.size()-1]);
			//删除最后一个元素
			_con.pop_back();
			//向下调整算法!
			adjust_down(0);
		}
	private:
		void adjust_down(size_t parent)
		{
			size_t  child = parent*2 +1;
			while (child < _con.size())
			{
				if (child + 1 < _con.size() && _con[child] < _con[child + 1])
					child++;

				if (_con[parent] < _con[child])//建大堆
				{
					std::swap(_con[parent], _con[child]);
					parent = child;
					child = parent * 2 + 1;
				}
				else
				{
					break;//没有更大的了,退出
				}
			}
		}
	private:
		Container _con;
	};
}

image-20221020172922446.png

int main()
{
	MySTL::priority_queue <int> pq;
	pq.push(3);
	pq.push(1);
	pq.push(2);
	pq.push(5);
	while (!pq.empty())
	{
		cout << pq.top() << ' ';
		pq.pop();
	}
	cout << endl;
	return 0;
}

image-20230405113500474

仿函数/函数对象

仿函数的概念

首先仿函数究竟是什么呢?——是一个类!仿函数的类型对象我们叫做函数对象!

template<class T>
struct less
{
	bool operator()(const T& x,const T& y)
	{
		return x < y;
	}
};
template<class T>
struct greater
{
	bool operator()(const T& x,const T& y)
	{
		return x > y;
	}
};

仿函数的要求是要重载(),()这也是一个运算符!例如像是我们去调用pq1.pop()的函数,后面的()就是一个函数调用符!

int main()
{
	less<int> lessFunc;
	lessFunc(0, 1);
	return 0;
}

如果不看上面的类的定义,单看后面的lessFunc(0,1),这就是一个函数调用!函数名是lessFunc——但是现在实际上是一个类!这就是为什么这个叫做仿函数!这个对象叫做函数对象!指的就是这个对象能像函数一样被使用!

但是这个其实也是一个函数调用

lessFunc(0, 1);//将这个转换为一个运算符重载!
lessFunc.operator()(0, 1);//等价于上面上面的!

仿函数的作用

那么仿函数究竟有什么作用呢?看起来和一般的函数区别?而且还多了一个封装!看起来更加的麻烦了!

我们可以举一个例子来看

//这是C语言的一个冒泡排序!
void BubbleSort(int* a, int size,bool(*cmp)(int,int))
{
	for (int i = 0; i < size; i++)
	{
		int exchange = 0;
		for(int j = 1.;j<size-i;j++)
		{
			if (cmp(a[j-1],a[j]))
			{
				std::swap(a[j - 1], a[j]);
				exchange = 1;
			}
		}
		if (!exchange)
		{
			break;
		}
	}
}

我们可以 看到在C语言中,如果我们想要解决升序降序的问题,我们就要传一个函数指针过来!——如果不用函数指针那么我们就只能重新写一个函数了!

那么在C++中的类里面我们是否也可以传一个函数指针呢?可以!但是这样子不好看!C++的设计都是在尽量的不去使用指针!

所以仿函数这时候就派上用场了!

template <class T,class Compare>
void BubbleSort(T* a, int size,Compare com)//因为Compare其实是一个类所以要显示实例化!而不是模板函数一样是推演实例化!所以要多加一个参数
{
	for (int i = 0; i < size; i++)
	{
		int exchange = 0;
		for(int j = 1.;j<size-i;j++)
		{
			//if (a[j] < a[j - 1])
			if (com(a[j], a[j - 1]))
			{
				std::swap(a[j - 1], a[j]);
				exchange = 1;
			}
		}
		if (!exchange)
		{
			break;
		}
	}
}

int main()
{
	less<int> lessFunc;
	int a[] = { 1,5,7,8,94,24,5,3,56,7 ,90 };
	BubbleSort(a, sizeof(a) / sizeof(a[0]), lessFunc);//升序排序
	//BubbleSort(a, sizeof(a) / sizeof(a[0]), less<int>());
    //怎么写也是可以的!因为是一个匿名对象!如果这个函数对象是要使用一次的话!可以直接使用!匿名对象!
	for (auto& e : a)
	{
		std::cout << e << " ";
	}
	std::cout << std::endl;
	greater<int> greaterFunc;
	int b[] = { 1,5,7,8,94,24,5,3,56,7 ,90 };
	BubbleSort(b, sizeof(b) / sizeof(b[0]), greaterFunc);//降序排序!
	//BubbleSort(b, sizeof(b) / sizeof(b[0]), greater<int>());
	for (auto& e : b)
	{
		std::cout << e << " ";
	}
	return 0;
}


image-20230405175758557

less和greater就是我们刚刚写的仿函数!

从我们实现代码的角度来说,这是一个泛型,是一个逻辑泛型——即com是一个逻辑!如果需要升序就传一个lessFunc,如果要降序就传一个greaterFunc

类型泛型就是不限类型!逻辑泛型就是不限逻辑!

仿函数一般都是传值,因为仿函数的拷贝代价很小!仿函数里面没有成员变量!所以只有一个字节作为标记位!

但是如果想要传引用也行!

void BubbleSort(T* a, int size,const Compare& com)//也是可以传引用的!但是要加上const否则就无法接收匿名对象!因为匿名对象具有常性
{
	for (int i = 0; i < size; i++)
	{
		int exchange = 0;
		for(int j = 1.;j<size-i;j++)
		{
			//if (a[j] < a[j - 1])
			if (com(a[j], a[j - 1]))//因为是const对象调用所以仿函数内部都要加上const
			{
				std::swap(a[j - 1], a[j]);
				exchange = 1;
			}
		}
		if (!exchange)
		{
			break;
		}
	}
}
template<class T>
struct less
{
	bool operator()(const T& x,const T& y)const//加上const让const对象能够调用!
	{
		return x < y;
	}
};
template<class T>
struct greater
{
	bool operator()(const T& x,const T& y)const
	{
		return x > y;
	}
};

我们也可以看到传引用其实很麻烦!所以没有什么必要!

使用仿函数来实现优先级队列的逻辑判断

我们上面的优先级队列要么只能是大堆!要么只能是小堆!不够泛用!原因就是因为我们的逻辑是写死的!但是学会了仿函数之后!我们也可以实现逻辑泛型了!

新增模板参数

	template<class T, class Container = std::vector<T>,class Compare = less<T>> 
	class priority_queue
	{
	public:
        // functions
	private:
		Container _con;
	};
}

加上第三个模板参数仿函数!

使用仿函数实现泛型的向下调整算法与向上调整算法

namespace MySTL
{
	template<class T>
	struct less
	{
		bool operator()(const T& x, const T& y)
		{
			return x < y;
		}
	};
	template<class T>
	struct greater
	{
		bool operator()(const T& x, const T& y)
		{
			return x > y;
		}
	};
	template<class T, class Container = std::vector<T>,class Compare = less<T>>
	class priority_queue
	{
	public:
        //functions
	private:
		void adjust_up(size_t child)
		{
			size_t parent = (child - 1) / 2;
			Compare com;
			while (child > 0)
			{
				//if (_con[child] > _con[parent])//建大堆!
				//if (_con[parent] < _con[child])//建大堆!
				if(com(_con[parent], _con[child]))//我们要与库保持一致要小于实现大堆!那么就要parent在前,就是与上面的逻辑一样
				{
					std::swap(_con[child], _con[parent]);
					child = parent;
					parent = (child - 1) / 2;
				}
				else
				{
					break;
				}
			}
		}
		void adjust_down(size_t parent)
		{
			size_t  child = parent*2 +1;
			Compare com;
			while (child < _con.size())
			{
				//if (child + 1 < _con.size() && _con[child] <  _con[child + 1]))
				if (child + 1 < _con.size() && com(_con[child], _con[child + 1])))//我们要与库保持一致要小于实现大堆!那么就要child在前
					child++;

				//if (_con[parent] < _con[child])//建大堆
				if(com(_con[parent], _con[child]))
				{
					std::swap(_con[parent], _con[child]);
					parent = child;
					child = parent * 2 + 1;
				}
				else
				{
					break;//没有更大的了,退出
				}
			}
		}
	private:
		Container _con;
	};
}

测试用例

int main()
{
	//大堆
	MySTL::priority_queue <int> pq;
	pq.push(3);
	pq.push(1);
	pq.push(2);
	pq.push(5);
	while (!pq.empty())
	{
		cout << pq.top() << ' ';
		pq.pop();
	}
	cout << endl;

	//小堆
	MySTL::priority_queue <int, vector<int>, greater<int>> pq1;
	pq1.push(3);
	pq1.push(1);
	pq1.push(2);
	pq1.push(5);
	while (!pq1.empty())
	{
		cout << pq1.top() << ' ';
		pq1.pop();
	}
	cout << endl;
	return 0;
}

image-20230405205825084

优先级队列模拟——最终版代码

namespace MySTL
{
	template<class T>
	struct less
	{
		bool operator()(const T& x, const T& y)
		{
			return x < y;
		}
	};
	template<class T>
	struct greater
	{
		bool operator()(const T& x, const T& y)
		{
			return x > y;
		}
	};
	template<class T, class Container = std::vector<T>,class Compare = less<T>>
	class priority_queue
	{
	public:
		priority_queue()//无参构造
		{}
		template<class InputIterator>
		priority_queue(InputIterator first, InputIterator last)
			:_con(first, last)//直接调用vector的构造
		{
			//此时还不是堆!
			//向下调整建堆!
			for (int i = (_con.size() - 2) / 2; i >= 0; i++)
			{
				adjust_down(i);
			}
		}
		void push(const T& value)
		{
			_con.push_back(value);
			adjust_up(_con.size()-1);
		}
		void pop()
		{
			//交换
			std::swap(_con[0], _con[_con.size()-1]);
			//删除最后一个元素
			_con.pop_back();
			//向下调整算法!
			adjust_down(0);
		}
		const T& top() const
		{
			return _con[0];
		}
		bool empty() const
		{
			return _con.empty();
		}
		size_t size()const
		{
			return _con.size();
		}

	private:
		void adjust_up(size_t child)
		{
			size_t parent = (child - 1) / 2;
			Compare com;
			while (child > 0)
			{
				//if (_con[child] > _con[parent])//建大堆!
				//if (_con[parent] < _con[child])//建大堆!
				if(com(_con[parent], _con[child]))//我们要与库保持一致要小于实现大堆!那么就要parent在前
				{
					std::swap(_con[child], _con[parent]);
					child = parent;
					parent = (child - 1) / 2;
				}
				else
				{
					break;
				}
			}
		}
		void adjust_down(size_t parent)
		{
			size_t  child = parent*2 +1;
			Compare com;
			while (child < _con.size())
			{
				//if (child + 1 < _con.size() && _con[child] <  _con[child + 1])
				if (child + 1 < _con.size() && com(_con[child], _con[child + 1]))//我们要与库保持一致要小于实现大堆!那么就要child在前
					child++;

				//if (_con[parent] < _con[child])//建大堆
				if(com(_con[parent], _con[child]))
				{
					std::swap(_con[parent], _con[child]);
					parent = child;
					child = parent * 2 + 1;
				}
				else
				{
					break;//没有更大的了,退出
				}
			}
		}
	private:
		Container _con;
	};
}

仿函数的应用

要知道我们优先级队列的类型可以不只是库里面的类型!还可以是自定义类型!

class Date
{
public:
	Date(int year = 1900, int month = 1, int day = 1)
		: _year(year)
		, _month(month)
		, _day(day)
	{}

	bool operator<(const Date& d)const
	{
		return (_year < d._year) ||
			(_year == d._year && _month < d._month) ||
			(_year == d._year && _month == d._month && _day < d._day);
	}

	bool operator>(const Date& d)const
	{
		return (_year > d._year) ||
			(_year == d._year && _month > d._month) ||
			(_year == d._year && _month == d._month && _day > d._day);
	}

	friend ostream& operator<<(ostream& _cout, const Date& d)
	{
		_cout << d._year << "-" << d._month << "-" << d._day;
		return _cout;
	}

private:
	int _year;
	int _month;
	int _day;
};

如果一个自定义类型是支持比较大小的!那么仿函数greater和less都是可以使用的!里面的>与< 本质也是调用的是Date类的运算符重载!

void testpriorityqueue()
{
	priority_queue<Date> q1;//默认是大堆
	q1.push(Date(2010, 1, 1));
	q1.push(Date(2010, 1, 2));
	q1.push(Date(2010, 1, 3));
	cout << q1.top() << endl;

	priority_queue<Date,vector<Date>,greater<Date>> q2;//默认是大堆
	q2.push(Date(2010, 1, 1));
	q2.push(Date(2010, 1, 2));
	q2.push(Date(2010, 1, 3));
	cout << q2.top() << endl;

}
int main()
{
	testpriorityqueue();
	return 0;
}

现在如果我们传入的是这个自定义类型的指针呢?

void testpriorityqueue()
{
	//大堆
	priority_queue<Date*> q3;
	q3.push(new Date(2010, 1, 1));
	q3.push(new Date(2010, 1, 2));
	q3.push(new Date(2010, 1, 3)); 
	cout << *q3.top() << endl;
	//小堆
	priority_queue<Date*,vector<Date*>,greater<Date*>> q4;/
	q4.push(new Date(2010, 1, 1));
	q4.push(new Date(2010, 1, 2));
	q4.push(new Date(2010, 1, 3));
	cout << *q4.top() << endl;
}
int main()
{
	testpriorityqueue();
	return 0;
}

image-20230406092317205

我们发现这时候堆的比较好像就失效了!这是为什么呢?因为我们传过去的是date类的地址!此时我们传入的仿函数是用地址的大小去比较的!谁地址谁大!谁地址小谁小!

因为每次地址是随机的!所以每次的结果都会在改变!

那么现在的结果已经不符合我们预期了!怎么办?

我们就可以重新写一个关Date* 的仿函数!

struct PDateCompare_greater
{
	bool operator()(const Date* x,const Date* y)
	{
		return *x > *y;
	}
};
struct PDateCompare_less
{
	bool operator()(const Date* x,const Date* y)
	{
		return *x < *y;
	}
};
void testpriorityqueue()
{
	//大堆
	//priority_queue<Date*> q3;
	priority_queue<Date*,vector<Date*>,PDateCompare_less> q3;
	q3.push(new Date(2010, 1, 1));
	q3.push(new Date(2010, 1, 2));
	q3.push(new Date(2010, 1, 3)); 
	cout << *q3.top() << endl;
    
	//小堆
	priority_queue<Date*,vector<Date*>,PDateCompare_greater> q4;
	q4.push(new Date(2010, 1, 1));
	q4.push(new Date(2010, 1, 2));
	q4.push(new Date(2010, 1, 3));
	cout << *q4.top() << endl;
}
int main()
{
	testpriorityqueue();
	return 0;
}

image-20230406100002253

如果比较的方式不是我们想要的!那么我们就可以通过自己写一个仿函数来完成,甚至如果自定义类不支持> 与< 重载我们也可以写一个仿函数来完成大小的比较!

STL中实现的比较

image-20230406103953230

STL中的建堆算法

STL中的建堆算法是在algorithm这个头文件中!

image-20230406104415345

  • push_heap将一个元素插入堆中
  • pop_heap将一个元素从堆中移除
  • make_heap 从一个范围内的元素中创建一个堆
  • sort_heap 堆排序!
  • if_heap 判定一个范围内的元素是不是堆
  • is_heap_until 找到第一个不按堆规则的元素

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

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

相关文章

正负10V电压输入±电流输出伺服阀控制器

10V、4~20mA模拟指令输入伺服阀放大器是一种控制元件&#xff0c;用于控制液压系统中的比例阀或伺服阀。它主要由三个部分组成&#xff1a;比例阀或伺服阀、放大器。 比例阀或伺服阀是控制流量或压力的元件&#xff0c;它的输出信号通常是电压或电流。放大器用于将实际值转换为…

微信小程序开发uni-app-8分钟上手开发

本篇文章uni-app微信小程序开发-8分钟上手开发 -首先到微信小程序官网登录/注册微信小程序 微信小程序官网 uni-app 微信小程序 注册微信小程序 这里要注意&#xff1a; 激活邮箱之后&#xff0c;选择主体类型为 “个人类型”&#xff0c;并按要求登记主体信息。主体信息提…

一种直流转交流的代码实现 - through FFT

# show how to use FFT, filtered DC signal and return back to SampleValue-time zone. # the basic concept is coming from ChatGPT. # Write in python language. # # created by twicave. # Jun09,2023 # import numpy as np import matplotlib.pyplot as plt# 定义正弦信…

【C# 10 和 .NET 6】构建和使用 Web 服务(第16章)

Building and Consuming Web Services 构建和使用 Web 服务 本章介绍如何使用 ASP.NET Core Web API 构建 Web 服务&#xff08;也称为 HTTP 或 REST 服务&#xff09;&#xff0c;以及如何使用 HTTP 客户端使用 Web 服务&#xff0c;这些客户端可以是任何其他类型的 .NET 应用…

Qt信号和槽

一、定义 信号与槽&#xff08;Signal & Slot&#xff09;是 Qt 编程的基础&#xff0c;也是 Qt 的一大创新。因为有了信号与槽的编程机制&#xff0c;在 Qt 中处理界面各个组件的交互操作时变得更加直观和简单。 信号&#xff08;Signal&#xff09;就是在特定情况下被发…

基于SpringBoot+vue的毕业生信息招聘平台设计和实现

博主介绍&#xff1a; 大家好&#xff0c;我是一名在Java圈混迹十余年的程序员&#xff0c;精通Java编程语言&#xff0c;同时也熟练掌握微信小程序、Python和Android等技术&#xff0c;能够为大家提供全方位的技术支持和交流。 我擅长在JavaWeb、SSH、SSM、SpringBoot等框架下…

淘宝618每日一猜答案6月12日 源氏木语获得多少个奖项?

淘宝6月12日每日一猜答案是什么&#xff1f;&#xff0c;接下来也会给大家来介绍一下6月12日淘宝大赢家每日一猜的答案。 淘宝每日一猜6月12日答案分享 活动问题&#xff1a;源氏木语获得多少个奖项&#xff1f; 活动答案&#xff1a;【15】 注意是阿拉伯数字&#xff01; …

20230612 set1打卡

哈希表理论基础 242.有效的字母异位词 349. 两个数组的交集 202. 快乐数 1. 两数之和

FPGA驱动FT601实现USB3.0相机 OV5640视频采集 提供2套工程源码和QT上位机源码

目录 1、前言2、FT601芯片解读和时序分析FT601功能和硬件电路FT601读时序解读FT601写时序解读 3、我这儿的 FT601 USB3.0通信方案4、vivado工程1--彩条视频采集传输详细设计框图及其原理vivado工程解读 5、vivado工程2--OV5640视频采集传输详细设计框图及其原理vivado工程解读 …

【几分醉意赠书活动 - 04期】 | 《C++、Linux、算法等系列众书》

个人主页&#xff1a; 几分醉意的CSDN博客主页_传送门 个人主页&#xff1a; 陈老板的CSDN博客主页_传送门 赠书活动 | 第四期 本期好书推荐&#xff1a;《C、Linux、算法等系列众书》 粉丝福利&#xff1a;书籍赠送&#xff1a;共计送出30本 参与方式&#xff1a;关注公众号&a…

【MySQL 数据查询】: 基本、分组、排序、聚合、分页、条件查询详解

前言 ✨欢迎来到小K的MySQL专栏&#xff0c;本节将为大家带来MySQL数据查询的讲解&#xff08;基本、分组、排序、聚合、分页、条件查询&#xff09; 目录 前言一、基本查询二、条件查询三、聚合函数(统计函数)四、分组查询五、排序查询五、分页查询六、总结 一、基本查询 MySQ…

全网最简单的软渲染器

引言 本文实现了一个包含矩阵变化、光栅化、面剔除、深度测试等功能的软渲染器。所谓软渲染器就是使用 CPU 渲染 3D 模型的程序。因此请记住我们的最终目的&#xff1a;将3D模型显示在屏幕上 。本文分为两个部分&#xff1a;预备知识、渲染器核心实现。预备知识概述了简化的渲…

可以在手机上使用的提醒事项软件有哪些

随着科技的进步&#xff0c;越来越多的人选择使用各种手机软件来提高自己的效率&#xff0c;高效率的手机软件已经成为人们生活和工作中不可或缺的一部分。其中&#xff0c;提醒事项软件是一种非常实用的工具&#xff0c;可以督促用户按时完成任务&#xff0c;有效防止遗忘。 …

chatgpt赋能python:如何利用Python在网上赚钱:一份SEO指南

如何利用Python在网上赚钱&#xff1a;一份SEO指南 随着互联网的快速发展和Python成为一种越来越受欢迎的编程语言&#xff0c;越来越多的人开始利用Python在网上赚取额外的收入。本篇文章将介绍一些Python工具和技术&#xff0c;以及SEO最佳实践&#xff0c;帮助你利用Python…

Spring Boot 回顾

Spring Boot 的发展是很快的&#xff0c;也使用了很多年。但是在工作中&#xff0c;还是发现了很多公司还没有怎么使用 Spring Boot&#xff0c;依旧是在使用 Spring MVC。于是决定复习总结一下。框架的进步和飞跃很重要&#xff0c;但是也不能一味追求新鲜事物&#xff0c;历史…

Yolo算法的演进—YoloCS有效降低特征图空间复杂度(附论文下载)

点击蓝字 关注我们 关注并星标 从此不迷路 计算机视觉研究院 公众号ID&#xff5c;计算机视觉研究院 学习群&#xff5c;扫码在主页获取加入方式 论文地址&#xff1a;YOLOCS: Object Detection based on Dense Channel Compression for Feature Spatial Solidification (arxiv…

【6.12 代随_55day】判断子序列、不同的子序列

判断子序列、不同的子序列 判断子序列动态规划方法图解步骤代码 不同的子序列1.方法图解步骤代码 判断子序列 力扣连接&#xff1a;392. 判断子序列&#xff08;简单&#xff09; 动态规划方法 &#xff08;这道题也可以用双指针的思路来实现&#xff0c;时间复杂度也是O(n)…

Linux变卡cpu占用高,已装curl、wget但提示命令找不到(挖矿病毒)

现象 服务器变卡&#xff0c;top cpu占用非常高&#xff0c;但是进程占用只有1% 下载安装htop查看 看样子中了挖矿病毒 解决方式 修改/etc/hosts 把dev.fugglesoft.me 指向127.0.0.1 F9 -> enter杀掉进程 又多出来一个执行sh的&#xff0c;修改病毒sh&#xff0c;vi进去随便…

Portraiture5.0滤镜插件中文升级版本下载及功能介绍

专注人像修图&#xff0c;打造完美摄影作品&#xff0c;Portrait5.0是款最新版的人脸图像后期处理软件。它可以帮助用户快速编辑修图&#xff0c;提供全方面的人物美白效果&#xff0c;让用户可以更轻松地修图&#xff0c;操作简单&#xff0c;方便快捷&#xff0c;非常不错。新…

Cilium 如何处理 L7 流量

还记得在 使用 Cilium 增强 Kubernetes 网络安全 示例中&#xff0c;我们通过设置网络策略限制钛战机 tiefighter 访问死星 deathstar 的 /v1/exhaust-port 端点&#xff0c;但放行着陆请求 /v1/request-landing。在提起 Cilium 时&#xff0c;都说其是使用 eBPF 技术推动的用于…