《数据结构(C语言版)》——矩阵相关笔记

5.矩阵的压缩储存

5.1 特殊矩阵

  • n阶对称矩阵
    按行序为主序,一维数组 s z [ n ( n + 1 ) / 2 ] sz[n(n+1)/2] sz[n(n+1)/2]作为储存结构
    a = [ a 11 a 12 . . . a 1 n a 21 a 22 . . . a 2 n . . . . . a n 1 a n 2 . . . a n n ] k = { i ( i − 1 ) / 2 + j − 1 , if  i  >=j j ( j − 1 ) / 2 + i − 1 , if  i <j a = \left[ \begin{matrix} a_{11} & a_{12} & ... & a_{1n} \\ a_{21} & a_{22} & ... & a_{2n} \\ & .....\\ a_{n1} & a_{n2} & ... & a_{nn} \end{matrix} \right]\\ \\ k = \begin{cases} i(i-1)/2+j-1, & \text{if }i\text{ >=j} \\ j(j-1)/2+i-1, & \text{if }i\text{<j} \end{cases} a=a11a21an1a12a22.....an2.........a1na2nannk={i(i1)/2+j1,j(j1)/2+i1,if i >=jif i<j
  • 下三角矩阵

a = [ a 11 0 . . . 0 a 21 a 22 . . . 0 . . . . . 0 a n 1 a n 2 . . . a n n ] k = { i ( i − 1 ) / 2 + j − 1 , if  i  >=j j ( j − 1 ) / 2 + i − 1 , if  i <j a = \left[ \begin{matrix} a_{11} & 0 & ... & 0 \\ a_{21} & a_{22} & ... & 0 \\ & & .....&0\\ a_{n1} & a_{n2} & ... & a_{nn} \end{matrix} \right]\\ \\ k = \begin{cases} i(i-1)/2+j-1, & \text{if }i\text{ >=j} \\ j(j-1)/2+i-1, & \text{if }i\text{<j} \end{cases} a=a11a21an10a22an2..............000annk={i(i1)/2+j1,j(j1)/2+i1,if i >=jif i<j

  • 对角矩阵

a = [ a 11 a 12 0 . . . . . . 0 a 21 a 22 a 23 . . . . . . 0 0 a 32 a 33 a 34 . . . 0 . . . . . 0 0 . . . a n − 1 , n − 2 a n − 1 , n − 1 a n − 1 , n 0 0 . . . 0 a n , n − 1 a n n ] L o c ( a i j ) = L o c ( a 11 ) + [ 2 ( i − 1 ) + ( j − 1 ) ] ∗ l a = \left[ \begin{matrix} a_{11} & a_{12} & 0 &... &... & 0 \\ a_{21} & a_{22} & a_{23} &... &... & 0 \\ 0 & a_{32} & a_{33} & a_{34} & ... & 0 \\ & &&&& .....\\ 0 &0&... & a_{n-1,n-2} & a_{n-1,n-1} & a_{n-1,n}\\ 0 & 0 & ... & 0 &a_{n,n-1} & a_{nn} \end{matrix} \right]\\ \\ Loc(a_{ij}) = Loc(a_{11})+[2(i-1)+(j-1)]*l a=a11a21000a12a22a32000a23a33............a34an1,n20.........an1,n1an,n1000.....an1,nannLoc(aij)=Loc(a11)+[2(i1)+(j1)]l

5.2稀疏矩阵

5.2.1顺序储存
  1. 三元组顺序表
   typedef int ElemType;
   /* 三元组类型定义,主要用来存储非零元 */
   typedef struct {
       int i, j;       // 该三元组非零元的行下标和列下标
       ElemType e;
   } Triple;
   /* 三元组稀疏矩阵类型定义 */
   typedef struct {
       Triple data[MAXSIZE + 1];   // 非零元三元组表,data[0]未用
       int mu, nu, tu;             // 矩阵的行数、列数和非零元个数
   } TSMatrix;
  1. 逻辑链接的稀疏矩阵
   /* 行逻辑链接的稀疏矩阵类型定义 */
   typedef struct {
       Triple data[MAXSIZE + 1];   // 非零元三元组表,data[0]未用
       int rpos[MAXRC + 1];        // 各行第一个非零元在三元组表中的位置表,rpos[0]未用
       int mu, nu, tu;             // 矩阵的行数、列数和非零元个数
   } RLSMatrix;
  1. 伪地址
    伪地址的运用于考察不多,故不做介绍
5.2_(*)矩阵转置
  • 方法1 按M的列序转置
//算法5.1
Status TransposeSMatrix(TSMatrix M, TSMatrix* T) {
    int p, q, col;
    
    (*T).mu = M.nu;
    (*T).nu = M.mu;
    (*T).tu = M.tu;
    
    if((*T).tu != 0) {
        q = 1;  // q用于T中非零元的计数
        
        // col代表M的列,T的行
        for(col = 1; col <= M.nu; ++col) {
            // 在M中查找第j列的元素,依次将其转置到T中
            for(p = 1; p <= M.tu; ++p) {
                if(M.data[p].j == col) {
                    (*T).data[q].i = M.data[p].j;    // M的列变为T的行
                    (*T).data[q].j = M.data[p].i;    // M的行变为T的列
                    (*T).data[q].e = M.data[p].e;    // 每个三元组值不变
                    ++q;
                }
            }
        }
    }
    return OK;
}
  • 方法2 快速转置
//算法5.2
Status FastTransposeSMatrix(TSMatrix M, TSMatrix* T) {
    int col, t, p, q;
    int* num;       // num[col] 表示M第col列中非零元的个数
    int* copt;      // copt[col]表示M第col列第一个非零元在转置后矩阵中的位置
    (*T).mu = M.nu;
    (*T).nu = M.mu;
    (*T).tu = M.tu;
    // 提前返回
    if((*T).tu == 0) {
        return ERROR;
    } 
    num  = (int*) malloc((M.nu + 1) * sizeof(int));
    copt = (int*) malloc((M.nu + 1) * sizeof(int)); 
    // 初始化数组num
    for(col = 1; col <= M.nu; ++col) {
        num[col] = 0;
    }
    // 统计M中的非零元,统计每列非零元的个数
    for(t = 1; t <= M.tu; ++t) {
        num[M.data[t].j]++;
    }
    // 第1列第1个非零元总是位于转置后矩阵中的首位
    copt[1] = 1;
    // 计算各列第1个非零元在转置矩阵中的位置
    for(col = 2; col <= M.nu; ++col) {
        copt[col] = copt[col - 1] + num[col - 1];
    }
    // 依次扫描M中的三元组
    for(p = 1; p <= M.tu; ++p) {
        col = M.data[p].j;                // 计算当前非零元所处的列
        q = copt[col];                    // 计算当前非零元在转置矩阵中的位置
        (*T).data[q].i = M.data[p].j;
        (*T).data[q].j = M.data[p].i;
        (*T).data[q].e = M.data[p].e;
        ++copt[col];                      // 再遇到此列元素时,其在转置矩阵中的位置应当增一(该步骤很重要)
    }
    
    return OK;
}
5.2.2 链式储存
  1. 带行指针的单链表
    • 每行的非零元用一个单链表存放
    • 设置一个行指针数组,指向本行第一个非零元结点;若本行无非零元,则指针为空
    • 数据结构定义方法:
	typedef  struct node
	{     int  col;
	      int  val;
	      struct node  *link;
	}JD;
	typedef  struct  node   *TD
  1. 十字链表
    设行指针数组和列指针数组,分别指向每行、列第一个非零元
	/* 非零元类型定义 */
	typedef struct OLNode {
	    int i, j;               // 该非零元的行下标和列下标
	    ElemType e;
	    struct OLNode* right;   // 该非零元所在的行表的后继链域
	    struct OLNode* down;    // 该非零元所在的列表的后继链域
	} OLNode, * OLink;
	
	/* 十字链表类型定义 */
	typedef struct {
	    OLink* rhead;       // 行链表头指针
	    OLink* chead;       // 列链表头指针
	    int mu, nu, tu;     // 矩阵的行数、列数和非零元个数
	} CrossList;
// 算法5.4:如何建立十字链表
Status CreateSMatrix(CrossList* M, char* path) {
    int i, j, k;
    OLNode* p, * q;
    FILE* fp;
    printf("请输入行数:");
    scanf("%d", &((*M).mu));
    printf("请输入列数:");
    scanf("%d", &((*M).nu));
    printf("请输入非零元素个数:");
    scanf("%d", &((*M).tu));
    printf("请输入%d个三元组信息\n", (*M).tu);  
    // 创建行链(类似行索引,0号单元弃用)
    (*M).rhead = (OLink*) malloc(((*M).mu + 1) * sizeof(OLink));
    if((*M).rhead == NULL) {
        exit(OVERFLOW);
    }  
    // 创建列链(类似列索引,0号单元弃用)
    (*M).chead = (OLink*) malloc(((*M).nu + 1) * sizeof(OLink));
    if((*M).chead == NULL) {
        exit(OVERFLOW);
    }  
    // 初始化行链索引为NULL
    for(k = 0; k <= (*M).mu; ++k) {
        (*M).rhead[k] = NULL;
    }   
    // 初始化列链索引为NULL
    for(k = 0; k <= (*M).nu; ++k) {
        (*M).chead[k] = NULL;
    }   
    // 依次录入非零元
    for(k = 1; k <= (*M).tu; ++k) {
        // 创建三元组结点
        p = (OLNode*) malloc(sizeof(OLNode));
        if(p == NULL) {
            exit(OVERFLOW);
        }     
        printf("第%2d组:", k);
        scanf("%d%d%d", &i, &j, &(p->e));    
        p->i = i;   // 行号
        p->j = j;   // 列号
        p->right = p->down = NULL;
        
        // 开始行的插入       
        // 如果该行还没有元素,或已有元素均位于该元素右侧,则可以直接插入
        if((*M).rhead[i] == NULL || (*M).rhead[i]->j > j) {
            // 定位行表中的插入位置
            p->right = (*M).rhead[i];
            (*M).rhead[i] = p;
        } else {
            // 寻找插入位置的前一个位置
            for(q = (*M).rhead[i]; (q->right) && (q->right->j < j); q = q->right) {
            }
            
            if(q->j == p->j || ((q->right) && q->right->j == p->j)) {
                printf("此位置已被占用!!\n");
                exit(ERROR);
            }
            
            p->right = q->right;
            q->right = p;
        }
        
        /*
         * 开始列的插入
         */
        
        // 如果该列还没有元素,或已有元素均位于该元素下侧,则可以直接插入
        if((*M).chead[j] == NULL || (*M).chead[j]->i > i) {
            // 定位列表中的插入位置
            p->down = (*M).chead[j];
            (*M).chead[j] = p;
        } else {
            // 寻找插入位置的前一个位置
            for(q = (*M).chead[j]; (q->down) && (q->down->i < i); q = q->down) {
            }
            
            if(q->i == p->i || ((q->down) && q->down->i == p->i)) {
                printf("此位置已被占用!!\n");
                exit(ERROR);
            }
            
            p->down = q->down;
            q->down = p;
        }
    } 
    return OK;
}

5.3广义表

L S = ( α 1 , α 2 , α 3 , . . . , α n ) LS = (\alpha_1,\alpha_2,\alpha_3,...,\alpha_n) LS=(α1,α2,α3,...,αn),其中 α i \alpha_i αi可以是单个元素也可以是广义表,一般而言, α 1 \alpha_1 α1为表头, α n \alpha_n αn为表尾

  • 任何一个非空列表其表头可能是原子,也可能是列表.而其表尾必定为列表。
  • 习惯上,用大写字母表示广义表的名称,用小写字母表示原子。当广义表LS非空时,称第一个元素a1为LS的表头(Head),称其余元素组成的表为LS的表尾(Tail)。
  • 值得提醒的是列表()和(())不同。前者为空表.长度n=0;后者长度n=1,可分解得到其表头、表尾均为空表()
  • (())与()是两个不同的列表,前者长度为1,表头与表尾均为空表,后者为空表,长度为0
  • 如:A = (),B = (e),C = (a,(b,c,d)),D = (A,B,C)
  • 储存结构:
  1. 链式储存结构,每个数据元素用一个节点表示
     //Atom-0:原子结点 List-1:表结点
     typedef enum { Atom, List } ElemTag;
     typedef struct GLNode {
          ElemTag tag;    // 公共标记,用于区分原子结点和表结点
          union {// 原子结点和表结点的联合部分
               AtomType atom;     // atom是原子结点的值域,AtomType由用户定义
               struct {
                     struct GLNode* hp;  // 指向表头
                     struct GLNode* tp;  // 指向表尾
               } ptr;                  // 表结点的指针域
          } Node;
     } GLNode;
     /* 广义表类型 */
     typedef GLNode* GList;
  1. 扩展的线性表表示
   typedef enum { Atom, List } ElemTag;
  /* 广义表(扩展线性链表存储表示)类型定义 */
  typedef struct GLNode {
      ElemTag tag;    // 公共标记,用于区分原子结点和表结点     
      // 原子结点和表结点的联合部分
      union
      {
          AtomType atom;      // atom是原子结点的值域,AtomType由用户定义
          struct GLNode* hp;  // 指向表头
      } Node;
      struct GLNode* tp;      // 指向表尾
  } GLNode;   
  typedef GLNode* GList;//广义表类型

本文为华中科技大学(WuHan China)人工智能与自动化学院 2020级《数据结构与算法分析》课堂所做的个人笔记,结课之余慢慢更新,未来将继续更新《数据结构习题集(C语言版)》解析以及所用代码等。
当然,如果你会使用git工具,也可以直接在作者的Github个人主页直接下载学习
本文相关链接:作者的Github相关主页
由于作者才疏学浅,文章有错误之处请私信或发邮件(szh-hust@outlook.com)指正

  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值