数据结构中关于最小生成树的步骤

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了数据结构中关于最小生成树的步骤相关的知识,希望对你有一定的参考价值。

普里姆算法的基本思想:取图中任意一个顶点 v 作为生成树的根,之后往生成树上添加新的顶点 w。在添加的顶点 w 和已经在生成树上的顶点v 之间必定存在一条边,并且该边的权值在所有连通顶点 v 和 w 之间的边中取值最小。之后继续往生成树上添加顶点,直至生成树上含有 n-1 个顶点为止。
克鲁斯卡尔算法
克鲁斯卡尔算法的基本思想:为使生成树上边的权值之和达到最小,则应使生成树中每一条边的权值尽可能地小。
具体做法: 先构造一个只含 n 个顶点的子图 SG,然后从权值最小的边开始,若它的添加不使SG 中产生回路,则在 SG 上加上这条边,如此重复,直至加上 n-1 条边为止。
参考技术A #include<stdio.h>
#include<stdlib.h>
#define INFINITY 100000//相当于无穷大
#define MAX_VERTEX_NUM 20//最多能有多少个点
//邻接矩阵图
typedef struct
char vexs[MAX_VERTEX_NUM];
int arcs[MAX_VERTEX_NUM][MAX_VERTEX_NUM];
int vexnum,arcnum;
MGraph;
//保存路径起始点,终点,以及权值
typedef struct
int adjvex;
int endvex;
int lowcost;
closedge[MAX_VERTEX_NUM];
//创建邻接矩阵
void CreateUDN(MGraph &G);
//找到输入字符对应的数字
int LocateVex(MGraph G,char v);
//输出邻接矩阵图
void PrintUDN(MGraph G);
//找出最小生成树
void MiniSpanTree_PRIM(MGraph G,closedge &minedge);
//输出最小生成树的每条边的起点,终点和权值
void PrintMinEdge(MGraph G,closedge minedge);
int main()

MGraph G;
closedge minedge;
CreateUDN(G);
printf("该图的邻接矩阵存储示意图如下:\n");
PrintUDN(G);
printf("\n");
MiniSpanTree_PRIM(G,minedge);
printf("该图生成树的边如下:\n");
PrintMinEdge(G,minedge);
printf("\n");
return 0;

//创建邻接矩阵
void CreateUDN(MGraph &G)

int i,j;
char vex1,vex2;//起点,终点字符
int vex1Index,vex2Index;//字符对应的数字
int weight;
char ch;
printf("请输入有多少个顶点,多少条边:\n");
scanf("%d%d",&G.vexnum,&G.arcnum);
printf("请输入顶点向量:\n");
scanf("%s",G.vexs);
//初始化都为0
for(i=0;i<G.arcnum;i++)
for(j=0;j<G.arcnum;j++)
G.arcs[i][j]=0;
//输入并赋值有路径的
printf("请输入所有边的起点,终点和权值:\n");
for(i=0;i<G.arcnum;i++)

while((ch=getchar())!='\n'); //吸收换行符
scanf("%c%c",&vex1,&vex2);
scanf("%d",&weight);
vex1Index=LocateVex(G,vex1);
vex2Index=LocateVex(G,vex2);
G.arcs[vex1Index][vex2Index]=G.arcs[vex2Index][vex1Index]=weight;

//剩下没路径的(当然不包括自己到自己)赋值无穷大
for(i=0;i<G.arcnum;i++)

for(j=0;j<G.arcnum;j++)
if(G.arcs[i][j]==0&&i!=j)

G.arcs[i][j]=INFINITY;



//将输入的字符转换成对应的数字(A-0,B-1,...)
int LocateVex(MGraph G,char v)

int i;
for(i=0;i<G.vexnum;i++)
if(v==G.vexs[i])
return i;

//输出对应矩阵
void PrintUDN(MGraph G)

int i,j;
for(i=0;i<=G.vexnum;i++)

for(j=0;j<=G.vexnum;j++)

if(i==0&&j==0)
printf("\t");
else if(i==0)

printf("%c\t",G.vexs[j-1]);

else if(j==0)

printf("%c\t",G.vexs[i-1]);

else

if(G.arcs[i-1][j-1]==INFINITY)
printf("∞\t");
else
printf("%d\t",G.arcs[i-1][j-1]);


printf("\n");


//生成最小生成树(实则就是记录最小路径的起点,终点,权值)
void MiniSpanTree_PRIM(MGraph G,closedge &minedge)

int i,j,k,z;
int temp;
int currentmin;
//起始初始化
k=0;
for(j=1;j<G.vexnum;++j)

minedge[j-1].adjvex=k;
minedge[j-1].endvex=j;
minedge[j-1].lowcost=G.arcs[k][j];

//找最小路径
for(i=0;i<G.vexnum-1;++i)

//找第一个路径最短的可达点
currentmin=minedge[i].lowcost;
k=i;
for(j=i+1;j<G.vexnum-1;j++)

if(minedge[j].lowcost<currentmin)

currentmin=minedge[j].lowcost;
k=j;


//做相应操作

temp=minedge[i].adjvex;
minedge[i].adjvex=minedge[k].adjvex;
minedge[k].adjvex=temp;
temp=minedge[i].endvex;
minedge[i].endvex=minedge[k].endvex;
minedge[k].endvex=temp;
temp=minedge[i].lowcost;
minedge[i].lowcost=minedge[k].lowcost;
minedge[k].lowcost=temp;

//依次找后面的可达最小路径
for(j=i+1;j<G.vexnum-1;++j)

z=minedge[i].endvex;
k=minedge[j].endvex;
if(k!=z)

if(G.arcs[z][k]<minedge[j].lowcost)

minedge[j].adjvex=z;
minedge[j].lowcost=G.arcs[z][k];





//输出最小生成树
void PrintMinEdge(MGraph G,closedge minedge)

int i;
for(i=0;i<G.vexnum-1;i++)
printf("%c%c\t%d\n",G.vexs[minedge[i].adjvex],G.vexs[minedge[i].endvex],minedge[i].lowcost);
参考技术B 比较典型的是Prim算法和Kruskal算法。 参考技术C jrj

数据结构中关于树的一切(java版)

每晚八点,我们在社区分享知识。

乐乐:sensus113  数据结构中关于树的一切(java版)(二)美果大冰:xj73226

备注入群,谢谢!数据结构中关于树的一切(java版)(二)

今天继续周二的分享~~

当我们实例化一个对象时,我们把值(点的相关数据)作为参数传递给类。看上面类的左孩子节点和右孩子节点。两个都被赋值为null。

为什么?

因为当我们创建节点时,它还没有孩子,只有节点数据。

代码测试

   /**

     * 构建树

     */

    public static void testCreate() {

        BinaryTree node = new BinaryTree("a");


        System.out.println("【node data】:" + node.getData());

        System.out.println("【node left data】:" + (node.left==null?"null":node.left.getData()));

        System.out.println("【node right data】:" + (node.right==null?"null":node.right.getData()));

    }

输出:

【node data】:a

【node left data】:null

【node right data】:null


我们可以将字符串'a'作为参数传给二叉树节点。如果将值、左孩子节点、右孩子节点输出的话,我们就可以看到这个值了。

下面开始插入部分的操作。那么我们需要做些什么工作呢?

有两个要求:

  • 如果当前的节点没有左孩子节点,我们就创建一个新节点,然后将其设置为当前节点的左节点。

  • 如果已经有了左节点,我们就创建一个新节点,并将其放在当前左节点的位置。然后再将原左节点值为新左节点的左节点。

图形如下:

数据结构中关于树的一切(java版)(二)

下面是插入的代码:

 /**

     * 插入节点 ,如果当前的节点没有左节点,我们就创建一个新节点,然后将其设置为当前节点的左节点。

     *

     * @param node

     * @param value

     */

    public static void insertLeft(BinaryTree node, String value) {

        if (node != null) {

            if (node.left == null) {

                node.setLeft(new BinaryTree(value));

            } else {

                BinaryTree newNode = new BinaryTree(value);

                newNode.left = node.left;

                node.left = newNode;

           }

        } 

    }

再次强调,如果当前节点没有左节点,我们就创建一个新节点,并将其置为当前节点的左节点。否则,就将新节点放在左节点的位置,再将原左节点置为新左节点的左节点。

同样,我们编写插入右节点的代码

/**

    * 同插入左节点

    * @param node

    * @param value

    */

   public static void insertRight(BinaryTree node, String value) {

       if (node != null) {

           if (node.right == null) {

               node.setRight(new BinaryTree(value));

           } else {

               BinaryTree newNode = new BinaryTree(value);

               newNode.right = node.right;

               node.right = newNode;

           }

       } 

   }

但是这还不算完成。我们得测试一下。

我们来构造一个像下面这样的树:

数据结构中关于树的一切(java版)(二)

  • 有一个根节点

  • b是左节点

  • c是右节点

  • b的节点是d(b没有左结点)

  • c的左节点是e

  • c的右节点是f

  • e,f都没有子节点

下面是这棵树的实现代码:

/**

     * 测试插入结点

     */

    public static void testInsert() {

        BinaryTree node_a = new BinaryTree("a");

        node_a.insertLeft(node_a, "b");

        node_a.insertRight(node_a, "c");


        BinaryTree node_b = node_a.left;

        node_b.insertRight(node_b, "d");


        BinaryTree node_c = node_a.right;

        node_c.insertLeft(node_c, "e");

        node_c.insertRight(node_c, "f");


        BinaryTree node_d = node_b.right;

        BinaryTree node_e = node_c.left;

        BinaryTree node_f = node_c.right;


        System.out.println("【node_a data】:" + node_a.getData());

        System.out.println("【node_b data】:" + node_b.getData());

        System.out.println("【node_c data】:" + node_c.getData());

        System.out.println("【node_d data】:" + node_d.getData());

        System.out.println("【node_e data】:" + node_e.getData());

        System.out.println("【node_f data】:" + node_f.getData());

    }

输出:

【node_a data】:a

【node_b data】:b

【node_c data】:c

【node_d data】:d

【node_e data】:e

【node_f data】:f


插入已经结束

现在,我们来考虑一下树的遍历。

树的遍历有两种选择,深度优先搜索(DFS)和广度优先搜索(BFS)。

DFS是用来遍历或搜索树数据结构的算法。从根节点开始,在回溯之前沿着每一个分支尽可能远的探索。  —  Wikipedia

BFS是用来遍历或搜索树数据结构的算法。从根节点开始,在探索下一层邻居节点前,首先探索同一层的邻居节点。 —  Wikipedia

下面,我们来深入了解每一种遍历算法。


深度优先搜索(Depth-First Search,DFS)

DFS 在回溯和搜索其他路径之前找到一条到叶节点的路径。让我们看看这种类型的遍历的示例。

数据结构中关于树的一切(java版)(二)

输出结果为: 1–2–3–4–5–6–7

为什么?

让我们分解一下:

  1. 从根节点(1)开始。输出

  2. 进入左节点(2)。输出

  3. 然后进入左孩子(3)。输出

  4. 回溯,并进入右孩子(4)。输出

  5. 回溯到根节点,然后进入其右孩子(5)。输出

  6. 进入左孩子(6)。输出

  7. 回溯,并进入右孩子(7)。输出

  8. 完成

当我们深入到叶节点时回溯,这就被称为 DFS 算法。

既然我们对这种遍历算法已经熟悉了,我们将讨论下 DFS 的类型:前序、中序和后序。

前序遍历

这和我们在上述示例中的作法基本类似。

  1. 输出节点的值

  2. 进入其左节点并输出。当且仅当它拥有左节点。

  3. 进入右节点并输出之。当且仅当它拥有右节点

/**

     * 前序遍历

     *

     * @param node

     */

    public static void preOrder(BinaryTree node) {

        if (node != null) {


            System.out.println(node.data);


            if (node.left != null) {

                node.left.preOrder(node.left);

            }


            if (node.right != null) {

                node.right.preOrder(node.right);

            }

        }

    }

中序遍历

数据结构中关于树的一切(java版)(二)

示例中此树的中序算法的结果是3–2–4–1–6–5–7。

左节点优先,之后是中间,最后是右节点。

代码实现:

/**

     * 中序遍历

     *

     * @param node

     */

    public static void inOrder(BinaryTree node) {

        if (node != null) {

            if (node.left != null) {

                node.left.inOrder(node.left);

            }


            System.out.println(node.data);


            if (node.right != null) {

                node.right.inOrder(node.right);

            }

        }

    }

  1. 进入左节点并输出之。当且仅当它有左节点。

  2. 输出根节点的值。

  3. 进入结节点并输出之。当且仅当它有结节点。

后序遍历

数据结构中关于树的一切(java版)(二)

以此树为例的后序算法的结果为 3–4–2–6–7–5–1 。

左节点优先,之后是右节点,根节点的最后。

代码实现:

/**

     * 后序遍历

     *

     * @param node

     */

    public static void postOrder(BinaryTree node) {

        if (node != null) {

            if (node.left != null) {

                node.left.postOrder(node.left);

            }


            if (node.right != null) {

                node.right.postOrder(node.right);

            }


            System.out.println(node.data);

        }

    }

广度优先搜索(BFS)

BFS是一层层逐渐深入的遍历算法

数据结构中关于树的一切(java版)(二)

下面这个例子是用来帮我们更好的解释该算法。


数据结构中关于树的一切(java版)(二)

我们来一层一层的遍历这棵树。本例中,就是1-2-5-3-4-6-7.

  • 0层/深度0:只有值为1的节点

  • 1层/深度1:有值为2和5的节点

  • 2层/深度2:有值为3、4、6、7的节点

代码实现:

/**

     * 广度排序

     *

     * @param node

     */

    public static void bfsOrder(BinaryTree node) {

        if (node != null) {

            Queue<BinaryTree> queue = new ArrayDeque<BinaryTree>();

            queue.add(node);


            while (!queue.isEmpty()) {

                BinaryTree current_node = queue.poll();


                System.out.println(current_node.data);


                if (current_node.left != null) {

                    queue.add(current_node.left);

                }

                if (current_node.right != null) {

                    queue.add(current_node.right);

                }

            }

        }

    }

为了实现BFS算法,我们需要用到一个数据结构,那就是队列。


本文转载自https://juejin.im/post/5ad56de7f265da2391489be3

未完待续~~下期再见


上期干货可点击“数据结构中关于树的一切(java版)(一)”查阅


 数据结构中关于树的一切(java版)(二)每晚八点,我们在社区等你。数据结构中关于树的一切(java版)(二)



数据结构中关于树的一切(java版)(二)

我们是祖国的花朵


数据结构中关于树的一切(java版)(二)

Nervos CKB 唯一官网:Nervos.org

欢迎关注Nervos Fans

Nerovs CKB 爱好者社区

Nervos Fans如下频道:

NervosFans twitter:@nervosfans

NervosFans 微博:@NervosFans

NervosFans 微信公号:Nervosfans

入群请加乐乐微信:sensus113

美果大冰微信:xj73226

备注入群,谢谢!





点击“阅读原文”查看原文



以上是关于数据结构中关于最小生成树的步骤的主要内容,如果未能解决你的问题,请参考以下文章

❤️数据结构入门❤️(3 - 4)- 最小生成树

Kruskal算法和Prim算法构造它的一棵最小代价生成树的过程

最小生成树

最小生成树的问题----数据结构

浅谈图论——最小生成树

利用普里姆算法求解最小生成树,写出步骤或画图表示过程。