Begtostudy(白途思)'s Professional Technology Blog

欢迎访问begtostudy的专业知识博客!主要是专业技术和算法为主。
  首页  :: 联系 :: 订阅 订阅  :: 管理

数据结构 第4章 数组和广义表

Posted on 2010-09-20 19:27  白途思  阅读(837)  评论(0编辑  收藏  举报

4 数组和广义表


本章主要介绍下列内容:
  1.数组的定义、基本运算和存储结构
  2.特殊矩阵的压缩存储
  3.广义表的定义、术语、存储结构、运算
  4.递归算法设计
课时分配:
    1
两个学时,2两个学时,3两个学时, 4两个学时
重点、难点:
   
特殊矩阵的压缩存储、广义表的存储结构、递归算法设计

 

第一节 数组

1.数组的定义和基本运算
   
数组的特点是每个数据元素可以又是一个线性表结构。因此,数组结构可以简单地定义为:若线性表中的数据元素为非结构的简单元素,则称为一维数组,即为向量;若一维数组中的数据元素又是一维数组结构,则称为二维数组;依次类推,若二维数组中的元素又是一个一维数组结构,则称作三维数组。
   
结论:线性表结构是数组结构的一个特例,而数组结构又是线性表结构的扩展。举例:

    其中,A是数组结构的名称,整个数组元素可以看成是由m个行向量和n个列向量组成,其元素总数为m×n C语言中,二维数组中的数据元素可以表示成a[表达式1][表达式2],表达式1和表达式2被称为下标表达式,比如,a[i][j] 数组结构在创建时就确定了组成该结构的行向量数目和列向量数目,因此,在数组结构中不存在插入、删除元素的操作。
   
二维数组结构的基本操作:
1)给定一组下标,修改该位置元素的内容 Assign(A,elem,index1,index2)
2)给定一组下标,返回该位置的元素内容 Value(A,elem,index1,index2)
2
.数组的存储结构
   
从理论上讲,数组结构也可以使用两种存储结构,即顺序存储结构和链式存储结构。然而,由于数组结构没有插入、删除元素的操作,所以使用顺序存储结构更为适宜。换句话说,一般的数组结构不使用链式存储结构。 组成数组结构的元素可以是多维的,但存储数据元素的内存单元地址是一维的,因此,在存储数组结构之前,需要解决将多维关系映射到一维关系的问题。举例:      LOC(i,j)=LOC(0,0)+(n*i+j)*L
   
数组结构的定义:
       #define MAX_ROW_INDEX 10
       #define MAX_COL_INDEX 10
       typedef struct{Elemtype elem[MAX_ROW_INDEX][MAX_COL_INDEX]        } ARRAY;
   
基本操作算法举例:
1)给数组元素赋值
    void Assign(ARRAY *A,Elemtype elem,int index1,int index2)
    { if (index1<0||index1>=MAX_ROW_INDEX||index2<0||index2>=MAX_COL_INDEX)      exit(ERROR);
    else A->elem[index1][index2]=elem; }
2)返回给定位置的元素内容
    int Value(ARRAY A,Elemtype *elem,int index1,int index2)
    { if (index1<0||index1>=MAX_ROW_INDEX|| index2<0||index2>=MAX_COL_INDEX) return FALSE;
    else { *elem= A.elem[index1][index2]; return OK;   
3
.矩阵的压缩存储
   
矩阵是在很多科学与工程计算中遇到的数学模型。在数学上,矩阵是这样定义的:它是一个由s×n个元素排成的s行(横向)n列(纵向)的表。下面就是一个矩阵

31特殊矩阵


   
对于这些特殊矩阵,应该充分利用元素值的分布规律,将其进行压缩存储。选择压缩存储的方法应遵循两条原则:一是尽可能地压缩数据量,二是压缩后仍然可以比较容易地进行各项基本操作。
   
三种特殊矩阵的压缩方法:
 
1)对称矩阵 对称矩阵的特点是aij=aji。一个n×n的方阵,共有n2个元素,而实际上在对称矩阵中有n(n-1)/2个元素可以通过其他元素获得。压缩的方法是首先将二维关系映射成一维关系,并只存储其中必要的n(n+1)/2个(主对角线和下三角)元素内容,这些元素的存储顺序以行为主序。举例: 假设定义一个数组型变量:int A[10];

    k是对称矩阵位于(ij)位置的元素在一维数组中的存放位置。 操作算法的实现:
    int Value(int A[],Elemtype *elem,int i,int j) {
    if(i<1||i>MAX_ROW_INDEX|| j<1||j>MAX_COL_INDEX) return FALSE;
    else { if (i>=j) k=i*(i-1)/2+j-1;
           else k=j*(j-1)/2+i-1;
           *elem=A[k];
           return TRUE; }
          }
  
2)下(上)三角矩阵 下三角矩阵的压缩存储与上面讲述的对称矩阵的压缩存储一样,只是将上三角部分的常量值存储在0单元,下三角和主对角上的元素从1号单元开始存放。 举例:

    操作算法的实现:
      int Value(int A[],Elemtype *elem,int i,int j)
      {
      if(i<1||i>MAX_ROW_INDEX||j<1||j>MAX_COL_INDEX) return FALSE;
      else { if (i>=j) k=i*(i-1)/2+j;
             else k=0;
             *elem=A[k];
             return TRUE;
            }
      }
  
3)对角矩阵
   
我们以三阶对角矩阵为例讨论一下它的压缩存储方法。对于对角矩阵,压缩存储的主要思路是只存储非零元素。这些非零元素按以行为主序的顺序,从下标为1 的位置开始依次存放在一维数组中,而0位置存放数值0

    操作算法的实现:
      int Value(int A[ ],Elemtype *elem,int i,int j)
      {
      if(i<1||i>MAX_ROW_INDEX||j<1||j>MAX_COL_INDEX) return FALSE;
      else { if (j>=(i-1)&&j<=(i+1)) k=3*(i-1)+j-i+1;
             else k=0;
             *elem=A[k];
             return TRUE;
            }
      }
3
2 稀疏矩阵的压缩存储

   
类型定义:
      #define MAX_SIZE 100
最大的非零元素个数
      typedef struct{
      int i,j; //
行序号、列序号
      Elemtype value; //
非零元素值
      }Three_Elem;
      typedef struct {
      Three_Elem Elem[MAXSIZE]; //
存储非零元素的一维数组
      int rows,cols,tu; //
稀疏矩阵的总行数、列数及非零元素个数
      }Matrix;
   
操作算法的实现:
  
1)返回元素内容
      int Value(Matrix M,Elemtype *elem,int i,int j)
      if (i<1||i>rows||j<1||j>cols) exit(ERROR);
      else { for (p=0;p<M.tu;p++)
               if(M.elem[p].i==i&&M.elem[p].j==j)
               { *elem=M.elem[p].value; return OK; }
   else if (M.elem[p].i>i||M.elem[p].i==i&&M.Elem[p].j>j) break;
      *elem=0;
      return OK;
          }
        }
  
2)输出三元组表示的稀疏矩阵
      void Print(Matrix M)
      {
      for (p=0,i=1;i<=M.rows;i++) {
      for (j=1;j<=M.cols;j++)
if(p<M.tu&&M.elem[p].i==i&&M.elem[p].j==j) printf("%4d",M.elem[p++].value;);
else printf("%4d",0);
      printf("\n");
      }
     }

第二节 广义表

1. 广义表的定义
   
广义表(Lists,又称列表)是线性表的推广。在第2章中,我们把线性表定义为n>=0个元素a1,a2,a3,…,an的有限序列。线性表的元素仅限于原子项,原子是作为结构上不可分割的成分,它可以是一个数或一个结构,若放松对表元素的这种限制,容许它们具有其自身结构,这样就产生了广义表的概念。
   
广义表是n(n>=0)个元素a1,a2,a3,…,an的有限序列,其中ai或者是原子项,或者是一个广义表。通常记作LS=a1,a2,a3,…,an)LS是广义表的名字,n为它的长度。若ai是广义表,则称它为LS的子表。
   
通常用圆括号将广义表括起来,用逗号分隔其中的元素。为了区别原子和广义表,书写时用大写字母表示广义表,用小写字母表示原子。若广义表LSn>=1)非空,则a1LS的表头,其余元素组成的表(a1,a2,…an)称为LS的表尾。
   
显然广义表是递归定义的,这是因为在定义广义表时又用到了广义表的概念。广义表的例子如下:
1A=()--A是一个空表,其长度为零。
2B=e--B只有一个原子eB的长度为1
3C=a,(b,c,d))--C的长度为2,两个元素分别为原子a和子表(b,c,d)
4D=ABC--D的长度为3,三个元素都是广义表。显然,将子表的值代入后,则有D=(( ),(e),(a,(b,c,d)))
5E=E--这是一个递归的表,它的长度为2
      E
相当于一个无限的广义表E=(a,(a,(a,(a,…)))).
   
从上述定义和例子可推出广义表的三个重要结论:
1)广义表的元素可以是子表,而子表的元素还可以是子表。由此,广义表是一个多层次的结构,可以用图形象地表示。
2)广义表可为其它表所共享。例如在上述例(4)中,广义表ABCD的子表,则在D中可以不必列出子表的值,而是通过子表的名称来引用。
3)广义表的递归性。
   
综上所述,广义表不仅是线性表的推广,也是树的推广。
2.
广义表的存储结构

   
由于广义表(a1,a2,a3,…an)中的数据元素可以具有不同的结构,(或是原子,或是广义表),因此,难以用顺序存储结构表示,通常采用链式存储结构,每
个数据元素可用一个结点表示。
   
由于广义表中有两种数据元素,原子或广义表,因此,需要两种结构的结点:一种是表结点,一种是原子结点。 下面介绍两种广义表的链式存储结构。
   
表结点由三个域组成:标志域、指示表头的指针域和指示表尾的指针域;而原子域只需两个域:标志域和值域。

    其类型定义如下:
        typedef enum{ATOM,LIST}elemtag;
        typedef struct glnode{
        elemtag tag;
        union{
        atomtype atom;
        struct {
        struct glnode *hp,*tp;
        }ptr;
        };
        } *glist;
   
例见书。
3.
分析求广义表深度和长度的递归算法(见教材)
   
这部分内容比较难,用1个课时讲解,用1个课时答疑

前往Begtostudy的编程知识博客(CSDN)