高阶数据结构学习 —— 图(1)

news2024/11/26 18:34:31

文章目录

  • 1、并查集
  • 2、了解图
  • 3、邻接矩阵
  • 4、压缩路径
  • 5、基本概念
  • 6、邻接表


1、并查集

并查集是一个森林,是由多棵树组成的。这相当于整套数据,分成多个集合。查找有交集的集合们,会把它们合并起来,所以叫并查集。

一开始拿到的是一堆数据,我们给它们编号,然后放到一个个集合中。如何做编号?如何做集合?我们先建立两个文件,Graph.cpp和UnionFindSet.h。

#include <iostream>
using namespace std;

int main()
{
	string a[] = { "张三", "李四", "王五", "赵六" };
	UnionFindSet<string> ufs(a, 4);
	return 0;
}
#pragma once
#include <vector>
#include <map>

template <class T>
class UnionFindSet
{
public:
	UnionFindSet(const T* a, size_t n)
	{

	}
private:
	vector<T> _a;
	
};

把接收到的a保存到_a,这样每个人编号就是下标了,但这不是好办法。

class UnionFindSet
{
public:
	UnionFindSet(const T* a, size_t n)//建立映射关系
	{
		for (size_t i = 0; i < n; ++i)
		{
			_a.push_back(a[i]);
			_indexMap[a[i]] = i;
		}
	}
private:
	vector<T> _a;//下标就是编号,编号找人
	map<T, int> _indexMap//但通过人找编号更合理,这样不需要每次都得遍历才能找到一个人
};

通过这样的代码可以简单理解一下编号是怎么回事,但代码不是这样写的。接下来看看集合。集合本质就是树,选择一个根节点,然后几个编号作为它的子节点,但该选哪几个编号作为一个树呢?这里的用的树是双亲表示法,每个孩子都存储它的双亲节点。还是用数组来表示,最一开始都初始化为-1,意思就是每个编号单独成一个集合

在这里插入图片描述
构成集合后会是这样
在这里插入图片描述
0和678作为一棵树,0作为根,6是孩子节点,把6的-1加过去,0位置变成-2,6位置存上双亲节点0,然后是7和8。这样的规则下,数组的下标就是集合中元素的编号,比如arr[0],就是编号为0的数据;每个位置存储的数字,如果为负数,说明当前编号是根,数字的绝对值就是这棵树的节点数量;如果数字为非负数,那么当前位置存储的就是这个编号的双亲编号。比如arr[6] = 0,6这个编号的双亲编号就是0,arr[0] = -4,0编号是根,节点数量是4。

假设4和8建立了联系,这两个集合要合并。这里要如何合并?根节点是谁?根节点是谁都可以,既可以是0也可以是1,选一个就好。假如选0做根节点,那么0就要加上1位置的数,1的位置存储0,4和9不变。

在这里插入图片描述

下面是完善后的并查集类

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

class UnionFindSet
{
public:
	UnionFindSet(size_t n)//给编号数量
		:_ufs(n, -1)
	{}

	void Union(int x1, int x2)//给两个编号,合并
	{
		//得先判断这两个编号能不能合并
		int root1 = FindRoot(x1);
		int root2 = FindRoot(x2);
		//本身在一个集合,就不需要合并
		if (root1 == root2) return;
		//可以合并的话就自己做规定怎么合并,这里就让小的编号做根
		if (root1 > root2) swap(root1, root2);
		_ufs[root1] += _ufs[root2];
		_ufs[root2] = root1;
	}

	int FindRoot(int x)//找一个编号的根
	{
		int parent = x;
		while (_ufs[parent] >= 0)//此时说明它本身不是根,就看存的值,往上找父节点,因为父节点可能不是根,所以需要while
		{
			parent = _ufs[parent];//对照上面的图来看
		}
		return parent;//小于0说明自己就是根,直接返回
	}

	bool InSameSet(int x1, int x2)//看看编号在不在一个集合里
	{
		return FindRoot(x1) == FindRoot(x2);
	}

	size_t SetSize()
	{
		size_t size = 0;
		for (size_t i = 0; i < _ufs.size(); i++)
		{
			if (_ufs[i] < 0) ++size;
		}
		return size;
	}
private:
	vector<int> _ufs;
};

看一道题,剑指offer的。

LCR 116. 省份数量

在这里插入图片描述

把我们写好的并查集类拿过来使用

class UnionFindSet
{
public:
	UnionFindSet(size_t n)
		:_ufs(n, -1)
	{}

	void Union(int x1, int x2)
	{
		int root1 = FindRoot(x1);
		int root2 = FindRoot(x2);
		if (root1 == root2) return;
		if (root1 > root2) swap(root1, root2);
		_ufs[root1] += _ufs[root2];
		_ufs[root2] = root1;
	}

	int FindRoot(int x)
	{
		int parent = x;
		while (_ufs[parent] >= 0)
		{
			parent = _ufs[parent];
		}
		return parent;
	}

	bool InSameSet(int x1, int x2)
	{
		return FindRoot(x1) == FindRoot(x2);
	}

	size_t SetSize()
	{
		size_t size = 0;
		for (size_t i = 0; i < _ufs.size(); i++)
		{
			if (_ufs[i] < 0) ++size;
		}
		return size;
	}
private:
	vector<int> _ufs;
};

class Solution {
public:
    int findCircleNum(vector<vector<int>>& isConnected) {
        UnionFindSet ufs(isConnected.size());
        for(size_t i = 0; i < isConnected.size(); ++i)
        {
            for(size_t j = 0; j < isConnected[i].size(); ++j)
            {
                if(isConnected[i][j] == 1)//是1说明两个城市相连,那就可以进入一个集合,所以用Union
                    ufs.Union(i, j);
            }
        }
        return ufs.SetSize();
    }
};

如果不用并查集,应该怎么写?那就现场写几个用到的函数

    int findCircleNum(vector<vector<int>>& isConnected) {
        vector<int> ufs(isConnected.size(), -1);
        auto findRoot = [&ufs](int x)
        {
            while(ufs[x] >= 0)
                x = ufs[x];
            return x;
        };
        for(size_t i = 0; i < isConnected.size(); ++i)
        {
            for(size_t j = 0; j < isConnected[i].size(); ++j)
            {
                if(isConnected[i][j] == 1)
                {
                    //合并集合
                    int root1 = findRoot(i);
                    int root2 = findRoot(j);
                    if(root1 != root2)
                    {
                        ufs[root1] += ufs[root2];
                        ufs[root2] = root1;
                    }
                }
            }
        }
        int count = 0;
        for(auto e : ufs)
        {
            if(e < 0) ++count;
        }
        return count;
    }

压缩路径:
当数据量比较大时会出现一棵树下来连接了很多层子树,这时候如果每次都要一点点往上找根,效率就比较低。这时候就要用压缩路径,压缩路径在findRoot函数中使用。查找一次比较深的节点的根后,我们发现需要减少一下层数,这个可以用一个变量来记录循环次数,当大于某个数时就得做压缩了。假设查找的是x节点的根,查找完后,就把x节点位置存储的值变成根节点的编号,这样就是把这个节点直接调为根节点的孩子,并且把x节点原父节点也调成根节点的孩子,但x节点的原兄弟节点不动。如果从根到x有3层,调整完后就变成了2层,等到再查找x节点原兄弟节点时,再把这个兄弟节点调成根的孩子,而这个兄弟节点的父节点就还是根的孩子。通过不断地调整,要做到的结果就是原本3层,变成1层,都变成根的孩子节点,这些节点的父节点都是根。

练题时,如果超时,估计就要用压缩路径了。

另一个题

990. 等式方程的可满足性

在这里插入图片描述

我们可以先遍历一遍,为等号的都放到一个集合里,再去看不相等的符号的,如果不相等的两个在一个集合里,那就false,不是那就继续。

    bool equationsPossible(vector<string>& equations) {
        vector<int> ufs(26, -1);
        auto findRoot = [&ufs](int x)
        {
            while(ufs[x] >= 0)
                x = ufs[x];
            return x;
        };
        //第一次遍历,把相等的值放到一个集合中
        for(auto& str : equations)
        {
            if(str[1] == '=')
            {
                int root1 = findRoot(str[0] - 'a');
                int root2 = findRoot(str[3] - 'a');
                if(root1 != root2)
                {
                    ufs[root1] += ufs[root2];
                    ufs[root2] = root1;
                }
            }
        }
        //第二遍,判断不相等的在不在一个集合,在就false
        for(auto& str : equations)
        {
            if(str[1] == '!')
            {
                int root1 = findRoot(str[0] - 'a');
                int root2 = findRoot(str[3] - 'a');
                if(root1 == root2) return false;
            }
        }
        return true;
    }

2、了解图

图是一种数据结构,图由顶点集合和边的集合构成,通常都会用G(V, E)来表示,V就是顶点集合,E就是边集合,所以G(V, E)的意思就是图由顶点(集合)和边(集合)组成。常见的图

在这里插入图片描述

树是一种特殊的图,无环,连通。树关注节点(顶点)中存的值,图关注的是顶点和边的权值。边如何表示?边连接两个点,还有权值,所以它是一个二维数组,e[i][j]就表示顶点i和j之间的边,这个位置的值就是权值。图分为有向图和无向图,也就是有方向箭头和没有方向箭头,无向图的两个顶点可以互相连接,有向图只能按照既定方向去走。

边的含义可以有很多,它不止是表示距离,也可以表示两个城市(顶点)之前坐高铁的时间,两个顶点的关系等。针对图有一些种类的问题,深度/广度优先,最小生成树,最短路径等,这些后面再写。

微信,qq是经典的无向图,无向图通常用来表示强社交关系,微博,bzhan等就是有向图,是一种弱社交关系,媒体社交。qq中有亲密度,亲密度就是权值。短视频的推荐算法也和图有关。

图有两个结构,邻接矩阵和邻接表。邻接矩阵存边的集合。每个顶点先假设下标,比如ABCD就是0123,这样一个二维数组的邻接矩阵,每个位置的值就表示权值,[i][j],ij相等,那就存0,表示是同一顶点,ij不同,如果之间没有关系,那就填上无穷这样的数字,也可以填别的,有向图可能可以从2到3,但不能从3到2,那么[3][2]就会是无穷。如果权值表示两个点之间的关系,那么就填为1或0。当然,规则由自己决定。

邻接矩阵存储方式非常适合稠密图,因为无论稠密还是稀疏,邻接矩阵都是每个位置都有值;邻接矩阵可以判断两个顶点的连接关系,并取到权值。但相对而言,邻接矩阵不适合查找一个顶点连接的所有边,它需要O(N),比如找顶点4连接的点,它就得遍历一下,每个边都得看一下那个位置的值。

邻接表

在这里插入图片描述

它是一个指针数组,把自己连接的所有点都挂在下面。它也能表示有无向。

邻接表适合存储稀疏图,适合查找一个顶点连接出去的边,因为只需要遍历连接的顶点的数,但不适合确定两个顶点是否相连和权值。

有向图情况下,会有入边表和出边表,就是A挂着的顶点B,入边表意思就是从B到A,出边表意思就是从A到B。一般只需要一个出边表即可。

邻接矩阵和邻接表是相辅相成,各有缺点的互补结构。

3、邻接矩阵

我们创建Graph.h文件,先写一个框架

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

namespace matrix
{
	template<class V, class W, W MAX_W = INT_MAX, bool Direction = false>//W权值,Direction表示有无向,false表示无向
	//中间加的MAX_W,其实就是非类型模版参数,是用来初始化邻接矩阵的,也可以传其它值
	class Graph
	{
	public:
		Graph(const V* a, size_t n)//创造图,手动添加
		{
			_vertexs.reserver(n);
			for (size_t i = 0; i < n; ++i)
			{
				_vertexs.push_back(a[i]);
				_indexMap[a[i]] = i;
			}
			_matrix.resize(n);
			for (size_t i = 0; i < _matrix.size(); ++i)
			{
				_matrix[i].resize(n, MAX_W)}
		}

		size_t GetVertexIndex(const V& v)//确定顶点下标,虽然都在map里,但还是要有一个函数
		{

		}

		void AddEdge(const V& src, const V& dst, const W& w)//增加边的话,应当传源点,目标点,权值
		{
			 
		}
	private:
		vector<V> _vertexs;//顶点集合,比如ABCD
		//有稀疏图,稠密图的叫法,就是在说边的多少
		map<V, int> _indexMap;//顶点映射下标,比如ABCD变为1234
		vector<vector<W>> _matrix;//邻接矩阵,展示顶点之间的关系和权值
	};
};

Graph构造函数那里就创建了一个图,三个成员都被初始化完成,传进来的就是一个数组a和顶点个数。接下来要开始添加边,在这之前先确定好顶点下标。

讲解在代码中

		size_t GetVertexIndex(const V& v)//确定顶点下标,虽然都在map里,但还是要有一个函数
		{
			auto it = _indexMap.find(v);
			if ( it != _indexMap.end())
			{
				return it->second;
			}
			else
			{
				throw invalid_argument("顶点不存在");//非法参数异常
				return -1;//抛异常是运行逻辑,程序走到这里会抛,但编译器不管运行逻辑,还是要检查这个函数是否正确结束,所以要返回。
			}
		}

		void AddEdge(const V& src, const V& dst, const W& w)//增加边的话,应当传源点,目标点,权值
		{
			//即使抛异常程序还能执行,但程序员已经知道程序错误了,最后程序正常地异常结束
			//assert则是暴力退出,且只在debug模式下才有assert,release模式下就屏蔽assert了
			size_t srci = GetVertexIndex(src);
			size_t dsti = GetVertexIndex(dst);
			//有向图和无向图区分
			_matrix[srci][dsti] = w;
			if (Direction == false)
				_matrix[dsti][srci] = w;
		}

打印函数

		void Print()
		{
			size_t n = _vertexs.size();
			//顶点
			for (size_t i = 0; i < n; ++i)
			{
				cout << "[" << i << "]" << "->" << _vertexs[i] << endl;
			}
			cout << endl;

			//打印横下标
			cout << "  ";
			for (size_t i = 0; i < n; ++i)
			{
				cout << i << " ";
			}
			cout << endl;

			for (size_t i = 0; i < _matrix.size(); ++i)
			{
				cout << i << " ";//打印竖下标
				for (size_t j = 0; j < _matrix[i].size(); ++j)
				{
					if (_matrix[i][j] == MAX_W)
					{
						cout << "* ";
					}
					else cout << _matrix[i][j] << " ";
				}
				cout << endl;
			}
			cout << endl;
		}

测试代码

void TestGraph1()//放在matrix命名空间里
{
	Graph<char, int, INT_MAX, true> g("0123", 4);
	g.AddEdge('0', '1', 1);
	g.AddEdge('0', '3', 4);
	g.AddEdge('1', '3', 2);
	g.AddEdge('1', '2', 9);
	g.AddEdge('2', '3', 8);
	g.AddEdge('2', '1', 5);
	g.AddEdge('2', '0', 3);
	g.AddEdge('3', '2', 6);
	g.Print();
}

int main()
{
	matrix::TestGraph1();
	return 0;
}

到现在为止,代码没有问题,我们来改进一下功能实现。

合并集合的时候,是让数量小的集合合并到大的,还是大的合并到小的,这里小的合并到大的为好。默认root1为数量大的

		if (abs(_ufs[root1]) < abs(_ufs[root2])) swap(root1, root2);
		_ufs[root1] += _ufs[root2];
		_ufs[root2] = root1;

4、压缩路径

不用递归写,毕竟是在压缩深度深的树。用非递归写。

	int FindRoot(int x)//找一个编号的根
	{
		int root = x;
		while (_ufs[root] >= 0)//此时说明它本身不是根,就看存的值,往上找父节点,因为父节点可能不是根,所以需要while
		{
			root = _ufs[root];//对照上面的图来看
		}
		//压缩路径
		while (_ufs[x] >= 0)
		{
			int parent = _ufs[x];
			_ufs[x] = root;
			x = parent;
		}
		return root;//小于0说明自己就是根,直接返回
	}

这里就是一次把当前节点到根节点之间的所有节点都变成根的孩子。

5、基本概念

完全图是最稠密的图,任意两个顶点之间都直接相连 。在有n个顶点的无向图中,若有n × (n-1)/2条边,即任意两个顶点之间有且仅有一条边,
则称此图为无向完全图;在n个顶点的有向图中,若有n × (n-1)条边,即任意两个顶点之间有且仅有方向相反的边,则称此图为有向完全图。

邻接顶点。就是两个顶点之间有边,就是两个邻接顶点。在无向图中G中,若(u, v)是E(G)中的一条边,则称u和v互为邻接顶点,并且称边(u,v)依附于顶点u和v;在有向图G中,若<u, v>是E(G)中的一条边,则称顶点u邻接到v,顶点v邻接自顶点u,并且称边<u, v>与顶点u和顶点v相关联。

顶点的度指的是与这个顶点相连的边的数量,记作deg(v)。有向图有入度和出度,也就是1这个顶点,1到3有1条边,2到1有2条边,所以1的入度是2,出度是1。

路径则好理解,从一个顶点到另一个顶点都可以走哪些边,这些边就组成了路径。路径可以有很多条,也就出现了最短路径问题。

路径长度。对于不带权的图,一条路径的路径长度是指该路径上的边的条数;对于带权的图,一条路径的路径长度是指该路径上各个边权值的总和。

简单路径与回路。若路径上各顶点v1,v2,v3,…,vm均不重复,则称这样的路径为简单路径。若路径上第一个顶点v1和最后一个顶点vm重合,则称这样的路径为回路或环。

子图。子图的顶点和边都是原图的一部分。

连通图。在无向图中,若从顶点v1到顶点v2有路径,则称顶点v1与顶点v2是连通的。如果图中任意一对顶点都是连通的,则称此图为连通图。

强连通图:在有向图中,若在每一对顶点vi和vj之间都存在一条从vi到vj的路径,也存在一条从vj到vi的路径,则称此图是强连通图。

任意点之间都有路径到达,无向图中叫连通图,有向图中叫强连通图。

生成树:一个连通图的最小连通子图称作该图的生成树。有n个顶点的连通图的生成树有n个顶点和n-1条边。注意这是无向图中的概念。

6、邻接表

邻接表要用到哈希。用上面邻接矩阵的代码做一下改动。

	template <class W>
	struct Edge
	{
		int _dsti;//目标点的下标
		W _w;
	};

	template<class V, class W, bool Direction = false>//这里就不需要INT_MAX了
	class Graph
	{
		typedef Edge<W> Edge;
	public:
		Graph(const V* a, size_t n)
		{
			_vertexs.reserve(n);
			for (size_t i = 0; i < n; ++i)
			{
				_vertexs.push_back(a[i]);
				_indexMap[a[i]] = i;
			}
		}

		size_t GetVertexIndex(const V& v)
		{
			auto it = _indexMap.find(v);
			if (it != _indexMap.end())
			{
				return it->second;
			}
			else
			{
				throw invalid_argument("顶点不存在");
				return -1;
			}
		}

		void AddEdge(const V& src, const V& dst, const W& w)
		{
			size_t srci = GetVertexIndex(src);
			size_t dsti = GetVertexIndex(dst);
		}

		void Print()
		{
			size_t n = _vertexs.size();
			for (size_t i = 0; i < n; ++i)
			{
			cout << "[" << i << "]" << "->" << _vertexs[i] << endl;
			}
			cout << endl;
		}
	private:
		vector<V> _vertexs;//顶点集合
		map<V, int> _indexMap;//顶点映射下标
		vector<Edge*> _tables;//邻接表
	};

然后完善,在命名空间里添加一个结构体,类里再使用它

	template <class W>
	struct Edge
	{
		size_t _dsti;//目标点的下标
		W _w;
		Edge<W>* _next;

		Edge(size_t dsti, const W& w)
			:_dsti(dsti)
			, _w(w)
			, _next(nullptr)
		{}
	};
	
	template<class V, class W, bool Direction = false>//这里就不需要INT_MAX了
	class Graph
	{
		typedef Edge<W> Edge;
	public:
		Graph(const V* a, size_t n)
		{
			_vertexs.reserve(n);
			for (size_t i = 0; i < n; ++i)
			{
				_vertexs.push_back(a[i]);
				_indexMap[a[i]] = i;
			}
			_tables.resize(n, nullptr);
		}
		
		void AddEdge(const V& src, const V& dst, const W& w)
		{
			size_t srci = GetVertexIndex(src);
			size_t dsti = GetVertexIndex(dst);
			Edge* eg = new Edge(dsti, w);
			//采用头插,尾插需要找尾,不如头插更有效率
			eg->_next = _tables[srci];
			_tables[srci] = eg;
			if (Direction == false)//如果是无向图
			{
				Edge* eg = new Edge(srci, w);
				eg->_next = _tables[dsti];
				_tables[dsti] = eg;
			}
		}

		void Print()
		{
			size_t n = _vertexs.size();
			for (size_t i = 0; i < n; ++i)
			{
				cout << "[" << i << "]" << "->" << _vertexs[i] << endl;
			}
			cout << endl;
			for (size_t i = 0; i < n; ++i)
			{
				cout << endl;
				cout << _vertexs[i] << "[" << i << "]";
				Edge* cur = _tables[i];
				while (cur)
				{
					printf(" - %-3d -> ", cur->_w);
					cout << _vertexs[cur->_dsti] << "[" << cur->_dsti << "]";
					cur = cur->_next;
				}
				cout << " -> nullptr" << endl;
			}
		}

测试

	void TestGraph2()
	{
		string a[] = { "张三", "李四", "王五", "赵六" };
		Graph<string, int> g1(a, 4);
		g1.AddEdge("张三", "李四", 100);
		g1.AddEdge("张三", "王五", 200);
		g1.AddEdge("王五", "赵六", 30);
		g1.Print();
	}

本篇gitee

下一篇开始写图的算法。

结束。

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

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

相关文章

idea上怎么将新创建项目转为maven项目

场景 在刚创建的一个项目中&#xff0c;往往没有被识别为maven项目&#xff0c;怎么做呢&#xff1f; 方法 然后选maven&#xff0c;这样这个项目就变成了maven项目

计数排序——不用比较的排序

原理&#xff1a; 代码&#xff1a; void CountSort(int* a, int n) {int max a[0];int min a[0];for (int i 0; i < n; i){if (a[i] > max){max a[i];}if (a[i] < min){min a[i];}}int grang max - min 1;int* count (int*)malloc(sizeof(int) * grang);me…

【SpringBoot】Docker部署

docker部署是主流的部署方式&#xff0c;极大的方便了开发部署环境&#xff0c;保持了环境的统一&#xff0c;也是实现自动化部署的前提。 1 项目的目录结构 package: 点击打包&#xff0c;生成 xxx-SNAPSHOT.jar target目录: 打包生成目录&#xff0c;生成的jar存放位置Docke…

Leetcode—2558.从数量最多的堆取走礼物【简单】

2023每日刷题&#xff08;十二&#xff09; Leetcode—2558.从数量最多的堆取走礼物 大顶堆实现代码 void swap(int *a, int *b) {int tmp *a;*a *b;*b tmp; }void downAdjustHeap(int *heap, int low, int high) {int i low;int j 2 * i 1;while(j < high) {if(j …

项目部署之OpenResty

项目部署之OpenResty 1. OpenResty介绍 OpenResty 是一个基于Nginx的高性能Web平台&#xff0c;用于方便地搭建能够处理超高并发、扩展性极高的动态Web应用、Web服务和动态网关。具备下列特点&#xff1a; 具备Nginx的完整功能基于Lua语言进行扩展&#xff0c;集成了大量精良…

单调队列和单调栈

单调队列 这种涉及到维护子数组的最大/小值的操作&#xff0c;一般都会是 1 剑指 Offer 59 - II. 队列的最大值 2 239. 滑动窗口最大值 3 1438. 绝对差不超过限制的最长连续子数组 单调栈

【算法】Floyd算法多源汇最短路

最短路问题基础问题到这里就结束了&#xff0c;附上最短路问题知识结构图。 题目 给定一个 n 个点 m 条边的有向图&#xff0c;图中可能存在重边和自环&#xff0c;边权可能为负数。 再给定 k 个询问&#xff0c;每个询问包含两个整数 x 和 y&#xff0c;表示查询从点 x 到点…

Python 模块:创建、导入和使用

什么是模块&#xff1f; 将模块视为代码库。模块是一个包含一组函数的文件&#xff0c;您想要在应用程序中包含这些函数。 创建一个模块 要创建一个模块&#xff0c;只需将要包含在其中的代码保存在扩展名为 .py 的文件中&#xff1a; 示例&#xff1a;将以下代码保存在名为…

YOLOv5算法 | 万字长文带你深度解析yolov5s.yaml配置文件

前言&#xff1a;Hello大家好&#xff0c;我是小哥谈。配置文件yolov5s.yaml在YOLOv5模型训练过程中发挥着至关重要的作用&#xff0c;属于初学者必知必会的文件&#xff01;在YOLOv5-6.0版本源码中&#xff0c;配置了5种不同大小的网络模型&#xff0c;分别是YOLOv5n、YOLOv5s…

D-Nerf:用于动态场景表示的神经辐射场

Pumarola A, Corona E, Pons-Moll G, et al. D-nerf: Neural radiance fields for dynamic scenes[C]//Proceedings of the IEEE/CVF Conference on Computer Vision and Pattern Recognition. 2021: 10318-10327. D-Nerf 较 NeRF 的改进 1 就是能够建模移动或变形的物体&#…

2021年上半年上午易错题(软件设计师考试)

1.在CPU中&#xff0c;用&#xff08;A&#xff09;给出将要执行的下一条指令在内存中的地址。 A.程序计数器 B.指令寄存器 C.主存地址寄存器 D.状态条件寄存器 3.采用DMA方式传送数据时&#xff0c;每传送一个数据都需要占用一个&#xff08;C&#xff09;。 A.指令周期…

最新Microsoft Edge浏览器如何使用圆角

引入 最近我看了edge官方的文档&#xff0c;里面宣传了edge的最新UI设计&#xff0c;也就是圆角&#xff0c;但是我发现我的浏览器在升级至最新版本之后&#xff0c;却没有圆角 网上有很多人说靠实验性功能即可解锁&#xff0c;但是指令我都试过了&#xff0c;每次都是搜索无结…

生鲜冻货经营小程序商城的效果如何

海鲜肉类也是人们的主要餐食之一&#xff0c;现产和冻货都有很高的市场需求度&#xff0c;由于受众广&#xff0c;因此对商家来说&#xff0c;除了本地生意外&#xff0c;外地生意才有更大的拓展空间。 但信息发展下&#xff0c;仅依靠电话、微信私信显然难以完善&#xff0c;…

SpringBoot集成与应用Neo4j

文章目录 前言集成使用定义实体配置定义Repository查询方法方式一&#xff1a;Query方式二&#xff1a;Cypher语法构建器方式三&#xff1a;Example条件构建器方式四&#xff1a;DSL语法 自定义方法自定义接口继承自定义接口实现自定义接口neo4jTemplateNeo4jClient 自定义抽象…

服务器资源检查

1.检查CPU情况 # 型号及核数 cat /proc/cpuinfo | grep name | cut -f2 -d: | uniq -c # 查看物理cpu个数 cat /proc/cpuinfo | grep "physical id" | sort | uniq | wc -l # 查看逻辑cpu个数 cat /proc/cpuinfo | grep "processor" | wc -l cat /proc/…

python随手小练9(南农作业题)

题目1&#xff1a; 用python提供的相关函数计算数学表达式的值 具体操作&#xff1a; a float(input("x:")) #input()输入&#xff08;默认为字符串str形式&#xff09; #float()将()内类型转换为浮点型&#xff08;即有小数点&#xff09; b a (3*a)/4 #…

《你不知道的Javascript系列》——不可变性immutable

基础 JavaScript中存在以下数据类型&#xff1a; 原生&#xff08;基本&#xff09;类型 —— Boolean, Number, String非原始&#xff08;引用&#xff09;类型或对象 —— Object, Array, Function特殊 —— Null, Undefined 原生数据类型默认是不可变的&#xff0c;对于常…

好消息FL Studio21.2中文版本发布了!新功能详细介绍

为庆祝 FL Studio 21.2 的发布&#xff0c;所有 FL Studio 用户都可免费使用一个月的 FL Cloud。只需打开 FL Studio 21.2&#xff0c;点击浏览器中的 "声音 "标签即可开始使用。FL Cloud 提供完全集成的采样库、人工智能母带处理和由 DistroKid 支持的数字发行&…

bbr 流相互作用图示

类似 AIMD 收敛图&#xff0c;给出 bbr 的对应图示&#xff1a; bbr 多流相互作用非常复杂&#xff0c;和右下角的 AIMD 相比&#xff0c;毫无美感&#xff0c;但是看一眼左下角的 bbr 单流情况&#xff0c;又过于简陋&#xff0c;而 bbr 的核心就基于这简陋的假设。 浙江温…

【rust/esp32】在idf中使用embedded-graphics控制st7789 LCD屏幕

文章目录 说在前面模块详情准备工作开始编译烧录结果 说在前面 esp32版本&#xff1a;s3运行环境&#xff1a;esp-idf(std)开发环境&#xff1a;wsl2LCD模块&#xff1a;ST7789V2 240*280 LCDgithub地址&#xff1a;这里 模块详情 某宙的esp32s3开发板 某雪的1.69inch LCD模块…