1.贪心算法求解最小生成树问题:
要求:分别用c/c++实现prim算法和Kruskal算法求解一个网络的最小生成树;
分析两种算法的时间复杂度和各自的特点
2.代码:
//c实现prim算法源代码:
#include
#include
#define N 100
int p[N], key[N], tb[N][N];
void prim(int v, int n)
{
int i, j;
int min;
for (i = 1; i <= n; i++)
{
p[i] = v;
key[i] = tb[v][i];
}
key[v] = 0;
for (i = 2; i <= n; i++)
{
min = INT_MAX;
for (j = 1; j <= n; j++)
if (key[j] > 0 && key[j] < min)
{
v = j;
min = key[j];
}
printf("%d--%d ", p[v], v);
key[v] = 0;
for (j = 1; j <= n; j++)
if (tb[v][j] < key[j])
p[j] = v, key[j] = tb[v][j];
}
}
int main()
{
int n, m;
int i, j;
int u, v, w;
while (scanf("%d%d", &n, &m))
{
for (i = 1; i <= n; i++)
{
for (j = 1; j <= n; j++)
tb[i][j] = INT_MAX;
}
while (m--)
{
scanf("%d%d%d", &u, &v, &w);
tb[u][v] = tb[v][u] = w;
}
prim(1, n);
printf("\n");
}
return 0;
}
#include
#include
#include
using namespace std;
#define MAXINT 1000 //定义了最大的权值
//图中的边的类
class Edge
{
public:
int head, tail;
double cost;
Edge(int h, int t, double c) :head(h), tail(t), cost(c){}
};
void prim(int ** matrix, int n, vector & mstree)
{
double * lowcost = new double[n];
int * nearver = new int[n];
//以0号节点作为树的根节点进行适当的初始化
nearver[0] = -1; //nearver中值为1的表示这个节点已经加到了生成数中了
lowcost[0] = MAXINT; //将0的权值设置为最大值
for (int i = 1; i < n; i++)
{
lowcost[i] = matrix[0][i]; //由于生成树中只有一个节点,因此每一个节点和生成树中最短的距离都是到0节点的距离
nearver[i] = 0; //对应的节点是0
}
//进行n-1次循环,每一次向生成树的节点集合中添加一个节点,向生成树中添加一条边
for (int i = 1; i < n; i++)
{
//首先从lowcost中找出最小的值,它对应的节点就是下一个需要加到生成树节点集合中的节点。
double mincost = MAXINT;
int node = 0;
for (int j = 1; j < n; j++)
if (nearver[j] != -1 && lowcost[j] < mincost)
{
mincost = lowcost[j];
node = j;
}
//上边的循环已经找出了距离生成树最近的节点了,下边对相应的lowcost和nearver进行设置
Edge tem(nearver[node], node, mincost);
mstree.push_back(tem);
//cout << tem.cost << " " << tem.head << " " << tem.tail << endl;
lowcost[node] = MAXINT;
nearver[node] = -1;
//现在已经将新的node加到了生成树的节点的集合中了,此时需要对lowcost数组进行调整,因为
//有可能新添加的节点到不再树中的某个的节点的距离表现在的更短
for (int j = 1; j < n; j++)
if (matrix[node][j] < lowcost[j] && nearver[j] != -1)
{
lowcost[j] = matrix[node][j];
nearver[j] = node;
}
}
}
void test()
{
typedef int * INT;
INT * matrix = new INT[6];
for (int i = 0; i< 6; i++)
matrix[i] = new int[6];
for (int i = 0; i < 6; i++)
for (int j = 0; j < 6; j++)
matrix[i][j] = MAXINT;
matrix[0][3] = 5;
matrix[0][1] = 6;
matrix[0][2] = 1;
matrix[1][2] = 5;
matrix[2][3] = 5;
matrix[1][4] = 3;
matrix[2][4] = 6;
matrix[2][5] = 4;
matrix[3][5] = 2;
matrix[4][5] = 6;
matrix[3][0] = 5;
matrix[1][0] = 6;
matrix[2][0] = 1;
matrix[2][1] = 5;
matrix[3][2] = 5;
matrix[4][1] = 3;
matrix[4][2] = 6;
matrix[5][2] = 4;
matrix[5][3] = 2;
matrix[5][4] = 6;
vector edges;
prim(matrix, 6, edges);
cout << edges.size() << endl;
double cost = 0;
for (int i = 0; i < edges.size(); i++)
{
cost += edges[i].cost;
cout << edges[i].head << " " << edges[i].tail << " " << edges[i].cost << endl;
}
cout << cost << endl;
}
int main()
{
test();
return 0;
}
c实现kruskal算法源代码:
#include
#include
#define MAX_VEX_NUM 50
#define MAX_ARC_NUM 100
#define UN_REACH 1000
typedef char VertexType;
typedef enum {
DG, UDG
} GraphType;
typedef struct {
VertexType vexs[MAX_VEX_NUM];
int arcs[MAX_VEX_NUM][MAX_VEX_NUM];
int vexnum, arcnum;
GraphType type;
} MGraph;
int getIndexOfVexs(char vex, MGraph *MG) {
int i;
for (i = 1; i <= MG->vexnum; i++) {
if (MG->vexs[i] == vex) {
return i;
}
}
return 0;
}
void create_MG(MGraph *MG) {
int i, j, k, weight;
int v1, v2, type;
char c1, c2;
type = 0;
if (type == 0)
MG->type = DG;
else if (type == 1)
MG->type = UDG;
else {
printf("Please input correct graph type DG(0) or UDG(1)!");
return;
}
printf("请输入顶点个数: ");
scanf("%d", &MG->vexnum);
printf("请输入边的个数: ");
scanf("%d", &MG->arcnum);
getchar();
for (i = 1; i <= MG->vexnum; i++) {
printf("请输入第%d个顶点为:", i);
scanf("%c", &MG->vexs[i]);
getchar();
}
//初始化邻接矩阵
for (i = 1; i <= MG->vexnum; i++) {
for (j = 1; j <= MG->vexnum; j++) {
if (i == j)
MG->arcs[i][j] = 0;
else
MG->arcs[i][j] = UN_REACH;
}
}
//输入边的信息,建立邻接矩阵
for (k = 1; k <= MG->arcnum; k++) {
printf("请输入一条边的两个顶点以及边的距离 ", k);
scanf("%c %c %d", &c1, &c2, &weight);
v1 = getIndexOfVexs(c1, MG);
v2 = getIndexOfVexs(c2, MG);
if (MG->type == 1)
MG->arcs[v1][v2] = MG->arcs[v2][v1] = weight;
else
MG->arcs[v1][v2] = weight;
getchar();
}
}
void print_MG(MGraph MG) {
int i, j;
/*if (MG.type == DG){
printf("Graph type: Direct graph\n");
}
else{
printf("Graph type: Undirect graph\n");
}*/
printf("图的顶点个数为: %d\n", MG.vexnum);
printf("图的边数为: %d\n", MG.arcnum);
printf("顶点设置为:\n ");
for (i = 1; i <= MG.vexnum; i++)
printf("%c\t", MG.vexs[i]);
printf("\n邻接矩阵为:\n");
for (i = 1; i <= MG.vexnum; i++) {
j = 1;
for (; j < MG.vexnum; j++) {
printf("%d\t", MG.arcs[i][j]);
}
printf("%d\n", MG.arcs[i][j]);
}
}
// 定义边结构体
typedef struct{
int start;
int end;
int cost;
}Edge;
void init_edge(MGraph MG, Edge edge[]){
int i, j;
int count = 0;
if (MG.type == 0){
for (i = 1; i <= MG.vexnum; i++){
for (j = 1; j <= MG.vexnum; j++){
if (MG.arcs[i][j] != 0 && MG.arcs[i][j] != UN_REACH){
edge[count].start = i;
edge[count].end = j;
edge[count].cost = MG.arcs[i][j];
count++;
}
}
}
}
else{
for (i = 1; i <= MG.vexnum; i++){
for (j = i; j <= MG.vexnum; j++){
if (MG.arcs[i][j] != 0 && MG.arcs[i][j] != UN_REACH){
edge[count].start = i;
edge[count].end = j;
edge[count].cost = MG.arcs[i][j];
count++;
}
}
}
}
}
void sort_edge(Edge edge[], int arcnum){
int i, j;
Edge temp;
for (i = 0; i < arcnum - 1; i++){
for (j = i + 1; j < arcnum; j++){
if (edge[i].cost > edge[j].cost){
temp = edge[i];
edge[i] = edge[j];
edge[j] = temp;
}
}
}
}
void print_edge(Edge edge[], int arcnum){
int i = 0;
while (i < arcnum){
printf("%d,%d,%d\n", edge[i].start, edge[i].end, edge[i].cost);
i++;
}
}
int findFather(int father[], int v){
int t = v;
while (father[t] != -1)
t = father[t];
return t;
}
void Kruskal_MG(MGraph MG, Edge edge[]){
int father[MAX_VEX_NUM];
int i, count, vf1, vf2;
// 初始化father数组
for (i = 0; i < MAX_VEX_NUM; i++){
father[i] = -1;
}
i = 0;
count = 0; // 统计加入最小生树中的边数
// 遍历任意两个结点之间的边
while (i < MG.arcnum && count < MG.arcnum){
vf1 = findFather(father, edge[i].start);
vf2 = findFather(father, edge[i].end);
// 如果这两个节点不属于同一个连通分量,则加入同一个连通分量
if (vf1 != vf2){
father[vf2] = vf1;
count++;
printf("%c,%c,%d\n", MG.vexs[edge[i].start], MG.vexs[edge[i].end], edge[i].cost);
}
i++;
}
}
int main(void) {
MGraph MG;
Edge edge[MAX_ARC_NUM];
create_MG(&MG);
print_MG(MG);
init_edge(MG, edge);
sort_edge(edge, MG.arcnum);
printf("最小生成树的结果为:\n");
Kruskal_MG(MG, edge);
return 0;
}
C++实现Kruskal算法源代码:
#include
#include
using namespace std;
struct EdgeNode
{
int v1; int v2; int value;
bool operator<(const EdgeNode &a) const
{
return a.value pq;
int Find(int x)
{
int i = x;
while (i != root[i])
i = root[i];
while (i != root[x])
{
x = root[x];
root[x] = i;
}
return i;
}
void Union(int a, int b)
{
a = Find(a);
b = Find(b);
if (a != b)
root[a] = b;
}
void Kruskal()
{
EdgeNode b;
cout << "加入最小生成树中的边依次为 : " << endl;
while (!pq.empty())
{
b = pq.top();
pq.pop();
if (Find(b.v1) != Find(b.v2))
{
cout << b.v1 << "----" << b.v2 << endl;
Union(b.v1, b.v2);
}
}
}
void main()
{
int n = 0; int m = 0;
cout << "请输入图中点的个数 : " << endl; cin >> n;
root = new int[n + 1];
for (int i = 1; i <= n; i++) root[i] = i;
cout << "请输入图中边的条数 : " << endl;
cin >> m;
EdgeNode a;
cout << "请依次输入每条边的两个顶点及其权重 : " << endl;
while (m--)
{
cin >> a.v1 >> a.v2 >> a.value;
pq.push(a);
}
Kruskal();
}
比较prim算法和kruskal算法的时间复杂度和各自的特点:
Prim算法这里是用邻接矩阵来保存图的,适合于稠密图,时间复杂度是O(n^2),观察代码很容易发现,时间主要用于每次遍历所有点寻找最小距离的顶点,在这点上,可以使用堆来优化,使得每次可以在log级别的时间内找到距离最小的点,复杂度为O(e+vlogv)。
Kruskal算法对所有的边进行排序,复杂度为O(eloge),适合于稀疏图。我认为无论在算法的复杂度上还是编程的复杂度上都比较偏向于prim,对于n比较大的矩阵,prim算法时间复杂度更小,对于比较小的矩阵我觉得从时间复杂度上讲两者的区别不是很大,所以总体来说使用prim比kruskal算法是更优的选择。