西南交通大学【数据结构实验5】

实验内容及要求:

从字符文件读入3元组(i, j, e)来建立两个矩阵A和B的十字链表存储结构(矩阵的行、列数m, n以及非零元素的数目需首先读入)。计算C=A+B,其中矩阵C也采用十字链表存储结构。计算结束后,以三元组形式输出矩阵C至另一个字符文件。矩阵C的十字链表存储空间可以独立于A和B,也可以就用A矩阵的十字链表来存储(则A矩阵的非零元素结点数目可能增加、减少或不变)。

要求实验报告必须写出:输入文件数据格式;输出文件数据格式;加法的实现算法;各子函数的详细说明等。每个同学至少准备一个测试用例(A, B以及期望的C)。每个同学除自己的测试用例外,必须随机选择另外两名同学,用他们的测试用例测试自己的程序。在实验报告中需注明3个测试用例分别来自哪位同学(给出姓名和学号,自己准备的测试用例也需写出姓名和学号)。

实验目的:掌握稀疏矩阵的十字链表存储结构及其基本操作。

数据结构设计简要描述:

采用十字链表存储稀疏矩阵

typedef int elem;  // 将int作为elem

// 定义十字链表节点

typedef struct node_ {

    int i;  // 行

    int j;  // 列

    elem e;  // 非0元素个数、元素值

    struct node_* right;  // 向列延伸

    struct node_* down;  // 向行延伸

}node, * crossLink;

    

算法设计简要描述:

先对字符文件AB.txt逐字符读入,创建A和B,在计算相加的时候,结合有序顺序表的合并的思想,进一步优化时间复杂度,使得只需遍历一遍A和B就可以完成相加操作。在相加时选择在A中插入新节点而不是将B中已有的节点给A,主要考虑最后释放的时候会涉及到空间重复释放的原因。

输入/输出设计简要描述:

输入:AB.txt文件,A在上,B在下,每一部分的第一行都是矩阵的行列数和非0元素的数目,接着才是各非0元素。A和B之间用一个空格隔开。

输出:C.txt文件,第一行是矩阵的行列数和非0元素数目,剩下是具体非0元素

编程语言说明:

使用Visual Studio Code编程。 主要代码采用C语言实现 ;动态存储分配采用C++的new和delete操作符实现;输入与输出采用C++的文件流对象和cout流;程序注释采用C/C++规范。    

主要函数说明:

// 初始化十字链表

void Init(crossLink& L, int m, int n, int e);

// 十字链表中插入节点

void Insert(crossLink& L, int i, int j, elem e);

// 十字链表中删除节点

void Delete(crossLink& L, int i, int j);

// 创建有数据的十字链表

void Create(crossLink& A, crossLink& B);

// 计算相加

void Add(crossLink A, crossLink B);

// 将结果输出到文件

void toFile(crossLink A);

// 销毁十字链表

void Relese(crossLink& L);

程序测试简要报告:

  1.  测试实例1    

程序输入

程序输出

结论

程序输出结果与期望输出结果相符。

测试实例2   

程序输入

程序输出

结论

程序输出结果与期望输出结果相符。

测试实例3  

程序输入

程序输出

结论

程序输出结果与期望输出结果相符。

源代码:

#include <iostream>
#include <fstream>
using namespace std;
typedef int elem;  // 将int作为elem
// 定义十字链表节点
typedef struct node_ {
    int i;  // 行
    int j;  // 列
    elem e;  // 非0元素个数、元素值
    struct node_* right;  // 向列延伸
    struct node_* down;  // 向行延伸
}node, * crossLink;
// 初始化十字链表
void Init(crossLink& L, int m, int n, int e) {
    node* temp = new node;
    L = temp;
    L->i = m;  // 稀疏矩阵的行数
    L->j = n;  // 稀疏矩阵的列数
    L->e = e;  // 稀疏矩阵的非0元素
    // 以下建行头节点数组
    node* row = new node[m];
    if (!row) {
        cout << "内存申请失败!!!" << endl;
        exit(1);
    }
    for (int i = 0; i < m; ++i) {
        row[i].i = i + 1;
        row[i].right = &row[i];
    }
    L->down = row;
    // 以下建列头节点数组
    node* col = new node[n];
    if (!col) {
        cout << "内存申请失败!!!" << endl;
        exit(1);
    }
    for (int j = 0; j < n; ++j) {
        col[j].j = j + 1;
        col[j].down = &col[j];
    }
    L->right = col;
}
// 十字链表中插入节点
void Insert(crossLink& L, int i, int j, elem e) {
    if (i < 1 || i > L->i || j < 1 || j > L->j) {
        cout << "行号或列号超出范围!!!" << endl;
        return;
    }
    // 建新元素节点
    node* n = new node;
    n->i = i;
    n->j = j;
    n->e = e;
    // 以下插入新节点到第i行循环链表
    // 插入后使第i行循环链表各节点按列下标升序连接
    node* pr = &L->down[i - 1];
    node* p = pr->right;
    while (p != &L->down[i - 1] && j > p->j)
    {
        pr = p;
        p = p->right;
    }
    pr->right = n;
    n->right = p;
    // 以下插入新节点到第j列循环链表
    // 插入后使第j列循环链表各接点按行下标升序连接
    pr = &L->right[j - 1];
    p = pr->down;
    while (p != &L->right[j - 1] && i > p->i)
    {
        pr = p;
        p = p->down;
    }
    pr->down = n;
    n->down = p;
}
// 十字链表中删除节点
void Delete(crossLink& L, int i, int j) {
    if (i < 1 || i > L->i || j < 1 || j > L->j) {
        cout << "行号或列号超出范围!!!" << endl;
        return;
    }
    // 以下在第i行链表中查找(i, j)
    node* pr1 = &L->down[i - 1];
    node* pr2 = pr1->right;
    while (pr2 != &L->down[i - 1] && i != pr2->i)
    {
        pr1 = pr2;
        pr2 = pr2->right;
    }
    // 查找失败
    if (pr2 == &L->down[i - 1]) {
        cout << "不存在此节点!!!" << endl;
        return;
    }
    // 以下在第j列链表中查找(i, j)
    node* pc1 = &L->right[j - 1];
    node* pc2 = pc1->down;
    while (pc2 != &L->right[j - 1] && j != pc2->j)
    {
        pc1 = pc2;
        pc2 = pc2->down;
    }
    // 查找失败
    if (pc2 == &L->right[j - 1]) {
        cout << "不存在此节点!!!" << endl;
        return;
    }
    // 行、列查找结果不一致
    if (pc2 != pr2) {
        cout << "发生错误!!!" << endl;
        return;
    }
    // 以下删除元素节点(i, j)
    pr1->right = pr2->right;
    pc1->down = pc2->down;
    delete pr2;
}
// 创建有数据的十字链表
void Create(crossLink& A, crossLink& B) {
    // 实例化文件对象
    ifstream ifs;
    ifs.open("AB.txt", ios::in);
    char c;
    // num:每行第几个字符  flag:标识变量  i j e:行 列 值
    int num = 1, flag = 1, i, j, e;
    // 以下读取A稀疏矩阵
    c = ifs.get();
    while (c != '\n' || num != 1)
    {
        if (num == 1) {
            if (c == '-') {
                cout << "稀疏矩阵文件错误!!!行或列不能为负!!!" << endl;
                return;
            }
            i = c - '0';
            ++num;
        }
        else if (num == 3) {
            if (c == '-') {
                cout << "稀疏矩阵文件错误!!!行或列不能为负!!!" << endl;
                return;
            }
            j = c - '0';
            ++num;
        }
        else if (num == 5) {
            // 当数值为负时
            if (c == '-') {
                c = ifs.get();
                e = -(c - '0');
            }
            else {
                e = c - '0';
            }
            ++num;
        }
        else if (num == 6) {
            if (flag) {
                Init(A, i, j, e);
                flag = 0;
            }
            else {
                Insert(A, i, j, e);
            }
            num = 1;
        }
        else {
            ++num;
        }
        c = ifs.get();
    }
    // 以下读取B稀疏矩阵
    flag = 1;
    c = ifs.get();
    while (c != '\n' || num != 1)
    {
        // 当读到文件末尾
        if (c == EOF) {
            break;
        }
        if (num == 1) {
            if (c == '-') {
                cout << "稀疏矩阵文件错误!!!行或列不能为负!!!" << endl;
                return;
            }
            i = c - '0';
            ++num;
        }
        else if (num == 3) {
            if (c == '-') {
                cout << "稀疏矩阵文件错误!!!行或列不能为负!!!" << endl;
                return;
            }
            j = c - '0';
            ++num;
        }
        else if (num == 5) {
            if (c == '-') {
                c = ifs.get();
                e = -(c - '0');
            }
            else {
                e = c - '0';
            }
            ++num;
        }
        else if (num == 6) {
            if (flag) {
                Init(B, i, j, e);
                flag = 0;
            }
            else {
                Insert(B, i, j, e);
            }
            num = 1;
        }
        else {
            ++num;
        }
        c = ifs.get();
    }
    ifs.close();
}
// 计算相加
void Add(crossLink A, crossLink B) {
    // 如果A和B的行或列不一样
    if (A->i != B->i || A->j != B->j) {
        cout << "A和B不满足相加条件!!!" << endl;
        return;
    }
    // 最后非0元素个数的变化
    int differ = 0;
    // 对A的行头节点数组遍历 将最后的结果存储在A
    // 因行的列下标是有序的 故借用有序顺序表合并的思想
    for (int i = 1; i <= A->i; ++i) {
        node* A_row = &A->down[i - 1];
        node* B_row = &B->down[i - 1];
        node* pA = A_row->right;
        node* pB = B_row->right;
        while (pA != A_row && pB != B_row)
        {
            // A和B都有(i, j)元素
            if (pA->j == pB->j) {
                pA->e += pB->e;
                // 如果相加为0 删除此节点
                if (pA->e == 0) {
                    differ -= 1;
                    node* temp = pA->right;
                    Delete(A, pA->i, pA->j);
                    pA = temp;
                }
                else {
                    pA = pA->right;
                }
                pB = pB->right;
            }
            else if (pA->j < pB->j) {  // A元素的列小于B元素的列
                pA = pA->right;
            }
            else {  // A元素的列大于B元素的列
                Insert(A, pB->i, pB->j, pB->e);  // 这里选择插入新节点 而不是将B中的节点放入A
                differ += 1;
                pB = pB->right;
            }
        }
        while (pB != B_row)
        {
            Insert(A, pB->i, pB->j, pB->e);
            differ += 1;
            pB = pB->right;
        }
    }
    A->e += differ;
}
// 将结果输出到文件
void toFile(crossLink A) {
    // 实例化文件对象
    ofstream ofs;
    ofs.open("C.txt", ios::out);
    // 将结果矩阵的行 列 非0元素个数先输出到文件中
    ofs << A->i << " " << A->j << " " << A->e << endl;
    // 遍历行 将每一个节点输出到文件
    for (int i = 1; i <= A->i; ++i) {
        node* p = &A->down[i - 1];
        p = p->right;
        while (p != &A->down[i - 1])
        {
            ofs << p->i << " " << p->j << " " << p->e << endl;
            p = p->right;
        }
    }
    ofs.close();
}
// 销毁十字链表
void Relese(crossLink& L) {
    // 先释放每个节点
    for (int i = 1; i <= L->i; ++i) {
        node* pr1 = &L->down[i - 1];
        pr1 = pr1->right;
        if (pr1 == &L->down[i - 1]) {
            continue;
        }
        else {
            node* pr2 = pr1->right;
            while (pr1 != &L->down[i - 1])
            {
                delete pr1;
                pr1 = pr2;
                pr2 = pr2->right;
            }
        }
    }
    // 释放头结点数组和列节点数组
    delete[] L->down;
    delete[] L->right;
    delete L;
}
int main(void) {
    // 定义A和B
    crossLink A, B;
    Create(A, B);
    Add(A, B);
    toFile(A);
    Relese(A);
    Relese(B);
    cout << "计算成功!!!" << endl;
    return 0;
}

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

还有糕手

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值