数据结构与算法·第5章【数组和广义表】

news2024/11/26 8:23:51

数组

基本操作

InitArray(&A, n, bound1, ..., boundn)

DestroyArray(&A)

Value(A, &e, index1, ..., indexn)

Assign(&A, e, index1, ..., indexn)

数组的顺序表示

两种顺序映象的方式:

  1. 以行序为主序(低下标优先);
  2. 以列序为主序(高下标优先)。

在这里插入图片描述

n n n维数组:LOC(x1, x2, ..., xn) = LOC(0, 0, ..., 0) + [(x1 × b1 + x2) × b2 + x3] × b3 + ... + xn

数据类型定义

#include <stdarg.h> // 标准头文件,提供宏 va_start、va_arg 和 va_end,用于存取变长参数表

#define MAX_ARRAY_DIM 8 // 假设数组维数的最大值为 8

typedef struct {
    ElemType *base;  // 数组元素地址,由 InitArray 分配
    int dim;         // 数组维数
    int *bounds;     // 数组维界基址,由 InitArray 分配
    int *constants;  // 数组映像函数常量基址,由 InitArray 分配
} Array;

其中:

Status InitArray(Array& A, int dim, ...) {
    // 若维数 dim 不合法,则返回 ERROR
    if (dim < 1 || dim > MAX_ARRAY_DIM) {
        return ERROR;
    }
    A.dim = dim;
    A.bounds = (int*)malloc(dim * sizeof(int));
    if (!A.bounds) {
        exit(OVERFLOW);
    }
    // 存储各维长度,并计算元素总数 elemtotal
    int elemtotal = 1;
    va_list ap;  // 定义 va_list 类型变量 ap,用于存放变长参数表信息的数组
    va_start(ap, dim);  // 初始化 ap 数组
    for (int i = 0; i < dim; ++i) {
        A.bounds[i] = va_arg(ap, int);
        if (A.bounds[i] < 0) {
            return UNDERFLOW;
        }
        elemtotal *= A.bounds[i];
    }
    va_end(ap);  // 结束 ap 数组

    A.base = (ElemType*)malloc(elemtotal * sizeof(ElemType));
    if (!A.base) {
        exit(OVERFLOW);
    }
    // 求映像函数的常数 ci,并存入 A.constants[i-1],i=1,...,dim
    A.constants = (int*)malloc(dim * sizeof(int));
    if (!A.constants) {
        exit(OVERFLOW);
    }
    A.constants[dim - 1] = 1;
    // L=1,指针的增减以元素的大小为单位
    for (int i = dim - 2; i >= 0; --i) {
        A.constants[i] = A.bounds[i + 1] * A.constants[i + 1];
    }
    return OK;  // 返回 OK
}

A.bounds是每一维可以放多少元素:a[A.bounds[0]][A.bounds[1]][A.bounds[2]]……
A.constants是指向每一维开始的元素的指针(因为是顺序存放,所以没有在计算机中没有明显的维数的区分,需要自己计算出指向每一维第一个元素的指针)

关于va_list的解释

/**
 * 在数组 A 中定位指定下标的元素,并计算出该元素的相对地址。
 * 
 * @param A     要定位的多维数组
 * @param ap    指示要定位的下标列表的可变参数
 * @param off   返回该元素在数组 A 中的相对地址
 * @return      如果下标合法,返回 OK;否则返回 OVREFLOW
 */
Status Locate(Array A, va_list ap, int& off) {
    // 初始化偏移量为 0
    off = 0;
    // 循环遍历所有维度
    for (int i = 0; i < A.dim; ++i) {
        // 获取当前维度的下标值
        int ind = va_arg(ap, int);  
        // 检查下标值是否超出边界
        if (ind < 0 || ind >= A.bounds[i]) {
            return OVREFLOW;
        }
        // 计算该维度下标对应的偏移量,并累加到总偏移量中
        off += A.constants[i] * ind;
    }
    // 如果下标合法,则返回 OK
    return OK;
}

矩阵的压缩存储

在这里插入图片描述

#define MAXSIZE 12500

typedef union {
    Triple data[MAXSIZE + 1]; // 用于存储稀疏矩阵中的非零元素
    int mu, nu, tu; // 分别表示稀疏矩阵的行数、列数和非零元素个数
} TSMatrix; // 稀疏矩阵类型

有2类稀疏矩阵:

  • 非零元在矩阵中的分布有一定规则
    例如: 三角矩阵, 对角矩阵
  • 随机稀疏矩阵
    非零元在矩阵中随机出现

随机稀疏矩阵的压缩存储方法:

  • 三元组顺序表

这个结构体一般用于表示稀疏矩阵中的非零元素。对于一个 m 行 n 列的稀疏矩阵,如果其非零元素个数为 k,则可以用一个长度为 k 的 Triple 数组来存储这些非零元素。

#define MAXSIZE 12500

typedef struct {
    int i, j; // 该非零元的行下标和列下标
    ElemType e; // 该非零元的值
} Triple; // 三元组类型
  • 行逻辑联接的顺序表
  • 十字链表

求转置矩阵

三元组作转置

Status FastTransposeSMatrix(TSMatrix M, TSMatrix &T){
    T.mu = M.nu;
    T.nu = M.mu;
    T.tu = M.tu;
    if (T.tu) {
        int col, t, p;
        int num[MAXSIZE + 1] = {0}; // 列计数器,用于记录每列非零元素的个数
        int cpot[MAXSIZE + 1] = {0}; // 行指针数组,用于记录每列第一个非零元素在转置矩阵中的位置
        // 统计每列非零元素的个数
        for (col = 1; col <= M.nu; ++col) {
            num[col] = 0;
        }
        for (t = 1; t <= M.tu; ++t) {
            ++num[M.data[t].j];
        }
        // 计算每列第一个非零元素在转置矩阵中的位置
        cpot[1] = 1;
        for (col = 2; col <= M.nu; ++col) {
            cpot[col] = cpot[col - 1] + num[col - 1];
        }
        // 执行转置操作
        for (p = 1; p <= M.tu; ++p) {
            col = M.data[p].j;
            int q = cpot[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;
            ++cpot[col]; // 该列的行指针加1
        }
    }
    return OK;
} // FastTransposeSMatrix

行逻辑连接的顺序表

#define MAXMN 500

typedef struct {
    Triple data[MAXSIZE + 1]; // 非零元三元组表
    int rpos[MAXRC + 1]; // 各行第一个非零元的位置表
    int mu, nu, tu;  // 矩阵的行数、列数和非零元个数            
} RLSMatrix; // 行逻辑链接顺序表类型
ElemType value(RLSMatrix M, int r, int c) {
    int p = M.rpos[r];
    while (M.data[p].i == r && M.data[p].j < c) {
        p++;
    }
    if (M.data[p].i == r && M.data[p].j == c) {
        return M.data[p].e;
    } else {
        return 0;
    }
} // value

矩阵乘法:

// 稀疏矩阵相乘
Status MultSMatrix(RLSMatrix M, RLSMatrix N, RLSMatrix &Q) {
    // 如果两个稀疏矩阵的列数不等,则无法相乘,返回错误状态
    if (M.nu != N.mu) {
        return ERROR;
    }
    // 计算结果矩阵Q的行数,列数以及非零元素个数
    Q.mu = M.mu;
    Q.nu = N.nu;
    Q.tu = 0;
    // 如果M、N之间存在非零元素,则进行矩阵相乘的处理
    if (M.tu * N.tu != 0) {
        // 遍历M的每一行
        for (int arow = 1; arow <= M.mu; ++arow) {
            // M矩阵中第arow行在三元组表中的起始位置
            int mp = M.rpos[arow];
            // 遍历N的每一列
            for (int bcol = 1; bcol <= N.nu; ++bcol) {
                // 初始化N矩阵中第bcol列在三元组表中的起始位置
                int np = N.rpos[bcol];
                // 累加M矩阵第arow行和N矩阵第bcol列的乘积
                ElemType temp = 0;
                while (mp < M.tu && np < N.tu) {
                    // 如果M矩阵和N矩阵中的当前位置元素在同一列,则累加乘积
                    if (M.data[mp].j == N.data[np].i) {
                        temp += M.data[mp].e * N.data[np].e;
                        mp++;
                        np++;
                    } else if (M.data[mp].j < N.data[np].i) {
                        mp++;
                    } else {
                        np++;
                    }
                } // while
                // 如果累加的乘积不为0,则添加到结果矩阵Q中
                if (temp != 0) {
                    Q.tu++;
                    // 将非零元素添加到Q三元组表的末尾
                    Q.data[Q.tu].i = arow;
                    Q.data[Q.tu].j = bcol;
                    Q.data[Q.tu].e = temp;
                }
            } // for bcol
        } // for arow
    } // if
    return OK;
} // MultSMatrix

十字链表

在这里插入图片描述

结构体定义

typedef struct OLNode {
    int i, j;       // 该非零元的行和列下标 
    ElemType e;     // 该非零元的值 
    struct OLNode *right, *down;   // 该非零元所在行表和列表的后继指针 
} OLNode, *OLink;

typedef struct {
    OLink *rhead, *chead;   // 行和列链表头,指向 rhead 与 chead 数组
                            // 指针向量基址由 CreateSMatrix 函数分配
    int mu, nu, tu;         // 稀疏矩阵的行数、列数和非零元个数      
} CrossList;

广义表

在这里插入图片描述
结构特点:

  • 广义表的长度定义为最外层包含元素个数;
  • 广义表的深度定义为所含括弧的重数;
    注意:“原子”的深度为 0 ;“空表”的深度为 1

在这里插入图片描述
表头要去掉一次括号,表尾直接拿并且包含原来的括号

广义表的存储结构

表头、表尾法

在这里插入图片描述
其中,NIL表示空表

子表表示

在这里插入图片描述
在这里插入图片描述
搭配这个例子才比较好理解一些

求深度

int GlistDepth(Glist L) {
    // 返回指针L所指的广义表的深度
    int max = 0;
    Glist pp;
    int dep;
    if(!L) return 1;
    if(L->tag==ATOM) return 0;
    for (pp = L; pp; pp = pp->ptr.tp) {
        dep = GlistDepth(pp->ptr.hp);
        if (dep > max) {
            max = dep;
        }
    }
    return max + 1;
} // GlistDepth

遇到求深度的一些填空题,可能要自己画一下了,不是用眼睛能看出来的

复制广义表

void CopyGList_GL_E(GList* T, GList L) {
    if (L == NULL) { 
        *T = NULL;
        return;
    }
    *T = (GLNode*)malloc(sizeof(GLNode)); 
    (*T)->tag = L->tag; 
    if (L->tag == 0) {
        (*T)->a.atom = L->a.atom;
    } else { 
        CopyGList_GL_E(&((*T)->a.ptr.hp), L->a.ptr.hp); 
        CopyGList_GL_E(&((*T)->a.ptr.tp), L->a.ptr.tp); 
    }
}

习题

5.19 马鞍点

5.19若矩阵 A A A中的某个元素 a是第行中的最小值同时又是第列中的最大值,则称此元素为该矩中的一个马鞍点。假设以二维数组存储矩阵 A m ∗ n A_{m*n} Amn,试编写求出矩阵中所有马鞍点的算法,并分析你的算法在最坏情况下的时间复杂度

void saddle(int a[m][n]) {
    int flag = 0, min, col;
    for (int i = 0; i < m; ++i) {
        min = a[i][0];
        for (int j = 0; j < n; ++j) {
            if (a[i][j] < min) {
                min = a[i][j];
                col = j;
            }
        }
        int flag1 = 1;
        for (int k = 0; k < m; ++k) {
            if (min < a[k][col]){
                flag1 = 0;
                break;
            }
        }
        if (flag1) {
            printf("%d行%d列是马鞍点,值为%d\n", i, col, min);
            flag = 1;
        }

    }
    if (!flag) {
        printf("无马鞍点\n");
    }
}

时间复杂度: O ( m 2 + m ∗ n ) O(m^2+m*n) O(m2+mn)

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/616107.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Go语言学习-创建Go模块(1)

这是介绍Go语言基础特性的第一部分教程。如果你是开开开始学习Go&#xff0c;请确保你看了关于介绍Go语言语法&#xff0c;Go模块的简洁代码文章&#xff1a;启动Go 在本教程中&#xff0c;你将会创建两个模块。第一个模块是个库来被用作其他库或者应用程序来引用。第二个模块是…

msvcp140.dll重新安装的三个解决方法,解决找不到msvcp140.dll问题

msvcp140.dll是Microsoft Visual C Redistributable for Visual Studio 2015的一个重要组件&#xff0c;它是一个动态链接库文件&#xff08;DLL&#xff09;&#xff0c;包含许多用于开发和执行C程序的函数。如果它不在您的计算机上或不正确&#xff0c;您将收到一个错误消息&…

linux 部署mysql

本文介绍下Centos7中mysql的安装(Centos7以下版本中有些命令和centos7中有些不同&#xff0c;安时需注意下自己的linux版本) 事先准备 1、查看系统中是否自带安装mysql yum list installed | grep mysql ![在这里插入图片描述](https://img-blog.csdnimg.cn/e322b2f4036c4d9…

不愧是华为出来的,太强了。。。

前言 实习去了博彦科技&#xff08;外包&#xff09;&#xff0c;做的就是螺丝钉的活&#xff0c;后面还因为人效不佳&#xff0c;被开了。 正式毕业后去了另外一个做电子发票的公司&#xff0c;但是都是功能测试和一点点APP测试&#xff0c;然后经常被开发怼&#xff0c;测试…

【Linux】13. 文件操作

1. 重新认识文件 经过之前的linux命令操作、进程相关概念的学习&#xff0c;我们对于文件也并不陌生 首先需要明确以下概念&#xff1a; 即使是空文件&#xff0c;也要在磁盘当中占据空间文件 文件内容 文件属性文件操作 对文件内容的操作 或者 对文件属性的操作 或者 二者…

软件测试人到30岁+,还有出路吗???

最近一个学生也可以说是朋友&#xff0c;他遇到了一个让他困扰的职场难题&#xff0c;背景如下&#xff1a; 1&#xff09;他们公司准备搞安全测试了&#xff0c;现在有人员培训的计划&#xff0c;所以全组有学习安全测试课程的安排。 2&#xff09;他自己目前专职性能测试1年了…

基于SSM的物流仓库管理系统

摘 要 随着社会的发展&#xff0c;社会的方方面面都在利用信息化时代的优势。互联网的优势和普及使得各种系统的开发成为必需。 本文以实际运用为开发背景&#xff0c;运用软件工程原理和开发方法&#xff0c;它主要是采用java语言技术和mysql数据库来完成对系统的设计。整个…

“专精特新”发展概况

专精特新概念自2011年开始萌芽&#xff0c;2021年上升为国家战略&#xff0c;2022年写进二十大报告&#xff0c;顶层设计持续推进&#xff0c;旨在聚焦产业链关键环节&#xff0c;加强技术创新&#xff0c;破解核心技术“卡脖子”问题。全国各地政府从企业调研、整体设计、培育…

AI崛起:哪些职位将首当其冲?

随着人工智能的迅猛发展&#xff0c;人们普遍担心自己的工作是否会被AI取代。虽然是否能毁灭人类尚难以确定&#xff0c;但可以肯定的是&#xff0c;AI绝对会抢走某些职位。以下是十个职业&#xff0c;它们很可能是被AI取代的首批。 制造业&#xff1a;随着自动化机器人的进步…

docker-compose 搭建 zipkin 服务端

目录 基于docker-compose搭建服务端 数据库 服务器 docker-compose.yaml 问题 测试 基于docker-compose搭建服务端 数据库 我这边存储选择了Mysql存储&#xff0c;新建了 zipkin库&#xff0c;数据库脚本如下 -- -- Copyright 2015-2019 The OpenZipkin Authors -- -- Li…

Java单元测试学习(二)

Java单元测试学习&#xff08;二&#xff09; 使用测试框架JUnitMockito和单元测试覆盖率框架JaCoCo 目录结构 依赖—很好&#xff0c;这里又有个小插曲 打开页面查看覆盖率时一直显示0/0---->最后的解决方式是①添加了maven-surefire-plugin插件 <?xml version&quo…

C++中流的分类

前言 关于流本质的问题&#xff0c;其实从我刚开始学习C的时候&#xff0c;就已经存在了。当时找了不少的资料&#xff0c;不过一直处于那种知其然而不知其所以然的状态&#xff0c;关于流的本质问题我还是一直没有搞通&#xff0c;始终就是懵懵懂懂的。 不过在今天&#xff0…

智能电能表采集失败的原因和解决方法

智能电能表采集失败的原因和解决方法 智能电能表作为现代电力系统中的重要组成部分&#xff0c;在电能计量、电费结算等方面发挥着关键作用。然而&#xff0c;在实际应用过程中&#xff0c;有时会出现电能表采集失败的情况&#xff0c;这可能源于网络连接故障、数据传输错误等…

数据结构基础-堆

堆实现 计算机科学中&#xff0c;堆是一种基于树的数据结构&#xff0c;通常用完全二叉树实现。堆的特性如下 在大顶堆中&#xff0c;任意节点 C 与它的父节点 P 符合 P.value \geq C.value而小顶堆中&#xff0c;任意节点 C 与它的父节点 P 符合 P.value \leq C.value最顶层…

维护嵌入式 Linux 内核——So Easy

导读Pengutronix 内核黑客 Jan Lbbe 总结了嵌入式 Linux 中正在不断增长的安全威胁&#xff0c;并在这次欧洲嵌入式 Linux 会议上概述了一个计划&#xff0c;以保持长期设备的安全和功能完整。 安全漏洞只发生在 Windows 上的好日子正在快速过去。恶意软件黑客和拒绝服务老手们…

【InsCode AI 创作助手】关于编程人员的未来发展趋势,看看AI们怎么说

一、你平时会使用这类AI工具吗&#xff1f;你对这类型的工具有什么看法&#xff1f; 1&#xff09;会经常使用AI工具吗&#xff1f; 是的&#xff0c;我在生活和工作中经常会使用AI工具&#xff0c;尤其是chatGPT&#xff08;3.5&#xff09;和文心一言&#xff0c;关于midjour…

【微信小程序开发】第 5 节 - 小程序代码的构成

欢迎来到博主 Apeiron 的博客&#xff0c;祝您旅程愉快 &#xff01; 时止则止&#xff0c;时行则行。动静不失其时&#xff0c;其道光明。 目录 1、缘起 2、了解项目的基本组成结构 3、小程序页面的组成部分 4、JSON 配置文件 4.1、json 配置文件的作用 4.2、app.json 文…

http首部(下)

开始头大&#xff0c;哈哈&#xff0c;这个东西真的很无聊且枯燥&#xff0c;奈何最近的学习中经常用到这些知识&#xff0c;还是过一遍比较放心。上一篇博客中我们讨论了http报文首部&#xff0c;其划分为请求头和响应头。请求头主要由请求行、请求字段、通用字段、实体字段组…

ChatGPT请不要和打工人争辩今天星期几

目录 1 今天星期几2 聊聊ChatGPT与工具的结合 1 今天星期几 周五了&#xff0c;一个星期快结束了&#xff0c;闲来问问chatgpt (gpt-3.5) 今天 ( 2023.06.03星期五&#xff09;星期几&#x1f601;&#xff0c;chatgpt给出的回答如下&#xff1a; 今天是2023年6月2号没错&…

Linux系统下安装配置 Nginx 详细教程介绍

Linux系统下安装配置 Nginx 详细教程介绍 一、下载 Nginx 安装包 打开Nginx官网 &#xff1a;nginx: download 然后我们找到一个版本&#xff0c;把鼠标移动到上面&#xff0c;右键 - 复制链接地址 我们使用 wget 命令把Nginx安装包下载到/usr/local/目录中 安装wget yum…