IOS实验室第一次招新题目学习收获(均使用C++进行实现)

本文详细介绍了蛇形矩阵的定义、输入输出方法,以及有序单循环链表的构建、插入操作、释放内存等,同时探讨了delete和free的区别以及nullptr与NULL在C++中的使用。
摘要由CSDN通过智能技术生成

目录

一、关于蛇形矩阵

1.定义:

2.输入:

3.输出与释放:

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

1.链表结构的构建:

2.输入:

 3.输出:

 4.释放:

5.目录:

 6.主函数:

*一些学到的新知识:

1.delete和free的区别:

2. nullptr和NULL的区别:

 三、类设计题目

1.基类和继承类

2. 基类的建设:

3.派生类的建设:

四、结语


一、关于蛇形矩阵

1.定义:

矩阵的输入输出一般使用二维数组,由于其列数和行数由控制台输入,所以要定义的是一个动态二维数组,如下所示:

int rows , cols;
cin >> rows >> cols;

//定义:
int **arr = new int *[rows];
for(int i = 0 ; i < rows ; i++)
{
    arr[i] = new int [cols];
}

2.输入:

有了定义,下一步就是要考虑如何输入,通过观察蛇形矩阵的特点,考虑边界条件和方向切换的情况,可以通过以下的方式进行输入:

int rowStart = 0 , rowEnd = rows - 1;
int colStart = 0 , colEnd = cols - 1;
int num = 1;

while (rowStart <= rowEnd && colStart <= colEnd)
{
    //行(从左到右输入):从 colStart 列到 colEnd 列
    for (int i = colStart; i <= colEnd; i++)
    {
        arr[rowStart][i] = num;
        num = num + 1;
    }
    rowStart = rowStart + 1;//(输入完一行,使得开始行数加一)

    //列(从上往下输入):从 rowStart 行到 rowEnd 列
    for (int i = rowStart; i <= rowEnd; i++)
    {
        arr[i][colEnd] = num;
        num = num + 1;
    }
    colEnd = colEnd - 1;//(输入完一行,使得终止列数减一)

    //行(从右到左输入):从 colEnd 列到 colStart 列
    for (int i = colEnd; i >= colStart; i--)
    {
        arr[rowEnd][i] = num;
        num = num + 1;
    }
    rowEnd = rowEnd - 1;//(输入完一行,使得终止行数减一)

    //列(从下往上输入):从 rowEnd 行到 rowStart 列
    for (int i = rowEnd; i >= rowStart; i--)
    {
        arr[i][colStart] = num;
        num = num + 1;
    }
    colStart = colStart + 1;//(输入完一行,使得开始列数加一)
}

3.输出与释放:

接下来就是很常规的输出和内存的释放:

//输出
for(int i = 0 ; i < rows ; i++)
{
    for(int j = 0 ; j < cols ; j++)
    {
        cout << setw(3) << arr[i][j];//setw()用于控制输出格式
    }
    cout << endl;
}

//释放内存:
for(int i = 0 ; i < rows ; i++)
{
    delete [] arr[i];
}
delete [] arr;
arr = NULL;

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

关于链表,本次学习主要通过链表基础知识详解(非常详细简单易懂)-CSDN博客一文,下面是一些简单的感悟和代码书写的思路:

1.链表结构的构建:

(链表由一个个节点构成,每个节点一般采用结构体的形式组织)

链表节点分为两个域

      数据域:存放各种实际的数据

      指针域:存放下一节点的首地址

typedef struct Node
{
    //数据域
    int data;
    //指针域
    struct Node* next;
}node;

2.输入:

//node **p_head:使用指向指针的指针
//原因:需要修改指向链表头部的指针。C++ 中的函数参数传递是值传递,
//     即函数内对参数的修改不会影响到外部的实际参数。
//     因此,如果想要在函数内修改外部的指针(比如修改链表头指针),就需要传入指针的指针,
//     这样在函数内修改指针的指向就可以影响到外部的实际指针。

//node *p_new:插入的新节点
//原因:由于只需要修改新节点本身的数据域和指针域,并不需要修改该节点在外部的指向,
//     所以直接传入指针即可

void link_insert_data(node **p_head , node *p_new)
{
    //定义两个指针pb和pf,用来遍历链表。
    node *pb,*pf;
    //将pb和pf初始化为指向链表头部的指针
    pb = pf = *p_head;

    //链表为空链表:
    if(*p_head == nullptr)
    {
        //新节点p_new作为链表的头节点
        *p_head = p_new;
        //将新节点的next指针置为nullptr,表示新节点是链表中的最后一个节点
        p_new->next = nullptr;
        return;
    }

    //链表非空链表:
    //查找新节点应该插入的位置
    while((p_new->data >= pb->data)  && (pb->next !=nullptr) )
    {
        pf = pb;
        pb = pb->next;//继续向后遍历
    }

    //找到一个节点的num比新来的节点num大,插在pb的前面
    if(p_new->data < pb->data)
    {
        //找到的节点是头节点,插在最前面
        if(pb == *p_head)
        {
            p_new->next = *p_head;
            *p_head = p_new;
        }

        //找到的节点不是头节点,将新节点插入到找到的节点(pf所指)之前。
        else
        {
            pf->next = p_new;
            p_new->next = pb;
        }
    }

    //没有找到比新节点数据(p_new->data)大的节点,则新节点应该插在链表的最后
    else
    {
        pb->next = p_new;
        p_new->next = nullptr;
    }
}
//大概还可以写成:(是一些尝试过程中的产物)
//输入不太一样,但效果应该差不多
void insertInSortedCircularLinkedList(node *head, int value)
{
    node *p_new = new Node();
    p_new->data = value;
    p_new->next = nullptr;

    if (head == nullptr)
    {
        // 空链表情况
        head = p_new;
        p_new->next = p_new;  // 让新节点指向自身,构成循环
    }
    else if (value <= head->data)
    {
        // 新节点的值比头节点小或等于头节点的值,插入到头部
        p_new->next = head;
        node *last = head;
        while (last->next != head)
        {
            last = last->next;  // 找到最后一个节点
        }
        last->next = p_new;  // 更新尾节点的 next 指针
        head = p_new;  // 更新头节点
    }
    else
    {
        // 中间或尾部插入
        node *current = head;
        while (current->next != head && current->next->data < value)
        {
            current = current->next;
        }
        p_new->next = current->next;
        current->next = p_new;
    }
}
或者:
node* insertInSortedCircularLinkedList(node *head, int value)
{
    node* newNode = new Node();
    //使用 new 运算符在堆内存中动态分配了一个 Node 类型的对象,并将返回的地址赋给指针 newNode
    newNode->data = value;

    if (head == nullptr)
    {
        newNode->next = newNode;
        return newNode;
    }

    node* pb = head ;
    node* pf = nullptr;
    do
    {
        pf = pb;
        pb = pb->next;
    }
    while (pb != head && newNode->next >= pb->data);

    if (pb == head)
    {
        newNode->next = head;
        pf->next = newNode;
        return newNode;
    }

    pf->next = newNode;
    newNode->next = pb;

    return head;
}

 3.输出:

void link_print(node *head)
{
    if (head == nullptr)
    {
        cout << "链表为空" << endl;
        return;
    }

    node *p_mov;
    //定义新的指针保存链表的首地址,防止使用head改变原本链表
    p_mov = head;
    //当指针回到头结点时,表示已经遍历完整个循环链表

    //当指针保存最后一个结点的指针域为nullptr时,循环结束
    while(p_mov != nullptr)
    {
        //先打印当前指针保存结点的指针域
        cout << p_mov->data << " ";

        //指针后移,保存下一个结点的地址
        p_mov = p_mov->next;
    }
    cout << endl;
}
//也可以写成do-while 循环的形式,效果一样
do 
{
    //先打印当前指针保存结点的指针域
    cout << p_mov -> data << " ";
    //指针后移,保存下一个结点的地址
    p_mov = p_mov -> next;
} while(p_mov != head);

 4.释放:

//加入之后会使退出变得非常卡顿,在此留疑
void freeCircularLinkedList(node* head)
{
    if (head == nullptr)
    {
        return;
    }

    //定义一个指针变量保存头结点的地址
    node* current = head;
    node* temp;

    do{
        temp = current;
        current = current -> next;
        delete temp;
    } while (current != head);
}

5.目录:

void menu(node*& head)
{
    cout << "******  Data Structure  ******" << endl;
    cout << "1——有序插入单列表" << endl;
    cout << "2——查看单列表" << endl;
    cout << "     退出,请输入0" << endl;
    int sel;
    while(cin >> sel)
    {
        if(sel == 0)
        {
            //freeCircularLinkedList(head);
            cout << endl << "期待您的下次使用!" ;
            break;
        }
        switch(sel)
        {
        case 1:
        {
            node* p_new = new Node();//申请一个新节点
            cout << "请输入要插入的值:"; //给新节点赋值
            cin >> p_new->data;
            p_new->next = nullptr;

            link_insert_data(&head,p_new);
            break;
        }

        case 2:
        {
            link_print(head);
            break;
        }

        default:
        {
            cout << endl;
            cout << "您输入数字有误,请重新输入:";
            break;
        }

        }
    }
}

 6.主函数:

int main()
{
    node* head = nullptr;
    menu(head);
    return 0;
}

*一些学到的新知识:

1.delete和free的区别:

delete 和 free 是释放内存的两种方式,主要用于释放动态分配的内存,但在 C++ 和 C 语言中有一些区别:

delete:

delete 是 C++ 中的关键字,用于释放使用 new 运算符动态分配的单个对象或对象数组的内存。
delete 会调用对象的析构函数,然后释放内存空间。
使用 delete 释放指向对象的指针后,指针会自动设置为 nullptr。
不能用 delete 来释放使用 malloc() 或 calloc() 分配的内存。

free:

free 是 C 语言中的函数,用于释放动态分配的内存。
free 只是简单地释放内存,不会调用任何析构函数。
调用 free 后,指针并不会被自动置空,可能会导致野指针问题。
不能用 free 来释放使用 new 运算符分配的内存。
总的来说,如果在 C++ 中动态分配了内存,应该使用 delete 进行释放;在 C 语言中动态分配内存则应使用 free 进行释放。在 C++ 中,最好避免混合使用 new/delete 和 malloc/free,以避免因为不同的释放方式导致的问题。

2. nullptr和NULL的区别:

在C++中,nullptr 是 C++11 中引入的空指针常量,用于表示空指针。而 NULL 在较早的 C++ 标准中就存在,通常被定义为宏或者整数 0,也用于表示空指针。

区别:

  1. nullptr 是 C++11 引入的空指针常量,具有明确的空指针类型,可以隐式转换为任意指针类型,且不会与整数 0 混淆。
  2. NULL 在较早的 C++标准中就存在,通常被定义为宏或者整数 0,可能会与整数 0 混淆,不具有明确的空指针类型。

*最好使用nullptr 来表示空指针,因为它更加明确和安全,避免了与整数 0 的混淆。

 三、类设计题目

1.基类和继承类

在写此题目之前,需要提前了解什么是基类和继承类,本次学习通过Th3.11:基类与派生类关系之详细再讨论_派生类是基类的子集-CSDN博客一文了解,在此仅简述其运用方法:

class Dad {};
class Son : public Dad {};

//C++中给出了final关键字,可以防止误用了不想当基类的类作为基类

类型1
class Dad  final {//让不想做基类的Dad类声明为final的!
public:
/*...*/
};
class Son : public Dad {//错误!Dad不能用作基类
public:
/*...*/
};
类型2
class Dad {
public:
/*...*/
};
class Son final : public Dad {//让不想做基类的Son类声明为final的!
public:
/*...*/
};
class GrandSon:public Son{//错误!Son不能用作基类
public:
/*...*/
}

多余不再赘述

2. 基类的建设:

#ifndef ACCOUNT_H
#define ACCOUNT_H

using namespace std;

class Account
{
private:
    double balance;
public:
    Account()
    {

    }
    Account(double Balance)
    {
        if (Balance >= 0)
        {
            balance = Balance;
        }
        else
        {
            balance = 0;
            cout << "\t\t该初始化余额是一个无效的值,已将账户余额定义为0" << endl;
        }
    }

    ~Account()
    {

    }

    void credit(double amount)
    {
        balance = balance + amount;
    }

    void debit(double amount)
    {
        if (amount > balance)
        {
            cout << endl << "\t\tDebit amount exceeded account Balance." << endl;
        }
        else
        {
            balance = balance - amount;
        }
    }

    double getBalance() const
    {
        return balance;
    }
};
#endif // ACCOUNT_H

3.派生类的建设:

#ifndef SAVINGSACCOUNT_H
#define SAVINGSACCOUNT_H

#include "Account.h"
using namespace std;

class SavingsAccount : public Account
{
private:
    double Rate;
    int Year;

public:
    SavingsAccount()
    {

    }
    SavingsAccount(double Balance , double rate , int year) : 
                  Account(Balance) , Rate(rate) , Year(year){}

    ~SavingsAccount()
    {

    }

    double calculate()
    {
        double total = getBalance();
        for (int i = 1; i <= Year; i++)
        {
            double interest = total * Rate * 0.01;
            total = total + interest;
        }
        return total;
    }
};
#endif // SAVINGSACCOUNT_H

4.主函数的建设:

(利用C++中的控制台界面来实现可视化菜单操作(大概可以算是某种程度上的可视化))

#include <iostream>

#include "SavingsAccount.h"
using namespace std;

void menu(SavingsAccount &account)
{
    system("cls");
    cout << "\t\t************************************" << endl;
    cout << "\t\t当前余额为:" << account.getBalance() << endl;
    cout << "\t\t************************************" << endl;
    cout << "\t\t******  1.存款                ******" << endl;
    cout << "\t\t******  2.取款                ******" << endl;
    cout << "\t\t******  3.算存款之后的总余额  ******" << endl;
    cout << "\t\t******  0.退出                ******" << endl;
    cout << "\t\t************************************" << endl;
    cout << "\t\t请输入您的选择:";
}
int main()
{
    double Balance , amount;
    cout << "\t\t创建一个账户,并输入初始余额:";
    cin >> Balance;
    cout << endl;

    SavingsAccount account(Balance , 0 , 0);

    system("pause");//请按任意键继续

    menu(account);
    int sel;

    while(cin >> sel)
    {
        cout << endl;

        if(sel == 0)
        {
            cout << "\t\t期待您的下次使用!" ;
            exit(0);
            //exit(0)函数可以立即终止程序的执行,并返回给操作系统。
            //参数0表示正常退出,非零值表示异常退出
        }
        switch(sel)
        {
        case 1:
            cout << "\t\t请输入存款金额:";
            cin >> amount;
            account.credit(amount);
            cout << endl;
            system("pause");//请按任意键继续
            menu(account);
            break;

        case 2:
            cout << "\t\t请输入取款金额:";
            cin >> amount;
            account.debit(amount);
            cout << endl;
            system("pause");//请按任意键继续
            menu(account);
            break;

        case 3:
            double rate;
            int year;
            cout << "\t\t请输入存款年利率(单位:%):";
            cin >> rate;
            cout << "\t\t请输入存款年份:";
            cin >> year;
            cout << endl;
            account = SavingsAccount(account.getBalance() , rate , year);
            cout << "\t\t账户经过" << year << 
                   "年(年利率为:" << rate << ")后,账户的总余额为:" << 
                   account.calculate() << endl;
            cout << endl;
            system("pause");//请按任意键继续
            menu(account);
            break;

        default:
            cout << "\t\t您输入数字有误,请重新输入:";
            break;
        }
    }
    return 0;
}

*本来想尝试一下改背景颜色,但是不太好看就没改,在此附上修改方式,参考c++可视化操作(一)——控制台点击型菜单(windows.h)-CSDN博客一文

#include <Windows.h>

//背景白色

HANDLE handle = GetStdHandle(STD_OUTPUT_HANDLE);
    SetConsoleTextAttribute(handle, BACKGROUND_RED | BACKGROUND_GREEN | BACKGROUND_BLUE | BACKGROUND_INTENSITY);


// 将文本颜色设置为淡蓝色

HANDLE handle = GetStdHandle(STD_OUTPUT_HANDLE);
    SetConsoleTextAttribute(handle, FOREGROUND_BLUE | FOREGROUND_GREEN | FOREGROUND_INTENSITY);

四、结语

本次学习到此结束啦,虽然可能程序上还有很多的不足(欢迎各位指正),还是感谢各位可以看我写到这里。

  • 17
    点赞
  • 24
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值