问题
自然界中很多的事物之间是呈现为网状关系的,而如何处理这个网络中的问题呢?
比如如何知道网络中的一个节点有没有可能能连接到另一个节点?
一个节点到另一个节点的最短距离或者最长距离?
比如计算机网络中一个路由节点怎么找到另一个指定的路由节点?
怎么样效率最高,时间最短,对整个网络的影响最小?
最短路径问题应该是图中出现较多的问题
那么如何解决最短路径问题
最短路径问题
在图中,节点与节点以边相连,一类最短路径是指节点到节点之间最短经过几条边,即最短路径以边数衡量,每个边都是一样的;另一类最短路径是指节点与节点之间的节点与节点之间的边是有数值的,也就是说是有权重的,每个边可能都不相同,计算最短路径要计算边上的权重相加最小才是最短路径。
广度优先搜索
实现原理
广度优先搜索由一点开始,先将与该点有直接联系的所有点放入一个队列(队列是一种先进先出的数据结构)中,再寻找与已经进入队列的节点直接相连的节点作为第二级元素放入队列,以此类推,直到图中的所有元素都存入队列中,这样通过对队列遍历就实现了对图的遍历,可以查找指定的元素
思考
可以边往队列中添加边遍历
边入队边出队
不过如果需要使用前面队列中的一级节点来添加二级节点,
可能前面的一级节点已经被弹出了,
不过如果这个一级节点已经使用过或许不会影响新节点的加入,
不过还有一个问题,
比如还需要判断新加入的二级节点是不是已经是一级节点了
如果有的一级节点连上了另一个一级节点,那么从这个一级节点看过去,另一个一级节点仿佛是二级的,不过该节点已经加入了队列,不需要再加入了,防止重复,
假如这个另一个节点已经弹出了,就无法判断是不是已经判断过了,
那如果加入了话,就会重复添加其连接的后面的节点,会很多不必要的重复的操作,浪费时间,消耗更多的资源
【这是在查找时会造成的问题,如果要进行更复杂的操作或者计算的话,可能会造成更多的错误】
如何创建一个图?
Python中有散列表,即字典
可以在字典中嵌套字典,实现图
C中有map(先试试)deque
将第一个节点作为键,其直接连接的节点作为值,添加进map或dict()
遍历第一个节点的键值对,对其中每个值找出其直接相连的节点,以该值作为新的键,其直接相连的节点作为值,再创建一个属于该节点的键值对
以此类推
。。。
直到最后的节点没有与其直接相连的其他节点
【注意:找二级节点的相连节点时,要将那个与他相连的第一个节点去掉】
如何遍历一个图?
使用广度优先搜索或者深度优先搜索
如何在图中查找?
将字典或map中的第一个键值对的键和值存入队列
遍历第一个键值对的值
将该值作为键的对应的值添加进队列
以此类推,直到键值对中的值不再是一个键
。。。
【那么队列的形式呢?】
队列如果用循环队列的话,也是有大小限制的,
如果一个图中的节点数未知的话就不好确定循环队列的容量【那有没有确定散列表(dict(),map)内元素个数的方法?】
可以使用链队
主要方法
Python
dict()的使用
deque的使用
C/C++
map的使用
链队的使用
参考代码:
#include<bits/stdc++.h>
using namespace std;
class Person
{
private:
string name;
int age;
bool sex;
public:
Person(string n,int a,bool s)
{
name=n;
age=a;
sex=s;
}
void show()
{
cout<<name<<" "<<age<<" "<<(sex?"male":"female")<<endl;
}
};
int main()
{
vector <map<string,vector<Person> > > M;
string Name;
int num;
// cin>>Name>>num;
vector<Person> f;
vector<Person>::iterator iter;
for(int i=0;i<4;i++)
{
// string n;
// int a;
// int s;
// cin>>n>>a>>s;
Person p("Tom",10,0);
f.push_back(p);
}
M.insert(make_pair("Wangshuo",f));
cout<<Name<<endl;
f.clear();
f = (M.begin())->second;
for(iter = f.begin(); iter < f.end(); iter++)
{
(*iter).show();
}
}
如何创建一个图?
使用map和vector
#include <bits/stdc++.h>
#include <vector>
#include <map>
using namespace std;
int build_im()
{
map<string, vector<string> > tu;
map<string, vector<string> >::iterator tu_iter;
vector<string>::iterator er_iter;
cout<<"输入 / 完成一个键值对的输入\n输入 > 结束输入\n"<<endl;
int num =0;
for(int j =0;; j++)
{
vector<string> er;
string me;
if(num==0)
{
cout<<"first :"<<endl;
}
else{
cout<<"-------------------"<<endl;
}
cin>>me;
if(me == ">")
{
break;
}
if(me == "")
{
cout<<"name should not be empty"<<endl;
break;
}
for(int i =0;; i++)
{
string er_name;
cin>>er_name;
if(er_name == "/")
{
break;
}
else if(er_name == "")
{
}
else
{
er.push_back(er_name);
}
}
num++;
tu.insert(make_pair(me,er));
}
for(tu_iter = tu.begin();tu_iter!=tu.end();tu_iter++)
{
cout<<"名字:"<<tu_iter->first<<endl;
for(er_iter = (tu_iter->second).begin();er_iter!=(tu_iter->second).end();er_iter++)
{
cout<<"好友:"<<*er_iter<<endl;
}
cout<<"***************"<<endl;
}
}
int main()
{
build_im();
return 0;
}
如何遍历一个图?如何将图中元素加入链队?如何实现广度优先搜索?
如果将图中的元素由第一个开始,将该元素存入链队,再将这个元素为键时,其值存入链队,再遍历其值,当其值为键时,其值的值存入链队,使用递归即可
递归函数一开始传入起始元素
遍历该元素的值
存入链队,并再将其每个值传入该递归函数
在存储的时候使用循环,一个个遍历一下链队,查看元素是否已经存在,存在就不存入
【可是这样一来,该句就产生了O(n*n)的时间复杂度,导致整个程序的效率非常低,链队不适合查找判断是否已经存储某个元素】
结束条件
其实如果只是要判断某个元素是否在图中,上面代码中对map的遍历就可以解决了,不需要用到队列
不过如果要求一个点到另一个点的最短路径,就不好说了
首先要在图中找到该点
再找该点直接相连的点
再找相连的点的相连的点【注意要防止重复】
以此类推
。。。
在这个过程中每查完一层,定义一个数加一
由于实现链队会比较麻烦,而且实现了效率也不高,故换一个方法