数据结构 答案2

本文深入讲解了数据结构中的核心概念,包括数组、广义表、特殊矩阵、稀疏矩阵、树、二叉树以及图的基本原理。文章详细解析了数组的存储顺序、三维数组地址计算公式、特殊矩阵的压缩存储技巧等问题,并对广义表和线性表进行了对比分析。此外,还介绍了树的多种操作算法,如层次遍历、拷贝树、查找节点等。
摘要由CSDN通过智能技术生成

第五章 数组与广义表
5.1请按行及按列优先顺序列出四维数组A2*3*2*3的所有元素在内存中的存储次序,开始结点为a0000 。 

解:
  按行优先的顺序排列时,先变化右边的下标,也就是右到左依次变化,这个四维数组的排列是这样的:(将这个排列分行写出以便与阅读,只要按从左到右的顺序存放就是在内存中的排列位置)
  a0000  a0001  a0002 
  a0010  a0011  a0012 
  a0100  a0101  a0102 
  a0110  a0111  a0112 
  a0200  a0201  a0202 
  a0210  a0211  a0212 
  a1000  a1001  a1002 
  a1010  a1011  a1012 
  a1100  a1101  a1102 
  a1110  a1111  a1112 
  a1200  a1201  a1202 
  a1210  a1211   a1212
 按列优先的顺序排列恰恰相反,变化最快的是左边的下标,然后向右变化,所以这个四维数组的排列将是这样的,(这里为了便于阅读,也将其书写为分行形式):
  a0000  a1000
  a0100  a1100
  a0200  a1200
  a0010  a1010
  a0110  a1110
  a0210  a1210
  a0001  a1001
  a0101  a1101
  a0201  a1201
  a0011  a1011
  a0111  a1111
  a0211  a1211
  a0002  a1002
  a0102  a1102
  a0202  a1202
  a0012  a1012
  a0112  a1112
  a0212  a0212 
关闭
 
5.2 给出C语言的三维数组地址计算公式。
解:
  因为C语言的数组下标下界是0,所以
    Loc(Amnp)=Loc(A000)+((i*n*p)+k)*d
  其中 Amnp表示三维数组。Loc(A000)表示数组起始位置。i、j、k表示当前元素的下标,d表示每个元素所占单元数。 
关闭
 
5.3 设有三对角矩阵 An*n,将其三条对角线上的元素逐行地存储到向量B[0...3n-3]中,使得B[k]=aij,求:
  (1)用i , j 表示k的下标变换公式。
  (2)用 k 表示 i,j 的下标变换公式。
 (1) 解: 
  要求i,j 到k 的下标变换公式,就是要知道在k之前已有几个非零元素,这些非零元素的个数就是k的值,一个元素所在行为i,所在列为j,则在其前面已有的非零元素个数为:
    (i*3-1)+j-(i+1) 
  其中 (i*3-1)是这个元素前面所有行的非零元素个数,j-(i+1)是它所在列前面的非零元素个数
  化简可得:
    k=2i+j; // c下标是从0开始的。
 (2) 解:
   因为K和i,j是一一对应的关系,因此这也不难算出:
    i=(k+1)/3 //k+1表示当前元素前有几个非零元素,被3整除就得到行号
    j=(k+1)%3+(k+1)/3-1 //k+1除以3的余数就是表示当前行中第几个非零元素,
                //加上前面的0元素所点列数就是当前列号 
关闭
 
5.4 设二维数组A5*6的每个元素占4个字节,已知Loc(a00)=1000,A共占多少个字节? A的终端结点a45的起始地位为何?按行和按列优先存储时,a25的起始地址分别为何?
解:
 (1)因含5*6=30个元素,因此A共占30*4=120个字节。
 (2)a45的起始地址为:
 Loc(a45)=Loc(a00)+(i*n+j)*d=1000+(4*6+5)*4=1116
 (3)按行优先顺序排列时,
 a25=1000+(2*6+5)*4=1068
 (4)按列优先顺序排列时:(二维数组可用行列下标互换来计算)
 a25=1000+(5*5+2)*4=1108 
关闭
 
5.5 特殊矩阵和稀疏矩阵哪一种压缩存储后会失去随机存取的功能?为什么?
答:
 后者在采用压缩存储后将会失去随机存储的功能。因为在这种矩阵中,非零元素的分布是没有规律的,为了压缩存储,就将每一个非零 元素的值和它所在的行、列号做为一个结点存放在一起,这样的结点组成的线性表中叫三元组表,它已不是简单的向量,所以无法用下标直接存取矩阵中的元素。 
关闭
 
5.6 简述广义表和线性表的区别与联系。
答:
 广义表是线性表的推广,线性表是广义表的特例。当广义表中的元素都是原子时,即为线性表。 

 
关闭
 
5.7 画出下列广义表的图形表示:
  (1) A(a,B(b,d),C(e,B(b,d),L(f,g))) (2) A(a,B(b,A))
解:
 
  
 (1)这是一个再入表。(2)则是一个递归表。 
 
5.8 设广义表L=((),()),试问head(L),tail(L),L的长度,深度各为多少?
解:
  ●head(L)=()
  ●tail(L)=(())
  ●L的长度为2
  ●L的深度为2 

5.9 求下列广义表运算的结果:
  (1)head ((p,h,w)); (2)tail((b,k,p,h)); (3) head (((a,b),(c,d)));
  (4)tail(((a,b),(c,d))); (5)head(tail(((a,b),(c,d))));
  (6)tailhead)(((a,b),(c,d)))).
答:
  (1)head ((p,h,w))=p; 
  (2)tail((b,k,p,h))=(k,p,h); 
  (3)head (((a,b),(c,d)))=(a,b);
  (4)tail(((a,b),(c,d)))=((c,d));
  (5)head(tail(((a,b),(c,d))))=(c,d);
  (6)tail(head(((a,b),(c,d))))=(b). 
 5.10 当三角矩阵采用题5.3所述的压缩存储时,写一算法求三对角矩阵在这种压缩存储表示下的转置矩阵。
解:
  转置矩阵就是将矩阵元素的行号与列号互换,根据已知的三对角矩阵的特点,其转置矩阵对角线元素不变,非零的非对角线元素aij与aji互换位置。又知元素的下标和存放一维数组空间位置的关系:k=2i+j。我们可以设计出这个矩阵的转置算法:
  #define N 10 //矩阵行、列数
  #define Length (3*N-2)//压缩矩阵的长度
  typedef struct{
    int data[Length];
   }DiaMatrix;
  void TransMatrix(DiaMatrix * C)
   { //压缩三对角矩阵转置
    int i, j;
    int t;
    for(i=0; i<N;i++)
     for(j=i; j<N; j++)
 if(i-j<=1&&i-j>=-1)
 { //将对应于行列号的压缩矩阵内的元素互换
        t=C->data[2*i+j];
        C->data[2*i+j]=C->data[2*j+i];
        C->data[2*j+i]=t;
       }//endif
   }//end 
5.11 当稀疏矩阵A和B均以三元组表作为存储结构时,试写出矩阵相加的算法,其结果存放在三元组表C中。
解:
  矩阵相加就是将两个矩阵中同一位置的元素值相加。由于两个稀疏矩阵的非零元素按三元组表形式存放,在建立新的三元组表C时, 为了使三元组元素仍按行优先排列,所以每次插入的三元组不一定是A的,按照矩阵元素的行列去找A中的三元组,若有,则加入C,同时,这个元素如果在B中也 有,则加上B的这个元素值,否则这个值就不变;如果A中没有,则找B,有则插入C,无则查找下一个矩阵元素。
  #define MaxSize 10 //用户自定义
  typedef int DataType; //用户自定义
  typedef struct
   { //定义三元组
    int i,j;
    DataType v;
   }TriTupleNode;
  typedef struct
   { //定义三元组表
    TriTupleNode data[MaxSize];
    int m,n,t;//矩阵行,列及三元组表长度
   }TriTupleTable;
  //以下为矩阵加算法 
  void AddTriTuple( TriTupleTable *A, TriTupleTable *B, TriTupleTable *C)
   {//三元组表表示的稀疏矩阵A,B相加
    int k,l;
    DataType temp;
    C->m=A->m;//矩阵行数
    C->n=A->n;//矩阵列数
    C->t=0; //三元组表长度
    k=0; l=0;
    while (k<A->t&&l<B->t)
     {if((A->data[k].i==B->data[l].i)&&(A->data[k].j==B->data[l].j))
       {temp=A->data[k].v+B->data[l].v;
        if (!temp)//相加不为零,加入C
         {C->data[c->t].i=A->data[k].i;
          C->data[c->t].j=A->data[k].j;
          C->data[c->t++].v=temp;
         }
        k++;l++; 
       }
     if ((A->data[k].i==B->data[l].i)&&(A->data[k].j<B->data[l].j))
       ||(A->data[k].i<B->data[l].i)//将A中三元组加入C
      {C->data[c->t].i=A->data[k].i;
       C->data[c->t].j=A->data[k].j;
       C->data[c->t++].v=A->data[k].v;
       k++;
      }
     if ((A->data[k].i==B->data[l].i)&&(A->data[k].j>B->data[l].j))
       ||(A->data[k].i>B->data[l].i)//将B中三元组加入C
      {C->data[c->t].i=B->data[l].i;
       C->data[c->t].j=B->data[l].j;
       C->data[c->t++].v=B->data[l].v;
       l++; 
      }
     }
    while (k<A->t)//将A中剩余三元组加入C
     {C->data[c->t].i=A->data[k].i;
      C->data[c->t].j=A->data[k].j;
      C->data[c->t++].v=A->data[k].v;
      k++;
     }
    while (l<B->t)//将B中剩余三元组加入C
     {C->data[c->t].i=B->data[l].i;
      C->data[c->t].j=B->data[l].j;
      C->data[c->t++].v=B->data[l].v;
      l++;
     }
   } 
关闭

 第六章 树
6.1.假设在树中,结点x是结点y的双亲时,用(x,y)来表示树边.已知一棵树边的集合为{(i,m), (i,n),(e,i),(b,e),(b,d),(a,b),(g,j),(g,k),(c,g),(c,f),(h,l),(c,h),(a,c)} 用树形表示法出此树,并回答下列问题:
 (1)哪个是根结点? (2)哪些是叶结点? (3)哪个是g的双亲? (4)哪些是g的祖先? 
 (5)哪些是g的孩子? (6)哪些是e的子孙? (7)哪些是e的兄弟?哪些是f的兄弟? 
 (8)结点b和n的层次各是多少? (9)树的深度是多少? (10)以结点c为根的子树的深度是多少? (11) 树的度数是多少?
答:

 a是根结点;
 dmnfjkl是叶结点;
 c是g的双亲;
 c,a是g的祖先;
 j,k是g的孩子;
 imn是e的子孙;
 d是e的兄弟;g,h是f的兄弟;
 b的层次是2;n的层次是5;
 树的深度是5;
 以c为根的子树深度是3;
 树的度数是3; 
关闭6.2 一棵度为2的有序树与一棵二叉树有何区别?
答:
  一棵度为二的有序树与一棵二叉树的区别在于:有序树的结点次序是相对于另一结点而言的,如果有序树中的子树只有一个孩子时, 这个孩子结点就无须区分其左右次序,而二叉树无论其孩子数是否为2,均需确定其左右次序,也就是说二叉树的结点次序不是相对于另一结点而言而是确定的。 
关闭
 
 6.3 试分别画出具有3个结点的树和3个结点的二叉树的所有不同形态。
答:
  三个结点的树如下:只有两种形态
 ○ ○ 
  / \ | 
 ○ ○ ○ 
 | 
 ○ 
三个结点的二叉树如下所示:有五种形态:
 (1) (2) (3) (4) (5)
 ○ ○ ○ ○ ○ 
 / \ / / \ \ 
 ○ ○ ○ ○ ○ ○ 
 / \ / \ 
 ○ ○ ○ ○ 
6.4 已知一棵度为m的树中有n1个度为1的结点,n2个度为2的结点,...nm个度为m的结点,问该树中有多少片叶子?
解:
 设该树中的叶子数为n0个。该树中的总结点数为n个,则有:
 n=n0+n1+n2+…+nm (1)
又有除根结点外,树中其他结点都有双亲结点,且是唯一的(由树中的分支表示),所以,有双亲的结点数为:
 n-1=0*n0+1*n1+2*n2+…+m*nm (2)
 联立(1)(2)方程组可得:
 叶子数为:n0=1+0*n1+1*n2+2*n3+...+(m-1)*nm 
关闭
 
 6.5一个深度为h的满k叉树有如下性质:第h层上的结点都是叶子结点,其余各层上每个结点都有k棵非空子树。如果按层次顺序(同层自左至右)从1开始对全部结点编号,问:
 (1)各层的结点数目是多少?
 (2)编号为i的结点的双亲结点(若存在)的编号是多少?
 (3)编号为i的结点的第j个孩子结点(若存在)的编号是多少?
 (4)编号为i的结点的有右兄弟的条件是什么? 其右兄弟的编号是多少?
解:
 (1) 层号为h的结点数目为kh-1
 (2) 编号为i的结点的双亲结点的编号是:|_ (i-2)/k _|+1(不大于(i-2)/k的最大整数。也就是(i-2)与k整除的结果.以下/表示整除。
 (3) 编号为i的结点的第j个孩子结点编号是:k*(i-1)+1+j;
 (4) 编号为i的结点有右兄弟的条件是(i-1)能被k整除 
   右兄弟的编号是i+1. 
关闭6.6高度为h的完全二叉树至少有多少个结点?至多有多少个结点?
解:
  高度为h的完全二叉树至少有2h-1个结点,至多有2h-1个结点(也就是满二叉树)。 
关闭
 6.7 在具有n个结点的k叉树(k>=2)的k叉链表表示中,有多少个空指针?
解:
  n个结点的K叉树共有n*k个指针域,已使用的指针域为n-1,所以空指针的个数为:n(k-1)+1; 
关闭
 6.8 假设二叉树包含的结点数据为1,3,7,12。
 (1)画出两棵高度最大的二叉树;
 (2)画出两棵完全二叉树,要求每个双亲结点的值大于其孩子结点的值。
解:
 (1)高度最大的两棵二叉树如图:
     ○1     ○1
     /       \
    ○3       ○3
    /         \
   ○7         ○7
   /           \

   ○2           ○2
  /             \
 ○12             ○12
  
 (2)两棵完全二叉树如下:

     ○12       ○12 
     / \       / \ 
    ○7 ○3     ○7 ○3 
    / \       / \ 
   ○1 ○2     ○2 ○1 
关闭
 6.9试找出分别满足下面条件的所有二叉树:
 (1)前序序列和中序序列相同; (2)中序序列和后序序列相同;
 (3)前序序列和后序序列相同; (4)前序、中序、后序序列均相同。
答:
 (1) 前序序列和中序序列相同的二叉树是:空二叉树或没有左子树的二叉树(右单支树)。
 (2) 中序序列和后序序列相同的二叉树是:空二叉树或没有右子树的二叉树(左单支树)。
 (3) 前序序列和后序序列相同的二叉树是:空二叉树或只有根的二叉树。
 (4) 前序、中序、后序序列均相同的二叉树:空树或只有根结点的二叉树。 
关闭
 6.10 试采用顺序存储方法和链接存储方法分别画也6.30所示各二叉树的存储结构。
解:
 (1)顺序存储方法:
 二叉树(a):
下标 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
 __________________________________________________________________
 bt | |1 |2 |∮|∮|3 |∮|∮|∮|∮|4 |∮|∮|∮|∮|∮|∮|∮|∮|∮|∮| 5|
 __________________________________________________________________
 二叉树(b):
下标 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
 __________________________________________________________________________________
 bt| |1 |∮|2 |∮|∮|3 |∮|∮|∮|∮|∮|∮|4 |∮|∮|∮|∮|∮|∮|∮|∮|∮|∮|∮|∮|5 |
 __________________________________________________________________________________
 二叉树?:
下标 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
 _______________________________________________________________________________
bt | |1 |∮|2 |∮|∮|3 |4 |∮|∮|∮|∮|5 |6 |∮|∮|∮|∮|∮|∮|∮|∮|∮|∮|7 |8 |
 _______________________________________________________________________________
 二叉树(d):
下标 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
 ┌—┬—┬—┬—┬—┬—┬—┬—┬—┬—┬—┬—┬—┬—┬—┬—┐
bt│ │1 │2 │3 │4 │∮│5 │6 │∮│7 │∮│∮│∮│∮│ 8│9 │
 └—┴—┴—┴—┴—┴—┴—┴—┴—┴—┴—┴—┴—┴—┴—┴—┘

 (2)链式存储结构:
 二叉树(a): 
 ↓root 
 ┌—┬—┬—┐ 
 │ │1 │∧│ 
 └┼┴—┴—┘ 
 ↓ 
 ┌—┬—┬—┐ 
 │∧│2 │ │ 
 └—┴—┴┼┘ 
 ↓ 
 ┌—┬—┬—┐ 
 │ │3 │∧│ 
 └┼┴—┴—┘ 
 ↓ 
 ┌—┬—┬—┐ 
 │∧│4 │ │ 
 └—┴—┴┼┘ 
 ↓ 
 ┌—┬—┬—┐ 
 │∧│5 │∧│ 
 └—┴—┴—┘ 
 二叉树(b):
 ↓root
 ┌—┬—┬—┐
 │∧│1 │ │
 └—┴—┴┼┘
 ↓
 ┌—┬—┬—┐
 │ │2 │∧│
 └┼┴—┴—┘
 ↓
 ┌—┬—┬—┐
 │∧│3 │ │
 └—┴—┴┼┘
 ↓
 ┌—┬—┬—┐
 │ │4 │∧│
 └┼┴—┴—┘
 ↓
 ┌—┬—┬—┐
 │∧│5 │∧│
 └—┴—┴—┘
 二叉树?:
 ↓root
 ┌—┬—┬—┐ 
 │∧│1 │ │ 
 └—┴—┴┼┘ 
  ↓
 ┌—┬—┬—┐
 │ │2 │ │
 └┼┴—┴┼┘
 ↓ ↓
 ┌—┬—┬—┐┌—┬—┬—┐
 │ │3 │ ││∧│4 │∧│
 └┼┴—┴┼┘└—┴—┴—┘
 ↓ ↓
 ┌—┬—┬—┐ ┌—┬—┬—┐
 │ │5 │ │ │∧│6 │∧│

 
解:各二叉树所对应森林如下:

  (a) ○A
  (b) ○A
    /
   ○B
   /
  ○C 
  ? ○A  ○B  ○C
  (d) ○A ○C
    |
    ○B
 (e) ○A ○C ○F ○I
 / \ |
 ○B ○E ○L
 / | \
 ○D○H○K
 | |
 ○G ○J 
关闭
 6.18高度为h的严格二叉树至少有多少个结点?至多有多少个结点? 
答:
 所谓严格二叉树是指该树中没有度数为1的分支结点的二叉树。
 所以:高度为h的的严格二叉树至少有2h-1个结点;至多有2h-1个结点(即满二叉树)。 
关闭
 6.19 在什么样的情况下,等长编码是最优的前缀码?
答:
 在每个字符的使用概率相同的情况下,也即在哈夫曼树中每片叶子的权重相等的时候,等长编码是最优的前缀码。 
关闭
 6.20 下述编码哪一组不是前缀码?
 {00,01,10,11},{0,1,00,11},{0,10,110,111}
答:
 第二组不是前缀码。因为0,1分别是00和11的前缀。(前缀码是指该编码集中的任一编码不是其他编码的前缀) 
关闭
 6.21 假设用于通信的电文由字符集{a,b,c,d,e,f,g,h}中的字母构成,这8个字母在电文中出现的概率分别为{0.07,0.19,0.02,0.06,0.32,0.03,0.21,0.10}.
 (1)为这8个字母设计哈夫曼编码。
 (2)若用这三位二进制数(0…7)对这8个字母进行等长编码,则哈夫曼编码的平均码长是等长编码的百分之几?它使电文总长平均压缩多少?
解:
 (1)哈夫曼编码
 
 根据上图可得编码表:
   a:1001
   b:01
   c:10111
   d:1010
   e:11
   f:10110
   g:00
   h:1000

  (2)用三位二进行数进行的等长编码平均长度为3,而根据哈夫曼树编码的平均码长为:
 4*0.07+2*0.19+5*0.02+4*0.06+2*0.32+5*0.03+2*0.21+4*0.10=2.61
 2.61/3=0.87=87%
 其平均码长是等长码的87%。
 所以平均压缩率为13%。 
关闭*6.22 二叉树的遍历算法可写为通用形式。例如通用的中序遍历为:
 void Inorder(BinTree,T,void(* visit)(DataType x)){
  if (T){
    Inorder(T->lchild,Visit);//遍历左子树
    Visit(T->data);//通过函数指针调用它所指的函数来访问结点
    Inorder(T->rchild,Visit);//遍历右子树
   }
 }
   其中Visit是一个函数指针,它指向形如void f(DataType x)的函数。因此我们可以将访问结点的操作写在函数f中通过调用语句Inorder(root,f)将f的地址传递给Visit,来执行遍历操作。请写一 个打印结点数据的函数,通过调用上述算法来完成书中6.3节的中序遍历。
解:
  函数如下:
 void PrintNode(BinTree T)
  {
   printf("%c",T->data);
  }
 //定义二叉树链式存储结构
 typedef char DataType;//定义DataType类型
 typedef struct node{
   DataType data; 
   struct node *lchild, *rchild;//左右孩子子树
  }BinTNode; //结点类型
 typedef BinTNode *BinTree ;//二叉树类型
 void Inorder(BinTree T,void(* Visit)(DataType x))
  {
   if(T)
    {
     Inorder(T->lchild,Visit);//遍历左子树
     Visit(T->data); //通过函数指针调用它所指的函数访问结点
     Inorder(T->rchild,Visit);//遍历右子树 
    }
  } 
关闭6.23 以二叉链表为存储结构,分别写出求二叉树结点总数及叶子总数的算法。
解:
 (1)求结点数的递归定义为:
  若为空树,结点数为0
  若只有根结点,则结点数为1;
  否则,结点数为根结点的左子树结点数+右子树结点数+1
 (2)求叶子数的递归定义为:
  若为空树,叶子数为0
  若只有根结点,则叶子数为1;
  否则,叶子数为根结点的左子树叶子数+右子树叶子数
 typedef char DataType;//定义DataType类型

  typedef struct node{
    DataType data;
    struct node *lchild, *rchild;//左右孩子子树
  }BinTNode; //结点类型
 typedef BinTNode *BinTree ;//二叉树类型
 int Node(BinTree T)
  {//算结点数
   if(T)
    if (T->lchild==NULL)&&(T->rchild==NULL)
     return 1;
    else return Node(T->lchild)+Node(T->rchild)+1;
   else return 0;
  }
 int Leaf(BinTree T)
  { //算叶子数
   if(T)
    if (T->lchild==NULL)&&(T->rchild==NULL)
     return 1;
    else return Leaf(T->lchild)+Node(T->rchild);
   else return 0;
  } 
关闭
 
 6.24以二叉链表为存储结构,分别写出求二叉树高度及宽度的算法,所谓宽度是指二叉树的各层上,具有结点数最多的那一层上的结点总数。
解:
 (1)根据递归定义:二叉树的高度为:
   当为空树时,高度为0;
   当只有一个结点时,高度为1;
   其他情况:高度为max(根的左子树高度,根的右子树高度)+1
 int Height(BinTree T)
  {
   int hl,hr; 
   if(T)
    {//非空树 
     if(t->lchild==NUll)&&(t->rchild==NULL)//只含一个根结点
      return 1;
     else 
      {
       hl=height(t->lchild);//根的左子树高度
       hr=height(t->rchild);//根的右子树高度
       if (hl>=hr)
        return hl+1;
       else return h2+1;
      }
    }
   else return 0;
  }
 (2)要求二叉树的宽度的话,则可根据树的高度设置一个数组temp。temp[i]用于存放第i层上的结点数(即宽度)。在访问结点时,把 相应计算该结点下一层的孩子数并存入相应数组元素中,遍历左子树后向上返回一层计算右子树的宽度,并取出最大的一个数组元素作为树的宽度。
 #define M 10 //假设二叉树最多的层数
 int Width(BinTree T)
  { 
   int static n[M];//向量存放各层结点数
   int static i=1;
   int static max=0;//最大宽度
   if(T)
    {
     if(i==1) //若是访问根结点
      { 
       n[i]++; //第1层加1
       i++; //到第2层
       if(T->lchild)//若有左孩子则该层加1
        n[i]++;
       if(T->rchild)//若有右孩子则该层加1
        n[i]++;
      }
     else
      { //访问子树结点
       i++; //下一层结点数
       if(T->lchild)
        n[i]++;
       if(T->rchild) 
        n[i]++;
      }
     if(max<n[i])max=n[i];//取出最大值
      Width(T->lchild);//遍历左子树
     i--; //往上退一层
     Width(T->rchild);//遍历右子树
    }
   return max;
  }//算法结束
关闭6.25 以二叉链表为存储结构, 写一算法交换各结点的左右子树。 
答:
  要交换各结点的左右子树,最方便的办法是用后序遍历算法,每访问一个结点时把两棵子树的指针进行交换,最后一次访问是交换根结点的子树。
 void ChangeBinTree(BinTree *T)
  { //交换子树
   if(*T) 
    { //这里以指针为参数使得交换在实参的结点上进行后序遍历
     BinTree temp;
     ChangeBinTree(&(*T)->lchild);
     ChangeBinTree(&(*T)->rchild);
     temp=(*T)->lchild;
     (*T)->lchild=(*T)->rchild;
     (*T)->rchild=temp;
    } 
  }
关闭
 6.26 以二叉链表为存储结构,写一个拷贝二叉树的算法void CopyTree(BinTree root, BinTree *newroot),其中新树的结点是动态申请的,为什么newroot要说明为BinTree型指针的指针? 

 
解:
  因为调用函数只能进行值传递,当返回类型为void时,就必须把实参的地址传给函数,否则函数不会对实际参数进行任何操作,也就得不到所需结果了。所以,newroot要说明为BinTree型指针
 void CopyTree(BinTree root,BinTree *newroot)
  { //拷贝二叉树
   if(root)//如果结点非空
    { //按前序序列拷贝
     *newroot=(BinTNode *)malloc(sizeof(BinTNode));//生成新结点
     (*newroot)->data=root->data;//拷贝结点数据
     CopyTree(root->lchild,&(*newroot)->lchild);//拷贝左子树
     CopyTree(root->rchild,&(*newroot)->rchild);//拷贝右子树
    }
   else //如果结点为空
   *newroot=NULL;//将结点置空
  }
关闭
 6.27 以二叉链表为存储结构,分别写出在二叉树中查找值为x的结点及求x所在结点在树中层数的算法。 
解:
  根据上几题的算法可以得出本题的算法如下:
 #define M 10 //假设二叉树最多的层数
 BinTree SearchBTree(BinTree *T,DataType x)
  {//以前序遍历算法查找值为x的结点
   if(*T)
    {
     if((*T)->data==x )return *T;
     SearchBTree(&(*T)->lchild,x);
     SearchBTree(&(*T)->rchild,x);
    }
  }
 int InLevel(BinTree T,DataType x)
  {
   int static l=0;//设一静态变量保存层数
   if(T)
    { 
     if(l==0)//若是访问根结点
      { 
       l++;//第1层
       if(T->data==x)return l;
       if(T->lchild||T->rchild) 
        l++;//若根有子树,则层数加1
      }
     else
      { //访问子树结点
       if(T->data==x)return l;
       if(T->lchild||T->rchild) 
        l++;//若该结点有子树,则层数加1
       else return 0;
      }
     InLevel(T->lchild,x);//遍历左子树
     InLevel(T->rchild,x);//遍历右子树
    }
  }
关闭
 
 6.28 一棵n个结点的完全二叉树以向量作为存储结构,试写一非递归算法实现对该树的前序遍历。
解:
  以向量为存储结构的完全二叉树,其存储在向量中的结点其实是按层次遍历的次序存放的,可以根据课本第74页的内容设计出算法:
 typedef char DataType;//设结点数据类型为char
 #define M 100//设结点数不超过100
 typedef DataType BinTree[M];
 void Preorder(BinTree T)
  { //前序遍历算法
   int n=T[0];
   int p[M];//设置一队列存放结点值
   int i,j;
   for(i=1;i<=n;i++)
    {
     if (i==1)//根结点
       j=1;
     else if(2*j<=n)//左子树
         j=2*j;
       else if(j%2==0&&j<n)//右兄弟
           j=j+1;
         else if(j>1)//双亲之右兄弟
            j=j/2+1;
     p[i]=T[j];//入队
     printf("%c",p[i]);//打印结点值
    }
  }
关闭
 6.29 以二叉链表为存储结构,一算法对二叉树进行层次遍历(层次遍历的定义见6.13).提示:应使用队列来保存各层的结点。 
答: 
 #define M 100 //假设结点数最多为100
 typedef char DataType;//队列结点值类型
 typedef struct//定义一个队列
  {
   int front;
   int rear;
   int count;
   DataType data[M];
  }QBTree;
 static QBTree Q;//设一全局静态变量保存遍历结果
 void Levelorder(BinTree T)
  {//层次遍历
   if(T)
    {

 
      if(QueueEmpty(&Q))
      { //根结点及子树结点入队
       EnQueue(&Q,T->data);
       if(T->lchild)
        EnQueue(&Q,T->lchild->data);
       if(T->rchild)
        EnQueue(&Q,T->rchild->data);
      }
     else
      { //子树结点入队
       if(T->lchild)
        EnQueue(&Q,T->lchild->data);
       if(T->rchild)
        EnQueue(&Q,T->rchild->data);
      }
     Levelorder(T->lchild);//遍历左子树
     Levelorder(T->rchild);//遍历右子树
    }
  }
关闭
 
 6.30以二叉链表为存储结构,写一算法用括号形式(key LT,RT)打印二叉树,其中key是根结点数据,LT和RT是括号形式的左子树和右子树。并且要求空树不打印任何信息,一个结点x的树的打印形式是x而不是(x,)的形式。
答:
  算法如下:
 void PrintTree(BinTree T)
  {//打印二叉树 (以前序遍历实现)
   if(T)//非空树 
    {
     printf("%c",T->data);//打印结点值
     if(T->lchild||T->rchild) 
      printf("(");//打印括号
     PrintTree(T->lchild);//打印左子树
     if(T->lchild&&T->rchild) 
      printf(",");//若存在两棵子树打印逗号
     PrintTree(T->rchild);//打印右子树
     if(T->lchild||T->rchild)
      printf(")");
    }
  }
 void PrintTree1(BinTree T)
  { //由于题目存在两义性,所以这里另作了一个打印算法打印二叉树 (以前序遍历实现)
   if(T)//非空树 
    {
     printf("(%c",T->data);//打印括号和结点值
     PrintTree1(T->lchild);//打印左子树
     if(T->lchild&&T->rchild) 
      printf(",");
     PrintTree1(T->rchild);//打印右子树
     printf(")");
    }
  }
关闭6.31 以线索链表作为存储结构。分别写出在前序线索树中查找给定结点*p的前序后继,以及在后序线索树中查找 *p的后序前趋的算法。
解:
  算法如下:
 BinThrNode * SearchPostInPre(BinThrNode *p)
  {//查找结点*p的前序后继
   if(p)
    {
     if(p->rtag==Link)&&(p->ltag==link)//当左、右都为孩子指针
      return p->lchild;//*p的前序后继为左孩子
     else return p->rchild;
    }
  }
 BinThrNode *SearchPreInPost(BinThrNode *p)
  { //查找*p结点的后序前趋
   if(p)
    {
     if(p->ltag==thread)||(p->rtag==thread)//当有左线索或无有孩子
        return p->lchild; //*p的后续前趋为p->lchild
     else return p->rchild;
    }
  }
关闭
 6.32 完成6.6.1节算法CreatHuffmanTree中调用的三个函数:InitHuffmanTree,InputWeight 和SelectMin。
解:
  整个程序如下:
 #define n 7 //叶子数目,根据实际需要定义
 #define m 2*n-1 //结点数目
 typedef struct
  {
   float weight;
   int lchild,rchild,parent;//左右孩子及双亲指针
  }HTNode;
 typedef HTNode HuffmanTree[m];//是向量类型的
 void InitHuffmanTree(HuffmanTree T)
  {//初始化
   int i;
   for (i=0; i<m; i++)
    {
     T[i].weight=0;
     T[i].lchild=-1;
     T[i].rchild=-1;
     T[i].parent=-1;
    }
  }
 void InputWeight(HuffmanTree T)
  {//输入权值
   float w;

    int i;
   for (i=0; i<n;i++)
    {
     printf("\n输入第%d个权值:",i+1);
     scanf("%f",&w);
     T[i].weight=w;
    }
  }
 void SelectMin(HuffmanTree T, int i, int *p1,int *p2)
  {//选择两个小的结点
   float min1=999999;//预设两个值
   float min2=999999;//并使它大于可能出现的最大权值
   int j;
   for (j=0;j<=i;j++)
   if(T[j].parent==-1) 
    if(T[j].weight<min1)
     {
      min2=min1;//改变最小权,次小权及其位置
      min1=T[j].weight;//找出最小的权值
      *p2=*p1;
      *p1=j; 
     }
    else if(T[j].weight<min2)
     {min2=T[j].weight;//改变次小权及位置
      *p2=j;}
  }//选择结束
关闭
 
 
第六章 树(算法设计) 
*6.22 【答案】二叉树的遍历算法可写为通用形式。例如通用的中序遍历为:
 void Inorder(BinTree,T,void(* visit)(DataType x)){
   if (T){
   Inorder(T->lchild,Visit);//遍历左子树
    Visit(T->data);//通过函数指针调用它所指的函数来访问结点
    Inorder(T->rchild,Visit);//遍历右子树
    }
 }
   其中Visit是一个函数指针,它指向形如void f(DataType x)的函数。因此我们可以将访问结点的操作写在函数f中通过调用语句Inorder(root,f)将f的地址传递给Visit,来执行遍历操作。请写一 个打印结点数据的函数,通过调用上述算法来完成书中6.3节的中序遍历。
6.23 【答案】以二叉链表为存储结构,分别写出求二叉树结点总数及叶子总数的算法。
6.24 【答案】以二叉链表为存储结构,分别写出求二叉树高度及宽度的算法,所谓宽度是指二叉树的各层上,具有结点数最多的那一层上的结点总数。
6.25 【答案】以二叉链表为存储结构, 写一算法交换各结点的左右子树。
6.26 【答案】以二叉链表为存储结构,写一个拷贝二叉树的算法void CopyTree(BinTree root, BinTree *newroot),其中新树的结点是动态申请的,为什么newroot要说明为BinTree型指针的指针? 
6.27 【答案】以二叉链表为存储结构,分别写出在二叉树中查找值为x的结点及求x所在结点在树中层数的算法。
6.28 【答案】一棵n个结点的完全二叉树以向量作为存储结构,试写一非递归算法实现对该树的前序遍历。
6.29 【答案】以二叉链表为存储结构,一算法对二叉树进行层次遍历(层次遍历的定义见6.13).提示:应使用队列来保存各层的结点。 
6.30 【答案】以二叉链表为存储结构,写一算法用括号形式(key LT,RT)打印二叉树,其中key是根结点数据,LT和RT是括号形式的左子树和右子树。并且要求空树不打印任何信息,一个结点x的树的打印形式是x而不是(x,)的形式。
6.31 【答案】以线索链表作为存储结构。分别写出在前序线索树中查找给定结点*p的前序后继,以及在后序线索树中查找 *p的后序前趋的算法。
6.32 【答案】完成6.6.1节算法CreatHuffmanTree中调用的三个函数:InitHuffmanTree,InputWeight 和SelectMin。

 第七章 图
7.1 在图7.23所示的各无向图中:
     
 (1)找出所有的简单环。
 (2)哪些图是连通图?对非连通图给出其连通分量。
 (3)哪些图是自由树(或森林)?
答:
 (1)所有的简单环:(同一个环可以任一顶点作为起点)
   (a)1231
   (b)无
   ?1231、2342、12341
   (d)无
 (2)连通图:
   (a)、?、(d)是连通图,
   (b)不是连通图,因为从1到2没有路径。具体连通分量为:
      
 (3)自由树(森林):自由树是指没有确定根的树,无回路的连通图称为自由树:
   (a)不是自由树,因为有回路。
   (b)是自由森林,其两个连通分量为两棵自由树。
   ?不是自由树。
   (d)是自由树。 
关闭
7.2 在图7.24(下图)所示的有向图中:
    
     
 (1) 该图是强连通的吗? 若不是,则给出其强连通分量。
 (2) 请给出所有的简单路径及有向环。
 (3) 请给出每个顶点的度,入度和出度。
 (4) 请给出其邻接表、邻接矩阵及逆邻接表。
答:
 (1)该图是强连通的,所谓强连通是指有向图中任意顶点都存在到其他各顶点的路径。
 (2)简单路径是指在一条路径上只有起点和终点可以相同的路径:
  有v1v2、v2v3、v3v1、v1v4、v4v3、v1v2v3、v2v3v1、v3v1v2、v1v4v3、v4v3v1、v3v1v4、另包括所有有向环,有向环如下:
     v1v2v3v1、v1v4v3v1(这两个有向环可以任一顶点作为起点和终点)
 (3)每个顶点的度、入度和出度:
   D(v1)=3  ID(v1)=1  OD(v1)=2
   D(v2)=2  ID(v2)=1  OD(v2)=1
   D(v3)=3  ID(v3)=2  OD(v3)=1
   D(v4)=2  ID(v4)=1  OD(v4)=1
 (4)邻接表:(注意边表中邻接点域的值是顶点的序号,这里顶点的序号是顶点的下标值-1)
 vertex firstedge next
   ┌—┬—┐ ┌—┬—┐ ┌—┬—┐
   0│v1│ —→│ 1│ —→│ 3│∧│
 ├—┼—┤ ├—┼—┤ └—┴—┘
 1│v2│ —→│ 2│∧│
 ├—┼—┤ ├—┼—┤
 2│v3│ —→│ 0│∧│
 ├—┼—┤ ├—┼—┤
 3│v4│ —→│ 2│∧│
 └—┴—┘ └—┴—┘
 逆邻接表:
 ┌—┬—┐ ┌—┬—┐
 0│v1│ —→│ 2│∧│
 ├—┼—┤ ├—┼—┤
 1│v2│ —→│ 0│∧│
 ├—┼—┤ ├—┼—┤ ┌—┬—┐
 2│v3│ —→│ 1│ —→│ 3│∧│
 ├—┼—┤ ├—┼—┤ └—┴—┘
 3│v4│ —→│ 0│∧│
 └—┴—┘ └—┴—┘
 邻接矩阵:
 0 1 0 1
 0 0 1 0
 1 0 0 0
 0 0 1 0 
关闭
 

7.3 假设图的顶点是A,B...,请根据下述的邻接矩阵画出相应的无向图或有向图。
         ┌    ┓ 
 ┌ ┓ | 0 1 1 0 0 |
 | 0 1 1 1 | | 0 0 0 1 0 | 
 | 1 0 1 1 | | 0 0 0 1 0 | 
 | 1 1 0 1 | | 1 0 0 0 1 | 
 | 1 1 1 0 | | 0 1 0 1 0 |
 ┕ ┙  ┕  ┙
 (a) (b) 
答:
 
关闭
 
7.4 假设一棵完全二叉树包括A,B,C...等七个结点,写出其邻接表和邻接矩阵。
解:
 邻接表如下:
  ┌—┬—┐ ┌—┬—┐ ┌—┬—┐
  0│A │ —→│ 1│ —→│ 2│∧│
 ├—┼—┤ ├—┼—┤ ├—┼—┤ ┌—┬—┐
 1│B │ —→│ 0│ —→│ 3│ —→│ 4│∧│
 ├—┼—┤ ├—┼—┤ ├—┼—┤ ├—┼—┤
 2│C │ —→│ 0│ —→│ 5│ —→│ 6│∧│
 ├—┼—┤ ├—┼—┤ └—┴—┘ └—┴—┘
 3│D │ —→│ 1│∧│
 ├—┼—┤ ├—┼—┤
 4│E │ —→│ 1│∧│
 ├—┼—┤ ├—┼—┤
 5│F │ —→│ 2│∧│
 ├—┼—┤ ├—┼—┤
 6│G │ —→│ 2│∧│
 └—┴—┘ └—┴—┘
 邻接矩阵如下:
 0 1 1 0 0 0 0
 1 0 0 1 1 0 0
 1 0 0 0 0 1 1 
 0 1 0 0 0 0 0

  0 1 0 0 0 0 0 
 0 0 1 0 0 0 0
 0 0 1 0 0 0 0
关闭
7.5 对n个顶点的无向图和有向图,采用邻接矩阵和邻接表表示时,如何判别下列有关问题?
 (1) 图中有多少条边?
 (2)任意两个顶点i和j是否有边相连?
 (3) 任意一个顶点的度是多少?
答:
 对于n个顶点的无向图和有向图,用邻接矩阵表示时:
 (1)设m为矩阵中非零元素的个数 
   无向图的边数=m/2
   有向图的边数=m
 (2)无论是有向图还是无向图,在矩阵中第i行,第j列的元素若为非零值,则该两顶点有边相连。
 (3)对于无向图,任一顶点i的度为第i行中非零元素的个数。
  对于有向图,任一顶点i的入度为第i列中非零元素的个数,出度为第i行中非零元素的个数,度为入度出度之和。
  当用邻接表表示时:
 (1)对于无向图,图中的边数=边表中结点总数的一半。
   对于有向图,图中的边数=边表中结点总数。
 (2)对于无向图,任意两顶点间是否有边相连,可看其中一个顶点的邻接表,若表中的adjvex域有另一顶点位置的结点,则表示有边相连。
   对于有向图,则表示有出边相连。
 (3)对于无向图,任意一个顶点的度则由该顶点的边表中结点的个数来决定。
  对于有向图,任意一个顶点的出度由该顶点的边表中结点的个数来决定,入度则需遍历各顶点的边表。(用逆邻接表可容易地得到其入度。)
关闭
7.6 n个顶点的连通图至少有几条边?强连通图呢?
答:
  n个顶点的连通图至少有n-1条边,强连通图至少有2(n-1)条边。
关闭
7.7 DFS和BFS遍历各采用什么样的数据结构来暂存顶点?当要求连通图的生成树的高度最小,应采用何种遍历?
答:
  DFS遍历采用栈来暂存顶点。BFS采用队列来暂存顶点。当要求连通图的生成树的高度最小时,应采用BFS遍历。
关闭
7.8 画出以顶点v1为初始源点遍历图7.25(下图)所示的有向图所得到的DFS 和BFS生成森林。
     
答:
   
关闭
7.9 按顺序输入顶点对:(1,2),(1,6),(2,6),(1,4),(6,4),(1,3),(3,4),(6,5),(4,5),(1,5), (3,5),根据第7.2.2节中算法CreatALGraph画出相应的邻接表。并写出在该邻接表上,从顶点4开始搜索所得的DFS和BFS序列,及 DFS和BFS生成树。 
答:
  相应的邻接表如下:
   ┌—┬—┐ ┌—┬—┐ ┌—┬—┐ ┌—┬—┐ ┌—┬—┐ ┌—┬—┐
  1│1 │ —→│ 5│ —→│ 3│ —→│ 4│ —→│ 6│ —→│ 2│∧│
   ├—┼—┤ ├—┼—┤ ├—┼—┤ └—┴—┘ └—┴—┘ └—┴—┘
 2│2 │ —→│ 6│ —→│ 1│∧│
 ├—┼—┤ ├—┼—┤ ├—┼—┤ ┌—┬—┐
 3│3 │ —→│ 5│ —→│ 4│ —→│ 1│∧│
 ├—┼—┤ ├—┼—┤ ├—┼—┤ ├—┼—┤ ┌—┬—┐
 4│4 │ —→│ 5│ —→│ 3│ —→│6 │ —→│ 1│∧│
 ├—┼—┤ ├—┼—┤ ├—┼—┤ ├—┼—┤ ├—┼—┤
 5│5 │ —→│ 3│ —→│ 1│ —→│ 4│ —→│ 6│∧│
 ├—┼—┤ ├—┼—┤ ├—┼—┤ ├—┼—┤ ├—┼—┤
 6│6 │ —→│ 5│ —→│ 4│ —→│ 2│ —→│ 1│∧│
 └—┴—┘ └—┴—┘ └—┴—┘ └—┴—┘ └—┴—┘
 根据上面的邻接表画出的图见下图:
 从顶点4开始搜索所得的DFS序列是:453162
 从顶点4开始搜索所得的BFS序列是:453612
 相应的生成树见上图。
关闭
7.10 什么样的图其最小生成树是唯一的?用PRIM 和Kruskal求最小生成树的时间各为多少?它们分别适合于哪类图?
答:
  当候选轻边集中的轻边数始终等于1条时,其最小生成树是唯一的。用Prim和Kruskal求最小生成树的时间复杂度分别为O(n2)和O(elge),前者适合于稠密图,后者适合于稀疏图.
关闭
7.11 对图7.26(下图)所示的连通图,请分别用Prim和Kruskal算法构造其最小生成树。 

 
答:
   
关闭
7.12 对图7.27(下图)所示的有向图,试利用Dijkstra算法求出从源点1到其它各顶点的最短路径,并写出执行算法过程中扩充红点集的每次循环状态(见表7.2). 
   
答:
  循环状态表如下:
  循环 红点集 K D[1] D[2] D[3] D[4] D[5] D[6] P[1] P[2] P[3] P[4] P[5] P[6] 
 初始化  {1} - 0 20 15 ∞ ∞ ∞ -1 1 1 -1 -1 -1 
 1 {1,3} 3 0 19 15 ∞ ∞ 25 -1 3 1 -1 -1 3 
 2 {1,3,2} 2 0 19 15 ∞ 29 25 -1 3 1 -1 2 3 
 3 {1,3,2,6} 6 0 19 15 29 29 25 -1 3 1 6 2 3 
 4 {1,3,2,6,4} 4 0 19 15 29 29 25 -1 3 1 6 2 3 
 5 {1,3,2,6,4,5} 5 0 19 15 29 29 25 -1 3 1 6 2 3 
 6 同上 - 同上 同上 
从源点1到各点的路径如下所示:
 1到2:132
  1到3:13
  1到4:1364
  1到5:1325
  1到6:136
 整个执行算法过程中的扩充红点集的每次循环状态见上表。
关闭
7.13 试写出图7.28(下图)所示有向图的所有拓扑序列,并指出就用7.6节给出的NonPreFirstTopSort算法求得的是哪个序列,设邻接表的边表结点中的邻接点序号是递增有序的。 
答:
 上图中所有拓扑序列如下:
 v0v1v5v2v3v6v4 *
 v0v1v5v2v6v3v4
 v0v1v5v6v2v3v4
 v1v0v5v6v2v3v4
 v1v0v5v2v3v6v4
 v1v0v5v2v6v3v4
 v1v5v0v2v3v6v4
 v1v5v0v2v6v3v4
 v1v5v0v6v2v3v4
 v5v1v0v2v3v6v4
 v5v1v0v2v6v3v4
 v5v1v0v6v2v3v4
 v5v0v1v2v3v6v4
 v5v0v1v2v6v3v4
 v5v0v1v6v2v3v4
 v0v5v6v1v2v3v4
 v1v5v6v0v2v3v4
 v5v6v1v0v2v3v4
 v5v6v0v1v2v3v4
 v5v0v6v1v2v3v4
 v5v1v6v0v2v3v4
 用NonPreFirstTopSort算法求得的是v0v1v5v2v3v6v4也就是上面带*号的那个。
关闭

7.14 什么样的DAG的拓扑序列是唯一的? 
答:
 确定了排序的源点,DAG图中无前趋顶点只有一个且从该点到终点只有一条路径时,它的拓扑序列才是唯一的。
关闭
7.15 请以V0为源点,给出用DFS搜索图7.28(下图)得到的逆拓扑序列。

 
答:
 逆拓扑序列是:V4 V2 V1 V0 V1 V6 V5
关闭
7.16 试在无向图的邻接矩阵和邻接链表上实现如下算法:
 (1)往图中插入一个顶点
 (2)往图中插入一条边
 (3)删去图中某顶点
 (4)删去图中某条边
解:
  (一)用邻接矩阵表示
 #define MaxVertexNum l00 //最大顶点数,应由用户定义
 typedef char VertexType; //顶点类型应由用户定义
 typedef int EdgeType; //边上的权值类型应由用户定义
 typedef struct{
   VextexType vexs[MaxVertexNum] //顶点表
   EdeType edges[MaxVertexNum][MaxVertexNum];
   //邻接矩阵,可看作边表
   int n,e; //图中当前的顶点数和边数
  }MGragh;
 (1)往图中插入一个顶点 
 void AddVertexMGraph(MGraph *G,VertexType x)
  {//往无向图的邻接矩阵中插入顶点
    if (G->n>=MaxVertexNum)
      Error("顶点数太多");
    G->vexs[G->n]=x;//将新顶点输入顶点表
    G->n++;//顶点数加1
  }
 (2)往图中插入一条边
 void AddedgeMGraph(MGraph *G,VertexType x,VertexType y)
  {//往无向图的邻接矩阵中插入边(x,y) 
   int i,j,k;
   i=-1;j=-1;
   for(k=0;k<G->n;k++)//查找X,Y的编号
    { if (g->vexs[k]===x) i=k;
     if (g->vexs[k]==y) j=k;
    }
   if (i==-1||j==-1) Error("结点不存在");
   else {//插入边(x,y)
        g->vex[i][j]=1;
        g->vex[j][i]=1;
        G->e++;//边数加1
      }
  }
 (3)删去图中某顶点
 void DeleteVertexMGraph(MGraph *G,VertexType x)
  {//无向图的邻接矩阵中删除顶点x

     int i,k,j;
    i=-1;
    for(k=0;k<G->n;k++)//查找X的编号
      if (g->vexs[k]=x) i=k;
    if (i==-1) Error("结点不存在");
    else {//删除顶点以及边
        k=0;//求出与x结点相关联的边数k
        for (j=0;j<G->n;j++)
          if (g->vexs[i][j]==0) k++;
          G->e=G->e-k;//设置新的边数
        for (k=i+1;k<G->n;k++)//在邻接矩阵中删除第i行
          for(j=0;j<G->n;j++)
            g->vexs[k-1][j]=g->vexs[k][j];
        for (k=i+1;k<G->n;k++)//在邻接矩阵中删除第i列
          for(j=0;j<G->n;j++)
            g->vexs[j][k-1]=g->vexs[j][k];
        G->n--;//总结点数-1
       }
  } 
 (4)删去图中某条边
 void DeleteedgeMGraph(MGraph *G,VertexType x,VertexType y)
   {//无向图的邻接矩阵中删除边(x,y)
     int i,j,k;
     i=-1;j=-1;
     for(k=0;k<G->n;k++)//查找X,Y的编号
       { if (g->vexs[k]=x) i=k;
        if (g->vexs[k]=y) j=k;
       }
     if (i==-1||j==-1) Error("结点不存在");
     else if (g->vexs[i][j]!=1)
         {//删除边(x,y)
           g->vex[i][j]=0;
           g->vex[j][i]=0;
           G->e--;//边数加1
         }
   }
(二)用邻接表表示 
 typedef struct node{//边表结点
    int adjvex; //邻接点域
    struct node *next; //链域
     //若要表示边上的权,则应增加一个数据域
   }EdgeNode;
 typedef struct vnode{ //顶点表结点
     VertexType vertex; //顶点域
    EdgeNode *firstedge;//边表头指针
    }VertexNode;
 typedef VertexNode AdjList[MaxVertexNum];//AdjList是邻接表类型
 typedef struct{
    AdjList adjlist;//邻接表
    int n,e; 图中当前顶点数和边数 
   }ALGraph; //对于简单的应用,无须定义此类型,可直接使用AdjList类型。 
 (1)往图中插入一个顶点
 void AddVertexALGraPh(ALGrahp *G,VertexType x)
   {//往无向图的邻接表表示中插入一个顶点
     if (G->n>=MaxVertexNum)
        Error("顶点数太多");
     G->adjlist[G->n].vertex=x;//将新顶点输入顶点表
     G->adjlist[G->n].firstedge=NULL;//边表置为空表
     G->n++;//顶点数加1
   }
 (2)往图中插入一条边
 void AddedgeALGraPh(ALGrahp *G,VertexType x,VertexType y)
   {//往无向图的邻接表中插入边(x,y)
     int i,j,k;
     EdgeNode *s;
     i=-1;j=-1;
     for(k=0;k<G->n;k++)//查找X,Y的编号
       { if (G->adjlist[k].vertex==x) i=k;
        if (G->adjlist[k].vertex==y) j=k;
       }
     if (i==-1||j==-1) Error("结点不存在");
     else {
         s=G->adjlist[i].firstedge;
         while ((s)&&(s->adjvex!=j)//查看邻接表中有无(x,y)
           s=s->next;
         if (!s)//当邻接表中无边(x,y),插入边(x,y)
           { 
             s=(EdgeNode *)malloc(sizeof(EdgeNode)); //生成边表结点
             s->adjvex=j; //邻接点序号为j
             s->next=G->adjlist[i].firstedge;

              G->adjlist[i].firstedge=s;//将新结点*s插入顶点x的边表头部
             s=(EdgeNode *)malloc(sizeof(EdgeNode));
             s->adjvex=i; //邻接点序号为i
             s->next=G->adjlist[j].firstedge;
             G->adjlist[j].firstedge=s; //将新结点*s插入顶点x的边表头部
             G->e++;//边数加1
           }
        }
   } 
 (3)删去图中某顶点
 void DeleteVertexALGraPh(ALGrahp *G,VertexType x)
  {//无向图的邻接表中删除顶点x 
 
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值