1 克鲁斯卡尔算法 -图的最小生成树:任意两点之间都有一条线路可以相通
2 普里姆算法(优化) -图的最小生成树
3 匈牙利算法 -二分图最大分配
4 主元素问题 -寻找数量超过50%的元素
1 克鲁斯卡尔算法
解决图的最小生成树问题,即任意两点之间都有一条线路可以相通
//按照边的权值进行从小到大排序,选择权值较小的,两个顶点不在同一集合内的边(即不会产生回路的边),加入到树中,直到加入了n-1条边为止
//时间复杂度为O(nlogn)
//用一个结构体存储边的关系
struct edge {
int u;
int v;
int w;
};
struct edge e[10];
int n=7,m=7;
int f[7]={0},sum=0,count=0;
//快速排序
void realKuaiSu(int left,int right){
int i,j;
struct edge t;
if (left > right)
{
return;
}
i = left;
j = right;
while (i != j)
{
//先从右边开始找
while (e[j].w >= e[left].w && i= e[left].w && i
2 普里姆算法
//从点开始,在没有线连接的点之间寻找最短的线
//时间复杂度是O(n²)
void Prim(){
//边
int e[7][7] = {0};
//用book来标记一个顶点是否已经加入生成树
int book[7]={0},min=0,i=0,j=0,k=0;
//初始化dis数组,顶点到各个顶点的距离的数组
int dis[7] = {1,2,3,4,5,6,7};
book[1] = 1;
count++;
while (count < n)
{
min = 999999;
for (i = 1; i <= n; i++)
{
if (book[i]==0 && dis[i]e[j][k])
{
dis[k] = e[j][k];
}
}
}
//sum
}
使用堆来优化
//使用堆来优化
//book数组用来记录哪些顶点已经放入生成树中
int dis[7],book[7]={0};
//h用来保存堆,pos用来存储每个顶点在堆中的位置,size为堆的大小
int h[7],pos[7],size;
//交换函数,用来交换堆中的两个元素的值
void swap(int x,int y){
int t = h[x];
h[x] = h[y];
h[y] = t;
//更新到pos
t = pos[h[x]];
pos[h[x]] = pos[h[y]];
pos[h[y]] = t;
}
//堆 向下调整函数
void down(int i){
//flag用来标记是否需要继续向下调整
int t,flag=0;
while (i*2<=size && flag==0)
{
//比较i和它左儿子i*2在dis中的值,并用t记录值较小的结点编号
if (dis[h[i]] > dis[h[i*2]])
{
t = i * 2;
}
else
{
t = i;
}
//如果它有右儿子,再对右儿子进行讨论
if (i*2+1 <= size)
{
//如果右儿子的值更小,更新较小的结点编号
if (dis[h[t]] > dis[h[i*2+1]])
{
t = i*2+1;
}
}
//如果发现最小的结点编号不是自己,说明子结点中又比父结点更小的
if (t != i)
{
swap(t, i);
//更新i为刚才与它交换的儿子结点的编号,便于接下来继续向下调整
i = t;
}
else
{
//否则说明当前的父结点已经比两个子结点都要小,不需要再进行调整
flag = 1;
}
}
}
//传入一个需要向上调整的结点编号i
void up(int i){
//用来标记是否需要继续向上调整
int flag=0;
if (i==1)
{
return;
}
//不在堆顶,并且当前结点i的值比父结点小的时候继续向上调整
while (i!=1 && flag==0)
{
//判断是否比父结点小
if (dis[h[i]] < dis[h[i/2]])
{
//交换它和它父结点的位置
swap(i, i/2);
}
else
{
//表示已经不需要调整了,当前结点的值比父结点的值要大
flag = 1;
}
//更新编号i为它父结点的编号,便于下一次继续向上调整
i = i/2;
}
}
//从堆顶取一个元素
int pop(){
int t = h[1];
//将堆的最后一个点赋值到堆顶
h[1] = h[size];
pos[h[1]] = 1;
//堆的元素减少1
size--;
//向下调整
down(1);
//返回堆顶点
return t;
}
//普里姆算法
void PrimDui(){
int n=7,m=7,i,j,k;
//uvw要比2m的最大值大1,因为此图是无向图
//first要比n的最大值大1,比2*m的最大值大1
int u[19],v[19],w[19],first[7],next[19];
//count记录生成树中顶点的个数,sum用来存储路径之和
int count=0,sum=0;
//用邻接表存储边
for (i = 1; i <= n; i++)
{
first[i] = -1;
}
for (i = 1; i <= 2*m; i++)
{
next[i] = first[u[i]];
first[u[i]] = i;
}
//Prim核心部分开始
//将1号顶点加入生成树
//用book来标记一个顶点已经加入生成树
book[1] = 1;
count++;
//初始化dis数组,这里是1号顶点到其余各个顶点的初始距离
dis[1] = 0;
for (i = 2; i <= n; i++)
{
dis[i] = 999999;
}
k = first[1];
while (k!=-1)
{
dis[v[k]] = w[k];
k = next[k];
}
//初始化堆
size = n;
for (i = 1; i <= size; i++)
{
h[i] = i;
pos[i] = i;
}
for (i=size/2; i>=1; i--)
{
down(i);
}
//先弹出一个堆顶元素,即1号顶点
pop();
while (count < n)
{
j = pop();
book[j] = 1;
count++;
sum = sum+dis[j];
//扫描当前顶点j所有的边,再以j为中间结点,进行松弛
k = first[j];
while (k != -1)
{
if (book[v[k]] == 0 && dis[v[k]] > w[k])
{
//更新距离
dis[v[k]] = w[k];
//对该点在堆中进行向上调整
//存储的是顶点v[k]在堆中的位置
up(pos[v[k]]);
}
k = next[k];
}
}
//sum
}
//割点:在一个无向连通图中,如果删除某个顶点后,图不再连通(任意两点之间不能互相到达),称这样的顶点为割点
//割边:在一个无向连通图中,如果删除某条边后,图不再连通...
//使用邻接表来存储,时间复杂度为O(m+n)
//一个算法要选用合适的数据结构
//二分图最大分配
//二分图:如果一个图的所有顶点可以被分为x和y两个集合,并且所有边的两个顶点恰好一个属于集合x,另一个属于集合y,每个集合内的顶点没有边相连,那么就是二分图
//增加配对数称为增广路
3 匈牙利算法
求二分图的最大匹配
时间复杂度是O(nm)
int book[101];
int match[101];
int e[101][101];
int m,n;
int dfs(int u){
for (int i=1; i<=n; i++)
{
if (book[i]==0 && e[u][i]==1)
{
//标记i已访问过
book[i] = 1;
//如果点未被配对或者找到了新的配对
if (match[i]==0 || dfs(match[i]))
{
//更新配对关系
match[i] = u;
match[u] = i;
return 1;
}
}
}
return 0;
}
void xiongYaLi(){
//n个点,m条边
n=10,m=10;
//用二维数组存储
int t1,t2;
//配对数
int sum = 0;
for (int i=1; i<=m; i++)
{
scanf("%d%d", &t1,&t2);
e[t1][t2] = 1;
//无向图,反向存储一遍
e[t2][t1] = 1;
}
for (int i=1; i<=n; i++)
{
match[i] = 0;
}
for (int i=1; i<=n; i++)
{
for (int j=1; j<=n; j++)
{
//清空上次搜索时的标记
book[j] = 0;
//寻找增广路,如果找到,配对数加1
if (dfs(i))
{
sum++;
}
}
}
//sum
}
4 主元素问题
//现在有一个序列,其中一个数出现的次数超过了50%,请找出这个数
//投票系统中如果有一个人的票数超过50%,这个人立即当选
//寻找多数元素,主元素问题
//在原序列中去除两个不同的元素后,那么在原序列中的多数元素在新的序列中还是多数元素。抵消法,直至剩下的元素都一样。
int candidate(int a[], int m, int n)
{
int j = m, c = a[m], count = 1;
while (j < n && count > 0)
{
++ j;
if (a[j] == c)
++ count;
else
-- count;
}
if (j == n)
return c;
else
return candidate(a, j+1, n);
};
//第一遍扫描完成以后,count不等于1,所以把A[1]舍去
//如果所有的元素都已经扫描完毕并且计数器大于0,那么返回c作为多数元素的候选者
// a[1...n]
int Majority(int a[], int n)
{
int c = candidate(a, 1, 10);
int count = 0;
int majority;
for (int i = 1; i <= n; ++ i)
if (a[i] == c)
++ count;
if (n%2 == 0)
majority = n/2 + 1;
else
majority = n/2;
if (count > majority)
return c;
else
return -1;
};
void duoYuanSu()
{
int a[11];
for (int i = 1; i < 11; ++ i)
scanf("%d",a+i);
printf("%d\n",Majority(a, 10));
getchar();
getchar();
}