蛇形矩阵,有序插入链表,类实现-----为进入河大iOS实验室,一个平平无奇小菜鸡的努力

一.蛇形矩阵的实现

控制台输入 行数 和 列数

输出蛇形矩阵

     其实我觉得最合适的形容是回型矩阵,将数字的运动规律分开来看:从左到右,从上到下,从右到左,从下到上。外面一圈结束以后开始向内逐层逼近中心,直至最后一个数(即行数与列数的乘积)输出。因此,我们可以定义代表从左到右输出的数字的整体变化趋势为top(top++),从上到下输出的数字的整体变化趋势为right(right--),从右到左的数字的变化趋势为bottom(bottom--),从下到上的数字的变化趋势为(left++)。

    我们将top和left初始化为0,将right初始化为列数-1,将bottom初始化为行数-1.

    矩阵中的数字在数组中按顺序输出,在一个整体的大循环中分块,从左到右,从上到下,从右到左,从下到上,依次进行数字的输出以实现蛇形代码。要注意的是,在外层大循环中,进入循环的条件是left<=right&&top<=bottom;我们按照顺时针的顺序,依次填充数字(从左到右、从上到下、从右到左、从下到上)。每填充完一条边,就更新相应的起始和结束位置。当起始位置大于结束位置时,表示矩阵已经填充完成

#include <iostream>

using namespace std;

int main()
{
    int rows;
    cout << "请输入行数:"<<endl;
    cin >> rows;
    int cols;
    cout << "请输入列数:" <<endl;
    cin >> cols;
    int top=0;
    int bottom=rows-1;
    int left=0;
    int right=cols-1;
    int num=1;
    int a[100][100];
    while(left<=right && top<=bottom)
    {
        for(int i=left;i<=right;i++)//从左到右的数字运动(填充)
        {
            a[top][i]=num++;

        }
        top++;
        for(int i=top;i<=bottom;i++)//从上到下
        {
            a[i][right]=num++;

        }
        right--;
        if(top<=bottom)
        {
            for(int i=right; i>=left; i--)//从右到左
            {
                a[bottom][i]=num++;

            }
            bottom--;
        }
        if(left<=right)
        {
            for(int i=bottom;i>=top;i--)//从下到上
            {
                a[i][left]=num++;

            }
            left++;
        }

    }
    for(int i=0;i<rows;i++)
    {
        for(int j=0;j<cols;j++)
        {
            cout << a[i][j] << "\t" ;

        }
        cout << endl;
    }

    return 0;
}

其中top++;right--;bottom--;left++;的原因是在数字的填充过程中,第一个大的循环完成后,即外层第一圈以及填充完全后,数字就要向里层填充,此时从左到右的数字填充过程中,所对应的数组的行数增加;此时从上到下的数字填充过程中,所对应的数组的列数减少;此时从右到左的数字填充过程中,所对应的数组的行数减少;此时从下到上的数字填充过程中,所对应的数组的列数增加;(这些仅仅是我自己的理解)大家可以结合代码理解。

二.有序单循环链表的插入

首先,我们先要了解一下链表,知道链表的定义:

当处理数据时,链表(Linked List)是一种常用的数据结构。它由一系列节点(Node)组成,每个节点都包含一个数据元素和一个指向下一个节点的指针。
与数组不同,链表的节点在内存中可以不连续存储,它们通过指针连接在一起。这意味着在链表中插入或删除节点的操作比数组更高效,因为不需要移动其他节点。
链表可以是单向链表(单链表)或双向链表(双链表)。在单链表中,每个节点都有一个指向下一个节点的指针。在双链表中,每个节点有一个指向前一个节点的指针和一个指向后一个节点的指针。
链表的一个重要特点是它可以动态地增长和收缩,因为它不需要预先分配内存空间。当需要插入一个新节点时,可以简单地分配新的节点,并将其连接到链表中。当不再需要一个节点时,可以将其从链表中移除并释放其内存。
链表的常见操作包括:

1.在链表的开头插入一个节点:将节点的指针指向链表的头节点,并更新链表的头指针。
2.在链表的末尾插入一个节点:遍历链表,找到尾节点,并将新节点连接到尾节点之后。
3.在链表中间插入一个节点:首先找到要插入位置的前一个节点,然后将新节点连接到前一个节点和后一个节点之间。
4.删除链表中的一个节点:首先找到要删除节点的前一个节点,然后将前一个节点和后一个节点连接起来,删除目标节点。
5.遍历链表:从头节点开始,通过指针依次访问每个节点,直到到达链表的末尾。

(这一段来自AI的解释,我无法解释的这么详尽,我通过AI和课本来理解链表)

链表的各种操作通常在类中实现,类定义一个指针和一个成员变量。

下面是三种对链表中节点的插入操作(这个题目涉及了插入)(包含我自己的理解)

1.在开头插入一个节点

//代码实现如下
void insertNode1(int data)//将这个输入的数据插入链表,就是让它成为这个链表的head
{
    Node* newNode=new Node;//动态分配一个新的Node对象,将其地址储存在指针变量newNode中
    newNode->data=data;//将输入的数据储存到节点中
    newNode->next=head;//新节点的next成员变量变成了head,
相当于链表头部节点的更新,将新节点连接在原来链表的头部节点之前
    head=newNode;//将头部指针head更新为新节点newNode的地址,新节点就成为了链表的头部节点。
}

2.在末尾插入一个节点

void insertNode2 (int data)
{
    Node* newNode = new Node;//动态分配内存空间
        newNode->data = data;//将输入的数据储存到节点中
        newNode->next = nullptr;//因为是在链表的末端插入,所以将新节点的next设置为空

        if (head == nullptr)//链表为空的情况 
        {
            head = newNode;//该新节点就是链表的唯一节点
        }
        else //链表不为空的情况
        {
            Node* temp = head;//temp:用来遍历链表
            while (temp->next != nullptr)//这个循环直至找到其next为空的节点,
///就是链表的最后一个节点
            {
                temp = temp->next;//
            }
            temp->next = newNode;//将链表最后一个节点的next指针指向新节点的地址,
//实现了末端节点的更新,新的节点成功插入了末端,成为最后一个节点。
        }

3.在指定位置插入链表

void insertNode3(int data, int position) 
{
    Node* newNode = new Node;//分配内存空间
    newNode->data = data;
    newNode->next = nullptr;
        
    if (position == 1)//我觉得这个跟前面第一个插入头结点是一样滴
    {
        newNode->next = head;
        head = newNode;
    }
    else 
    {
        Node* temp = head;//利用temp遍历链表,判断position是否为有效的参数
        for (int i = 1; i < position - 1 && temp != nullptr; i++)
        {
            temp = temp->next;
        }
        if (temp == nullptr) 
        {
            cout << "Invalid position." << endl;//输入的位置有误(无效),无法插入节点
            return;//直接从该函数返回,终止插入操作
        }
        newNode->next = temp->next;//先将新节点和下一个节点建立联系
        temp->next = newNode;//再和前一个节点建立联系
        }
    }

依据对上面三种不同插入类型的理解,我完成了第二道题目。

在节点值有序的单循环链表中插入指定结点,使插入后的链表依然有序。

大致效果:

这一题 ,不仅要插入,还要进行排序

对此,我给出的代码是

#include <iostream>

using namespace std;
struct Node//声明一个名为Node的结构体
{
    int data;
    struct Node *pnext;
};
int main()
{
    cout << "***********Data Structure************" <<endl;
    cout << "1----------有序插入单列表"<< endl;
    cout << "2----------查看单列表" << endl;
    cout << "0----------停止插入" << endl;
    struct Node *head=new Node;//创建一个head指针,指向一个新的Node结构体对象
    head->pnext=NULL;//新的链表为空,或者这个链表只有头节点
    int choice;
    cin >> choice;
    while (choice!=0)//根据题目来设置循环
    {

        if (choice==1)//有序插入链表
        {
            Node *p=head;//将指针p初始化为链表的头结点head
            while (true)//一个可以连续插入节点的循环
            {
                cout << "请输入要插入的值:"<<endl;
                Node *p1 =new Node;//分配内存空间
                cin >> p1->data;

                if(p1->data!=0)
                {
                    while(p->pnext!=NULL&&p->pnext->data < p1->data)//排序data由低到高
                    {//找到合适的插入位置
                        p=p->pnext;//指针p一直向后移动,直至找到合适的插入位置
                    }
                    //下面操作的代码与上面第三个节点插入类型的代码一样
                    p1->pnext=p->pnext;
                    p->pnext=p1;
                    p=head;//将指针p重新指向head以便下一次的节点插入操作
                }
                else//停止插入
                {
                    delete p1;//释放p1所占用的内存空间

                    break;
                }
            }
        }

        else if (choice==2)//查看链表
        {
            Node *p=head->pnext;
            for(; p!=NULL; p=p->pnext)//遍历链表
            {

                cout << p->data << " " ;//输出排序后的链表
            }


        }
        cout << "请输入您的选择:"<< endl;
        cin >> choice;
    }
    Node*p=head;//初始化指针p,从链表头部开始遍历
    while(p!=NULL)
    {
        Node*temp=p->pnext;//定义指针temp用来存放p-pnext的地址,在当前指针被删除后
                           //还能够继续遍历链表
        delete p;//释放当前节点所占用的内存空间
        p=temp;//继续遍历,保证每个节点的内存都被正确的释放

    }

    return 0;
}

三.类设计题目

1.基类Account

数据成员:

balance: double类型,表示账户余额;

构造函数:

构造函数接受一个初始余额参数,初始化数据成员balance,并能确认初始余额的有效性,保证它大于等于0,如果小于0,则将balance置为0,并显示出错信息,表示该初始化余额是一个无效的值;

成员函数:

credit:可以向当前余额加钱,

debit:负责从账户中取钱,并保证账户不会不会透支,如果提取金额大于账户余额,函数将保持balance不变,并打印信息“Debit amount exceeded account balance.”

getBalance:返回当前balance的值;

2.派生类SavingsAccount:

继承基类Account,并提供一个附加的double类型的数据成员rate表示这个账户的年利率和一个附加的int类型表示存款年份。

构造函数:

接收初始化余额和初始利率值

成员函数:

calculate:返回代表账户经过这些存款年后账户的总余额;

3.main.cpp函数中进行对类的测试,

  • 要求一个账户能够创建账户(可以写在mian函数里),并对这个账户进行存取款操作(switch菜单操作)
  • 可以输入存款年份和年利率(switch菜单操作),求这些年后可以得到的总余额

既然这是一个类实现题目,我们就要了解C++中的class类。它是一种用户自定义的数据类型,

将数据成员和成员函数组合在一起,以便创建具有特定特性和行为的对象。

首先要声明类的名称,类的私有成员变量,以及构造函数,析构函数,set函数,get函数,打印函数(用于输出内容),工具函数(实现类对象的各种操作)。此外,类还有封装功能,就是将数据和对数据的操作封装在一起,组成一种新的数据类型。其中,构造函数:创建类对象时自动调用,完成类对象的初始化的工作;析构函数:在程序结束对象销毁前自动调用,在系统回收对象前进行扫尾工作,他本身并不释放对象占用的内存空间;set函数:便于类的客户设置私有成员,我认为是对私有成员进行赋值工作;get函数:主要便于类的客户访问私有成员;打印函数:可以输出私有成员变量;工具函数:可以实现对私有成员进行各种操作。

这个问题要求分函数编写代码,我的代码如下

首先是main.cpp

#include <iostream>
#include "Account.h"//一定要带上头文件
#include "SavingAccount.h"
using namespace std;

int main()
{
    Account account;
    double ba;
    cout << "请输入账户余额:" << endl;
    cin >> ba;
    account.setAccount(ba);//利用set函数将cin的ba值赋给Account类中的私有成员balance

    int a = 1;
    while (true)
    {
        cout << "**********1---向账户里加钱*********" << endl;
        cout << "**********2---从账户中取钱*********" << endl;
        cout << "**********3---查看存款总余额*******" << endl;
        cout << "**********0---退出!***************" << endl;
        int choice;
        cin >> choice;
        switch (choice)
        {
            case 1:
            {
                double credit;
                cout << "请输入金额:";
                cin >> credit;
                account.creditAmount(credit);//引用 Account类中的工具函数
                break;
            }
            case 2:
            {
                double debit;
                cout << "请输入金额:";
                cin >> debit;
                account.debitAmount(debit);//引用 Account类中的工具函数
                break;
            }
            case 3:
            {
                double rate;
                cout << "请输入年利率:" << endl;
                cin >> rate;
                int year;
                cout << "请输入存款年数:" << endl;
                cin >> year;
                //引用 SavingAccount类中的构造函数,不能在循环外面对其进行初始化
                //否则输出的totalBlance一直为0
                SavingAccount savingaccount(account.getbalance(), year);
                //引用 SavingAccount类中的工具函数
                double totalBlance = savingaccount.calculate(year, rate);
                cout << "总余额:" << totalBlance << endl;
                break;
            }
            case 0:
                a = 0;
                cout << "退出账户系统,谢谢使用!" << endl;
                break;
            default:
                cout << "输入错误,请重新输入!" << endl;
                break;
        }
        if (a == 0)
            break;
    }
    return 0;
}

然后是Account.h

#include <iostream>

using namespace std;

#ifndef ACCOUNT_H
#define ACCOUNT_H
class Account//声明类的名称
{
public:
    Account()//无参构造函数
    {
        balance =0;
    }
    Account(double ba)//构造函数,可自动调用
    {
        if(ba<0)
        {
            balance=0;
            cout << "Error!"<< endl;
        }
        else
            balance=ba;

    }
    void setAccount(double ba)//对私有成员balance进行赋值
    {
        balance=ba;
    }
    double getbalance() const//便于在main.cpp中访问私有成员balance
    {
        return balance;
    }

    void creditAmount(double credit)//向余额中存钱的工具函数
    {
        balance=balance+credit;
        cout << "新的余额为:"<< balance << endl;
    }
    void debitAmount(double debit)//从余额中取钱的工具函数
    {
        if(debit>balance)
        {
            cout << "Debit amount exceeded account balance!" << endl;
        }
        else
        {
            balance=balance-debit;
            cout << "新的余额为:"<< balance << endl;
        }
    }

private:
    double balance;//私有成员
};





#endif // ACCOUNT_H

 最后是SavingAccount.h

#ifndef SAVINGACCOUNT_H
#define SAVINGACCOUNT_H
#include "Account.h"//在这里用到了Account类中的私有成员变量balance
#include <iostream>

using namespace std;

class SavingAccount
{
public:
    SavingAccount(double ba,int y)//构造函数,对私有成员进行初始化
    {
        account=Account (ba);
        year=y;
        rate=0.0;
    }

    double calculate (int y,double ra)//工具函数,计算在银行存储的总金额
    {
        double tempba=account.getbalance();//访问Account类中的私有成员balance
        year=y;
        rate=ra;
        for (int i=0;i<year;i++)
        {
            tempba+=tempba*ra;
        }
        return tempba;
    }
private:
    double rate;
    int year;
    Account account;
};
#endif // SAVINGACCOUNT_H

在我完成第三个作业的过程中遇到了一个问题,无论输入的rate和year是大是小,最终输出的totalBalance都是类似1.756e-123的浮点数,最终发现是在SavingAccount.h中,缺少了对Account account 的初始化,在构造函数中少了account=Account (ba),同时在主函数中也缺少了该构造函数的引用,导致Account类中的balance的值无法引用到SavingAccount类的calculate函数中,得出来的结果也不正确。后来,我在SavingAccount类中正确完成了初始化,却在主函数中错误的引用了SavingAccount类的构造函数,我将其都初始化为0,导致输出的totalBalance一直为0,不过最后还是成功改正了错误。

四.完成这三项作业我最大的收获

我从来就没想过,我会把这么多的时间都放在C++上。通过这次作业,我对链表有了更加深刻的理解,能够更灵活的运用数组解决问题,而且对于类的实现有了自己的感悟。会独立思考问题和错误,并逐步改正,除此之外我的思维更加敏捷了,我会继续保持这种学习的激情,坚持独立思考,认真完成。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值