of {$slidecount} ½ {$title} ATZJG.NET {$author}

首页






网络优化
图论


Haifeng Xu


(hfxu@yzu.edu.cn)

This slide is based on the book:
赵静、但琦 主编《数学建模与数学实验》(第4版)

目录

图论的基本概念

图论的基本概念

图的定义

有序三元组 $G=(V,E,\Psi)$ 称为一个图, 其中

在图 $G$ 中,

若将图 $G$ 的每一条边 $e$ 都对应一个实数 $w(e)$, 则称 $w(e)$ 为该边的, 并称图 $G$ 为赋权图.

记号约定

记 $V(G)$ 为图 $G$ 的顶点集, $E(G)$ 为图 $G$ 的边集. 记号 $\nu=|V(G)|$ 和 $\varepsilon=\varepsilon(G)=|E(G)|$ 分别表示图 $G$ 的顶点数和边数.

常用术语

  1. 端点相同的边称为.
  2. 若一对顶点之间有两条以上的边连接(联结), 则称这些边为重边.
  3. 存在边相连的两个顶点称为相邻的顶点, 有一个公共端点的边称为相邻的边.
  4. 边和它的端点称为互相关联的.
  5. 既没有环也没有重边的图, 称为简单图.
  6. 任意两个顶点都相邻的简单图, 称为完备图, 记为 $K_n$, 其中 $n$ 为顶点的数目.
  7. 若 $V=X\cup Y$, 且 $X\cap Y=\emptyset$, $X$ 中任意两个顶点不相邻, $Y$ 中任意两个顶点不相邻, 则称 $G$ 为二元图(或二部图). 若 $X$ 中每个顶点皆与 $Y$ 中的一切顶点相邻(反之可以推出来), 则称其为完备二元图, 记为 $K_{m,n}$, 其中 $m,n$ 分别为 $X$ 和 $Y$ 的顶点数目.

顶点的次数

在无向图中, 与顶点 $v$ 关联的边的数目(环算两次)称为 $v$ 的次数, 记为 $d(v)$.

在有向图中, 从顶点 $v$ 引出的边的数目称为 $v$ 的出度, 记为 $d^{+}(v)$, 而指向顶点 $v$ 的边的数目称为 $v$ 的入度, 记为 $d^{-}(v)$. $d(v)=d^{+}(v)+d^{-}(v)$ 称为 $v$ 的次数.

一些基本结论

一些基本结论

定理. 图 $G$ 的所有顶点的度数之和是所有顶点个数的两倍. \[ \sum_{v\in V(G)}d(v)=2\varepsilon(G). \]

证明. 每增加一条边, 顶点的个数增加2. 因此所有顶点的度数之和是所有顶点个数的两倍.

推论. 任何图中奇次顶点的总数必为偶数.

证明. 设图 $G$ 中奇次顶点分别为 $v_{i_1},v_{i_2},\ldots,v_{i_k}$. 它们的度(或次数)分别为 $d_{i_1},d_{i_2},\ldots,d_{i_k}$, 这些数均为奇数. 由于 \[ d_{i_1}+d_{i_2}+\ldots+d_{i_k}+\text{剩余偶次顶点的次数之和}=2\varepsilon(G) \] 故 $k$ 是偶数.

子图

子图

子图

定义. 设图 $G=(V,E,\Psi)$, $G_1=(V_1,E_1,\Psi_1)$,

References

https://en.wikipedia.org/wiki/Glossary_of_graph_theory_terms#subgraph

图的矩阵表示

图的矩阵表示

关联矩阵

关联矩阵描述的是顶点与边的关系.

对无向图 $G$, 其关联矩阵 $M=(m_{ij})_{\nu\times\epsilon}$, 其中 \[ m_{ij}=\begin{cases} 1, & \text{若}\ v_i \text{与}\ e_j\ \text{相关联}\\ 0, & \text{若}\ v_i \text{与}\ e_j\ \text{不关联}\\ \end{cases} \]

对有向图 $G$, 其关联矩阵 $M=(m_{ij})_{\nu\times\epsilon}$, 其中 \[ m_{ij}=\begin{cases} 1, & \text{若}\ v_i \text{是}\ e_j\ \text{的起点}\\ -1, & \text{若}\ v_i \text{是}\ e_j\ \text{的终点}\\ 0, & \text{若}\ v_i \text{与}\ e_j\ \text{不关联}\\ \end{cases} \]

邻接矩阵

邻接矩阵描述的是顶点与顶点之间是否相连的关系.

对无向图 $G$, 其邻接矩阵 $A=(a_{ij})_{\nu\times\nu}$, 其中 \[ a_{ij}=\begin{cases} 1, & \text{若}\ v_i \text{与}\ v_j\ \text{相邻}\\ 0, & \text{若}\ v_i \text{与}\ v_j\ \text{不相邻}\\ \end{cases} \]

对有向图 $G=(V,E)$, 其邻接矩阵 $A=(a_{ij})_{\nu\times\nu}$, 其中 \[ a_{ij}=\begin{cases} 1, & \text{若}\ (v_i,v_j)\in E\\ 0, & \text{若}\ (v_i,v_j)\not\in E\\ \end{cases} \]

对有向赋权图 $G$, 其邻接矩阵 $A=(a_{ij})_{\nu\times\nu}$, 其中 \[ a_{ij}=\begin{cases} w_{ij}, & \text{若}\ e_{ij}=(v_i,v_j)\in E\ \text{且}\ w_{ij}\ \text{为边}\ e_{ij}\ \text{的权}\\ 0, & \text{若}\ i=j\\ \infty, & \text{若}\ (v_i,v_j)\not\in E \end{cases} \]

最短路问题及算法

最短路问题及算法

基本概念

[Def]关于图(graph)的基本概念---路径(path),道路(trail),通路(walk)

所谓的路径(path)是指一个图 $P$, 满足

\[ V(P)=\{x_0,x_1,\ldots,x_{\ell}\},\quad E(P)=\{x_0x_1, x_1x_2,\ldots,x_{\ell-1}x_{\ell}\}. \]

这里 $x_i\neq x_j$, $\forall\ i\neq j$.

这条路径 $P$ 通常记为 $x_0x_1x_2\ldots x_{\ell}$. 顶点 $x_0$ 和 $x_{\ell}$ 是路径 $P$ 的两个端点. 而 $\ell=e(P)$ 是路径 $P$ 的长度.

我们称 $P$ 是从 $x_0$ 到 $x_{\ell}$ 的一条路径(path), 或简记为 $x_0-x_{\ell}$ path. 当然 $P$ 也是一条从 $x_{\ell}$ 到 $x_0$ 的路径, 或简记为 $x_{\ell}-x_0$ path.

有时, 我们将强调 $P$ 是从 $x_0$ 到 $x_{\ell}$ 的, 并且称 $x_0$ 为起点(或始点)(initial vertex), $x_{\ell}$ 为终点(terminal vertex).

以 $x$ 为起点的道路称为 $x$-path.

大多数路径可以视为某给定图 $G$ 的一个子图.

图 $G$ 的一条通路(walk)是指一条由顶点与边组成的交错序列: \[ x_0,\alpha_1,x_1,\alpha_2,x_2,\ldots,\alpha_{\ell},x_{\ell}. \] 其中 $\alpha_i=x_{i-1}x_i$, $i=1,2,\ldots,\ell$. 这里顶点和边都可以重复. $\ell$ 称为这条通路(walk)的长度.

如果通路(walk)中限定边不能重复, 顶点可以重复, 则称这条通路为道路(trail).

因此, 所谓的路径, 实际上就是顶点和边均不重复的通路.

若道路(trail)的两端点重合(也即是一条闭合道路 a closed trail),则称其为一个回路(circuit).

设 $W=x_0x_1\ldots x_{\ell}$ 是一条通路(walk), 满足 $\ell\geqslant 3$, $x_0=x_{\ell}$. 并且诸 $x_i(0 < i < \ell)$ 互不相同, 也不同于 $x_0$, 则称 $W$ 是一个 圈(cycle).

为简单起见, 这个 cycle 记为 $x_1x_2\ldots x_{\ell}$. 注意到这里的记号不同于之前的, 这里 $x_1x_{\ell}$ 也是该 cycle 的一条边. 进一步的, $x_1x_2\ldots x_{\ell}$, $x_{\ell}x_{\ell-1}\ldots x_1$, $x_2x_3\ldots x_{\ell}x_1$, $x_ix_{i-1}\ldots x_1x_{\ell}x_{\ell-1}\ldots x_{i+1}$ 都指的是同一个 cycle.

记号: 符号 涵义 $P^{\ell}$ 长度为 $\ell$ 的一条路径(path) $C^{\ell}$ 长度为 $\ell$ 的一个圈 (cycle)

我们称 $C^3$ 是一个三角形(triangle), $C^4$ 是一个四边形(quadrilateral), $C^5$ 是一个五边形(pentagon), 等等.

一个 cycle 被称为是 even (或 odd) 的, 如果它的长度是 even (或 odd) 的.

给定顶点 $x,y$, 它们之间的距离 $d(x,y)$ 指连接这两点的所有路径中长度最短的那条路径的长度.

\[ d(x,y)=\min\{\text{length of } x-y\ \text{path}\} \]

若 $x,y$ 之间不存在 $x-y$ path, 则令 $d(x,y)=\infty$.

一个图称为是连通的(connected), 如果对于任意两个不同点组成的点对 $\{x,y\}$, 都存在一条从 $x$ 到 $y$ 的路径(path).

设无向图 $G(V,E)$ 是连通的, 设边集 $E_1\subset E$. 在图 $G$ 中删除 $E_1$ 中所有的边后得到的子图是不连通的, 而删除了 $E_1$ 的任一真子集后得到的子图是连通图, 则称 $E_1$ 是 $G$ 的一个割边集.

若割边集仅由一条边组成, 则称这条边为割边桥(bridge).

References:
Béla Bollobas, Graph Theory, An introductory course. GTM 63.

参见 http://www.atzjg.net/admin/do/view_question.php?qid=2106

Dijkstra 算法

Dijkstra 算法

MATLAB 代码

% Page 90.
% filename: Dijkstra.m
% based on the file: road1.m on page 90.
% by haifeng
% Date: 2018-04
w=[ 0   2   1   8   inf inf inf inf;
    2   0   inf 6   1   inf inf inf;
    1   inf 0   7   inf inf 9   inf;
    8   6   7   0   5   1   2   inf;
    inf 1   inf 5   0   3   inf 9  ;
    inf inf inf 1   3   0   4   6  ;
    inf inf 9   2   inf 4   0   3  ;
    inf inf inf inf 9   6   3   0  ];

n=size(w,2); % w 的列数

l=w(1,:);
z=ones(1,n);
% l = 0     2     1     8   Inf   Inf   Inf   Inf
% z = 1     1     1     1     1     1     1     1

% s 向量中存放的是图的各顶点标号. 注意 Matlab 中从1开始.
s=[];
s(1)=1; % s(1)=1 是顶点1.
u=s(1); % 第一个顶点通常记为 u_0, 即 u_0=s(1), 
k=1
l
z
u
'=================='
index_of_minValue=2;% 定义变量index_of_minValue(书中的 v), 循环中要使用.
% 注意: 不能初始化 v=0; 否则后面的 s(k+1 )=v; k=k+1; u=s(k); 会导致 u=0; 从而在第二次循环中
% 使得 if l(i)>l(u)+w(u,i) 语句中的 l(u) 变为 l(0). Matlab 会出错:
% 提示: 下标索引必须为正整数类型或逻辑类型。

while k < n
    for i=1:n
        if l(i) > l(u)+w(u,i)
            l(i)=l(u)+w(u,i);
            z(i)=u;
        end
    end

    
    ll=l;
    for i=1:n
        for j=1:k
            if i==s(j)
                ll(i)=inf;
            end
        end
    end
    
    minValue=inf;
    for i=1:n
        if ll(i) < minValue
            minValue=ll(i);
            index_of_minValue=i;
        end
    end
    
    s(k+1)=index_of_minValue;
    k=k+1;
    u=s(k);
end

l
z

Floyd算法

Floyd算法

Floyd算法是求每对顶点之间最短路径的算法.

算法的基本思想

直接在图的带权邻接矩阵中用插入顶点的方法依次构造出 $\nu$ 个矩阵 $D^{(1)},D^{(2)},\ldots,D^{(\nu)}$, 使最后得到的矩阵 $D^{(\nu)}$ 称为图的距离矩阵. 同时求出插入点矩阵以便得到两点间的最短路径.

算法原理

求距离矩阵的方法

把带权邻接矩阵 $W$ 作为距离矩阵的初值, 即 $D^{(0)}=(d_{ij}^{(0)})_{\nu\times\nu}=W$.

  1. $D^{(1)}=(d_{ij}^{(1)})_{\nu\times\nu}$, 其中 $d_{ij}^{(1)}=\min\{d_{ij}^{(0)},d_{i1}^{(0)}+d_{1j}^{(0)}\}$. $d_{ij}^{(1)}$ 是 $v_i$ 到 $v_j$ 的只允许以 $v_1$ 作为中间点的路径中最短路径的长度.
  2. $D^{(2)}=(d_{ij}^{(2)})_{\nu\times\nu}$, 其中 $d_{ij}^{(2)}=\min\{d_{ij}^{(1)},d_{i2}^{(1)}+d_{2j}^{(1)}\}$. $d_{ij}^{(2)}$ 是 $v_i$ 到 $v_j$ 的只允许以 $v_1$, $v_2$ 作为中间点的路径中最短路径的长度.
  3. $\ldots$
  4. $D^{(\nu)}=(d_{ij}^{(\nu)})_{\nu\times\nu}$, 其中 $d_{ij}^{(\nu)}=\min\{d_{ij}^{(\nu-1)},d_{i\nu}^{(\nu-1)}+d_{\nu j}^{(\nu-1)}\}$. $d_{ij}^{(\nu)}$ 是 $v_i$ 到 $v_j$ 的只允许以 $v_1,v_2,\ldots,v_{\nu}$ 作为中间点的路径中最短路径的长度, 即是从 $v_i$ 到 $v_j$ 中间可插入任何顶点的路径中最短路径的长度, 因此 $D^{(\nu)}$ 即为距离矩阵.

求路径矩阵的方法

在建立距离矩阵的同时可建立路径矩阵 $R=(r_{ij})_{\nu\times\nu}$, 这里 $r_{ij}$ 的含义是从 $v_i$ 到 $v_j$ 的最短路径要经过点 $r_{ij}$.

\[ R^{(0)}=(r_{ij}^{(0)})_{\nu\times\nu},\quad r_{ij}^{(0)}=j. \]

每求得一个 $D^{(k)}$ 时, 按下列方式产生相应的新的 $R^{(k)}$: \[ r_{ij}^{(k)}=\begin{cases} k,&\text{若}\ d_{ij}^{(k-1)}>d_{ik}^{(k-1)}+d_{kj}^{(k-1)},\\ r_{ij}^{(k-1)},&\text{否则}. \end{cases} \]

当 $d_{ij}^{(k-1)}>d_{ik}^{(k-1)}+d_{kj}^{(k-1)}$ 时, 显然经过 $k$ 点会缩短距离. 否则维持现状即可.

即当 $v_k$ 被插入任何两点间的最短路径时, 被记录在 $R^{(k)}$ 中, 依次求 $D^{(\nu)}$ 时求得 $R^{(\nu)}$, 可由 $R^{(\nu)}$ 来查找任何点对之间的最短路径.

查找最短路径的方法

若 $r_{ij}^{\nu}=p_1$, 则点 $p_1$ 是点 $i$ 到点 $j$ 的最短路径的中间点, 然后用同样的方法再分头查找, 即朝着上游、下游分头追溯. 若

则由点 $i$ 到点 $j$ 的最短路径为: \[ i,p_k,p_{k-1},\ldots,p_2,p_1,q_1,q_2,\ldots,q_m,j. \]

算法步骤

Floyd算法

$D(i,j)$: 点 $i$ 到点 $j$ 的距离.

$R(i,j)$: $i$ 到 $j$ 之间的插入点.

输入带权邻接矩阵 $W$.

Floyd算法的Matlab代码

Floyd算法的Matlab代码

% page 93.
% filename: floyd.m
function [D,R] = floyd(A)
n=size(A,1); % n 等于矩阵 A 的行数.
D=A
for i=1:n
    for j=1:n
        R(i,j)=j;
    end
end
R

for k=1:n
    for i=1:n
        for j=1:n
            if D(i,k)+D(k,j) < D(i,j)
                D(i,j)=D(i,k)+D(k,j);
                R(i,j)=R(i,k);
            end
        end
    end
    k
    D
    R
    '-----------------------'
end

---Matrix D : ---------
0 7 5 3 9
7 0 2 4 6
5 2 0 2 4
3 4 2 0 6
9 6 4 6 0
---Matrix R : ---------
1 4 4 4 4
3 2 3 3 3
4 2 3 4 5
1 3 3 4 3
3 3 3 3 5
----------------

匹配与覆盖

匹配与覆盖

二元图的匹配

关于匹配的一般性质对于二元图自然也成立. 但二元图的匹配还有其自身的一些重要性质.

定理 3

设 $G=(X,Y,E)$ 是二元图, $M$ 是图 $G$ 的一个匹配, $K$ 是图 $G$ 的一个覆盖. 则 $M,K$ 分别是图 $G$ 的最大匹配、最小覆盖的充要条件是: $|M|=|K|$.

定理 4

对二元图 $G=(X,Y,E)$, 有

证明

(1) 设 $M$ 是 $G$ 中饱和 $X$ 中每个顶点的匹配. $G$ 是二元图, 因此 $X$ 中的顶点互不相邻. 又 $M$ 是匹配, 所以 $M$ 中的边互不相邻. 于是任取 $S\subset X$, 对任意 $u\in S$, 通过 $M$, 存在 $v\in Y$ 与之相邻. 而这些 $v\in N(S)$. 因此 $|N(S)|\geqslant|S|$.

反之, 假设 $\forall\ S\subset X$, 有 $|N(S)|\geqslant|S|$. 可用归纳法构造出饱和 $S$ 的一个匹配.
具体的, 参见问题2262.

例 1

例 1

如下图所示, 图 $G$ 是二元图. $M=\{(x_1,y_1), (x_2,y_2), (x_3,y_3)\}$ 是二元图 $G$ 的匹配, $K=\{x_1,x_2,x_3\}$ 是图 $G$ 的一个覆盖.

因为 $|M|=|K|=3$, 故 $M$ 和 $K$ 分别是图 $G$ 的最大匹配和最小覆盖.

又因为 $\forall\ S\subset X$, 有 $|N(S)|\geqslant |S|$, 因此存在饱和 $X$ 的所有顶点的匹配(或取 $t=3$, 用定理 4 中 (3) 的结论).

但对于 $Y$, $\exists\ S\subset Y$, 使得 $|N(S)| < |S|$. 如取 $S=\{y_1,y_2,y_3,y_4\}$, $N(S)=\{x_1,x_2,x_3\}$, 显然 $|N(S)| < |S|$, 因此不存在完美匹配.

最小生成树问题

最小生成树问题

Kruskal 算法

Kruskal 算法

Kruskal 算法的 idea 是: 程序管理的是一些连通子树的集合, 每次在添加新的边的时候, 检验是否形成环, 如不形成环, 则添加.

为此, 对于每条欲添加的边, 检验两个端点(不妨称之为左端点和右端点)是否已构建子树集合中的点, 分别用整数 $k_1$ 和 $k_2$ 表示其状态, $0$ 代表不在内, $1$ 代表在其中.

另一方面, 对于子树也进行标号, 记录添加的边之顶点属于哪个子树.


算法的基本思想: 最初把图的 $n$ 个顶点看作 $n$ 个分离的部分树, 每棵树有一个顶点. 算法的每一步选择可连接两分离树的边中权最小的边连接两个部分树, 合二为一, 部分树逐渐减少, 直到部分树的总边数为 $n-1$, 便得到最小生成树.

算法步骤

为了选取权尽可能小的边, 事先对图的边按权重由小到大排序, 得到图的边权列表(记为 $Q$), 作为算法输入.

$T$: 存放生成树的边与权的集合, 初始为 $\Phi$.

  1. $T\leftarrow\Phi$
  2. 若 $|T|=n-1$, 则输出 $T$, 停止; 否则转下一步.
  3. 从 $Q$ 中取出排头边 $(u,v)$, 并从 $Q$ 中删除 $(u,v)$.
  4. 若 $T\cup\{(u,v)\}$ 中有圈, 则转(3); 否则转下一步.
  5. $T\leftarrow T\cup\{(u,v)\}$, 转 (2).

Matlab 代码

% SpanningTree_Kruskal.m
% Book. page 109
%-----------------------------------
clear
edge=textread('net1.txt');
% net1.txt 是网络的边权文件, 每行为一条边, 第 1、2 列分别为边的两端顶点编号,第3列为边权,边权由小到大排列。
% 读取后, edge 是一个 15x3 的矩阵, 元素是 double 类型.
% 1 3 1
% 2 5 1
% 4 6 1
% 1 2 2
% 4 7 2
% 5 6 3
% 7 8 3
% 6 7 4
% 4 5 5
% 2 4 6
% 6 8 6
% 3 4 7
% 1 4 8
% 3 7 9
% 5 8 9


NodeNum=max(max(edge(:,1:2))); % 网络顶点数
e1=length(edge(:,1)); % 网络边数

p=1;    % p 是当前连通子树的编号
TreeNode=[edge(1,1) p
    edge(1,2) p];
TreeEdge=[edge(1,:)];   %当前子树的边集

for i=2:e1
    if length(TreeEdge(:,1)) < NodeNum-1      %测试将当前边加入子树, 是否会形成圈
        k1=0; k2=0;
        for j=1:length(TreeNode(:,1))
            if edge(i,1)==TreeNode(j,1)
                k1=1;
                p1=TreeNode(j,2);
            end
        end
        for j=1:length(TreeNode)
            if edge(i,2)==TreeNode(j,1)
                k2=1;
                p2=TreeNode(j,2);
            end
        end
        
        % 当前边的 2 个顶点都不在已形成的子树中
        if k1+k2==0
            TreeEdge=[TreeEdge
                edge(i,:)];
            p=max(TreeNode(:,2))+1;
            TreeNode=[TreeNode
                edge(i,1) p
                edge(i,2) p];
        end
        
        % 当前边的其中一个顶点不在已形成的子树中
        if k1+k2==1
            TreeEdge=[TreeEdge
                edge(i,:)];
            if k1==1  % 如果当前要加入的边的第一个顶点与TreeNode(:,1)中的相同
                p=p1;
                TreeNode=[TreeNode
                    edge(i,2) p];
            else
                %p=p2;
                TreeNode=[TreeNode
                    edge(i,1) p2];
            end
        end
        
        % 当前边的 2 个顶点分别属于已形成的不同的连通子树中
        if k1+k2==2 & p1 ~= p2
            TreeEdge=[TreeEdge
                edge(i,:)];
            if p1 < p2
                t=find(TreeNode(:,2)==p2);
                TreeNode(t,2)=p1;
            else
                t=find(TreeNode(:,2)==p1);
                TreeNode(t,2)=p2;
            end
        end
    end
end

dlmwrite('Net1_Tree1.txt',TreeEdge, '\t')
dlmwrite('Net1_Tree1_nodes.txt',TreeNode, '\t')

%=================================
% help max
% --------
%If A is a vector, then max(A) returns the largest element of A.
%If A is a matrix, then max(A) is a row vector containing the maximum value of each column.
% A=magic(5)
% A =
%     17    24     1     8    15
%     23     5     7    14    16
%      4     6    13    20    22
%     10    12    19    21     3
%     11    18    25     2     9
% max(A)
% ans =
%     23    24    25    21    22

Prim 算法

Prim 算法

Prim 算法的 idea 是: 既然要得到权最小的生成树, 我们可以通过一条边接着一条边的生长方式构建最终所需的权最小生成树.

算法的基本思想: 以权重最小的一条边作为初始子树 $T_1$, 连接与 $T_1$ 最近的顶点得子树 $T_2$, 如此继续下去, 直到所有顶点都用到为止.

算法步骤

为了选取权尽可能小的边, 事先对图的边按权重由小到大排序, 得到图的边权列表(记为 $Q$), 作为算法输入.

$T$: 存放生成树的边与权的集合, 初始为 $\Phi$.

  1. $T\leftarrow\Phi$
  2. 若 $|T|=n-1$, 则输出 $T$, 停止; 否则转下一步.
  3. 从 $Q$ 中取出排头边 $(u,v)$, 并从 $Q$ 中删除 $(u,v)$.
  4. 若 $(u,v)$ 仅有一个顶点在 $T$ 中, 则转下一步, 否则转(3).
  5. $T\leftarrow T\cup\{(u,v)\}$, 转 (2).

Matlab 代码

% SpanningTree_Prim.m
% Book. page 112
%-----------------------------------
clear
edge=textread('net1.txt');%建议不要使用textread, 改用textscan
%fileID=fopen('net1.txt');
%edge=textscan(fileID,);

% net1.txt 是网络的边权文件, 每行为一条边, 第 1、2 列分别为边的两端顶点编号,第3列为边权,边权由小到大排列。
% 读取后, edge 是一个 15x3 的矩阵, 元素是 double 类型.
% 1 3 1
% 2 5 1
% 4 6 1
% 1 2 2
% 4 7 2
% 5 6 3
% 7 8 3
% 6 7 4
% 4 5 5
% 2 4 6
% 6 8 6
% 3 4 7
% 1 4 8
% 3 7 9
% 5 8 9


NodeNum=max(max(edge(:,1:2))); % 网络顶点数
e1=length(edge(:,1)); % 网络边数

TreeNode=[edge(1,1) edge(1,2)];% 当前连通子树的顶点集
TreeEdge=[edge(1,:)];   %当前子树的边集
t1=length(TreeEdge(:,1)); % 当前连通子树的边数


while t1 < NodeNum-1      %测试当前边是否仅有一个顶点在当前连通子树里
    k1=0; k2=0;
    i=2;
    while i < e1
        k1=0; k2=0;
        for j=1:length(TreeNode)
            if edge(i,1)==TreeNode(j)
                k1=1;
            end
        end
        for j=1:length(TreeNode)
            if edge(i,2)==TreeNode(j)
                k2=1;
            end
        end
        
        % 当前边没有或 2 个顶点都在当前连通子树中
        if k1+k2 ~=1
            i=i+1;
        end
        
        % 当前边仅有一个顶点在当前连通子树里
        if k1+k2 == 1
            TreeEdge=[TreeEdge
                edge(i,:)];
            if k1==1
                TreeNode=[TreeNode edge(i,2)];
            else
                TreeNode=[TreeNode edge(i,1)];
            end
            edge(i,:)=[];
            break
        end
    end
    
    t1=length(TreeEdge(:,1));
end

dlmwrite('Net1_Tree2.txt',TreeEdge, '\t')


%=================================
% help max
% --------
%If A is a vector, then max(A) returns the largest element of A.
%If A is a matrix, then max(A) is a row vector containing the maximum value of each column.
% A=magic(5)
% A =
%     17    24     1     8    15
%     23     5     7    14    16
%      4     6    13    20    22
%     10    12    19    21     3
%     11    18    25     2     9
% max(A)
% ans =
%     23    24    25    21    22

End






Thanks very much!