【华为od刷题(C++)】HJ35 蛇形矩阵(指针)

我的代码1:

#include 
using namespace std;

int main() {
    int row;//row:定义了矩阵的行数(和列数,实际上是一个正方形矩阵)

    while (cin >> row) {
        //这个循环会持续执行,直到输入流被结束
        //每次读取一个整数并赋值给row,程序就开始执行填充操作

        int** a = new int* [row];
        //动态地为一个二维数组(a)的行分配内存

        /*这里 a 是一个指向指针的指针 (int**),用于存储二维数组的行

        new int*[row] 动态分配了一个包含 row 个指针的数组,每个指针会指向一行数组

        a 是一个指针数组,每个元素是一个 int* 类型的指针
        该指针数组中每个元素将指向一个实际的整型数组(每行的内容)*/

        /*假设 row = 3,这行代码执行后,内存中会有如下结构:
        a 是一个 指向 3 个指针 的指针
        a[0]、a[1]、a[2] 每个指针暂时指向一个未分配的内存区域,
        后来每个指针会指向实际的整型数组(即矩阵的每一行)

        图示:
        a --> [a[0], a[1], a[2]]
        a[0] --> 未分配的内存(待分配为一个整型数组)
        a[1] --> 未分配的内存(待分配为一个整型数组)
        a[2] --> 未分配的内存(待分配为一个整型数组)*/

        for (int i = 0; i < row; ++i) {
            //这个循环从 i = 0 到 i = row - 1 遍历每一行

            a[i] = new int[row];
        }
        //这段代码通过一个 for 循环,为二维数组的每一行分配内存

        /*为 a[i] 指向的内存分配一个 row 个元素大小的 int 数组,
        (即每一行有 row 个整数)
        这确保了矩阵的每一行都拥有 row 个整数空间

        假设 row = 3,经过这个步骤后,内存中的结构会变成:
        a --> [a[0], a[1], a[2]]    // a是一个包含3个指针的数组
        a[0] --> [int, int, int]    // a[0]指向大小为3的int数组
        a[1] --> [int, int, int]    // a[1]指向大小为3的int数组
        a[2] --> [int, int, int]    // a[2]指向大小为3的int数组*/

        int start = 1;//start是一个计数器,初始化为1

        for (int i = 0; i < row; ++i) {
        //外层循环控制行数,从第0行开始,直到第row-1行

            int k = i;
            for (int j = 0; j <= i; ++j) {
            //内层循环控制列数,从第0列开始,填充每行的部分内容

                a[k][j] = start;
                //填充矩阵中的元素
                //k是行索引,j是列索引,start是当前要填入的数字

                start++;
                //每次填充后,start自增,保证数字递增

                k--;//k每次减少,意味着矩阵的元素是按照斜对角线的方式填充的
            }
        }
        /*填充过程示例:
        假设 row = 3,矩阵的填充过程如下:

        第一行 (i = 0):
        j = 0,k = 0,填充 a[0][0] = 1,然后 start = 2,k-- 变为 k = -1
        此时矩阵:
        1 0 0
        0 0 0
        0 0 0

        第二行 (i = 1):
        j = 0,k = 1,填充 a[1][0] = 2,然后 start = 3,k-- 变为 k = 0
        j = 1,k = 0,填充 a[0][1] = 3,然后 start = 4,k-- 变为 k = -1
        此时矩阵:
        1 3 0
        2 0 0
        0 0 0

        第三行 (i = 2):
        j = 0,k = 2,填充 a[2][0] = 4,然后 start = 5,k-- 变为 k = 1
        j = 1,k = 1,填充 a[1][1] = 5,然后 start = 6,k-- 变为 k = 0
        j = 2,k = 0,填充 a[0][2] = 6,然后 start = 7,k-- 变为 k = -1
        此时矩阵:
        1 3 6
        2 5 0
        4 0 0

        最终矩阵输出时将会是:
        1 3 6 
        2 5 
        4 */

        //这个部分输出整个矩阵
        for (int i = 0; i < row; ++i) {
            for (int j = 0; j < row; ++j) {
            //外层循环遍历每一行,内层循环遍历每一列

                if (a[i][j] != 0) {
                    //只输出非零的元素(因为没有填充的部分默认值为0)

                    cout << a[i][j] << " ";
                    //输出矩阵中的每个元素,之后输出一个空格
                }
            }
            cout << endl;//每行输出完后换行
        }
    }
    return 0;
}

这段代码的目标是通过动态内存分配生成一个正方形矩阵,并按照斜对角线的方式填充数字,然后输出矩阵

生成的输出序列:

  1. 第一条斜对角线:1
  2. 第二条斜对角线:3 2
  3. 第三条斜对角线:6 5 4
  4. 第四条斜对角线:10 9 8 7

我的代码2:

#include //引入了 C++ 的标准输入输出库,用来进行数据的输入和输出
using namespace std;
/*表示使用 std 命名空间
这样可以直接使用 cin, cout, endl 等标准库中的对象和函数
/而不需要每次写 std:: 前缀*/

int main() {
    int row;//定义了一个整数变量 row,用于存储用户输入的行数

    while (cin >> row) {
        /*这是一个循环,
        cin >> row 会读取用户输入的一个整数,
        并将其存储在 row 变量中
        只要用户输入有效的整数,就会继续执行循环体*/

        for (int i = 1; i <= row; ++i) {
            //外层 for 循环控制行数,从 1 到 row,即一共输出 row 行

            for (int j = 1; j < row - (i - 1); ++j) {
                //内层 for 循环控制每一列的输出内容
                //列数为 row - (i - 1),这意味着随着行数 i 的增加,列数会逐渐减少

                cout << ((j + i - 1) * (j + i - 1) + j + i - 1) / 2 - (i - 1) << " ";
                /*这一行是内层循环中的输出语句
                每次输出的内容由一个复杂的数学表达式决定*/
            }
            cout << (row * row + row) / 2 - (i - 1) << endl;
            /*这一行输出的是当前行的最后一个数字,
            它的计算公式为 (row * row + row) / 2 - (i - 1)

            (row * row + row) / 2 是一个常数值(与 row 有关),
            表示一个固定的数值
            减去 i - 1 用来调整每行的最后一个数字*/
        }
    }
    return 0;
}
  • 该程序通过嵌套循环,逐行输出矩阵,每一行的数字通过一个数学表达式进行计算,数字呈现出一定的规律性
  • 外层循环控制行数,内层循环控制列数,每一列的输出依赖于行数和列数,通过复杂的数学公式来计算
  • 每行最后一个数字的输出通过固定的数学计算公式得到

上三角形蛇形矩阵是一种矩阵,其具有以下特点:

上三角形矩阵:指的是矩阵中,所有在主对角线以下的元素都为零,只有主对角线及其上方的元素有值;举例来说,3×3的上三角矩阵是:

a11

a12 a13
0 a22 a23
0 0 a33

蛇形排列:指的是矩阵中的元素以蛇形方式填充。在蛇形排列中,元素从左到右、从上到下依次排列,奇数行从左到右,偶数行则是从右到左。举个例子,4×4的蛇形矩阵如下所示:

1 2 3 4
8 7 6 5
9 10 11 12
16 15 14 13

n×n上三角形蛇形矩阵的构造方法:

  1. 构造矩阵大小:首先确定矩阵的大小为n×n(n是正整数)

  2. 填充矩阵:我们按照蛇形排列的规则填充矩阵,元素从1开始到n×n依次填充

  3. 上三角部分:由于是上三角矩阵,只保留主对角线及其上方的元素,其他位置填充为0

例子:

假设n=4时,我们可以构造如下的上三角形蛇形矩阵:

填充蛇形矩阵:按照蛇形规则填充4×4矩阵,得到:

1 2 3 4
8 7 6 5
9 10 11 12
16 15 14 13

转化为上三角形:在上三角形矩阵中,删除主对角线下方的元素,将其设为0;得到:

1 2 3 4
0 7 6 5
0 0 11 12
0 0 0 13

这样,我们得到了一个n×n的上三角形蛇形矩阵

总结:

  • 上三角形矩阵要求矩阵的下三角部分为零
  • 蛇形排列的特点是奇数行从左到右填充,偶数行从右到左填充
  • 上三角形蛇形矩阵将这两个概念结合,形成一个特定的矩阵

在矩阵中,主对角线斜对角线是两个常见的概念,它们分别表示矩阵中特定的元素排列方式。

1. 主对角线(Principal Diagonal)

主对角线是指从矩阵的左上角到右下角的一条对角线。在一个 n×n 的方阵中,主对角线的元素是所有行和列索引相等的元素。即:

Ai,j (where i=j)

主对角线上的元素的下标相同;对于一个 3×3 的矩阵:

A11 A12 A13
A21 A22 A23
A31 A32 A33

主对角线的元素是 A11,A22,A33

2. 斜对角线(Anti-Diagonal)

斜对角线是指从矩阵的右上角到左下角的一条对角线。斜对角线上的元素的下标满足 i+j=n+1,其中 n 是矩阵的大小,i 是行号,j 是列号;例如,对于一个 3×3 的矩阵:

A11 A12 A13
A21 A22 A23
A31 A32 A33

斜对角线的元素是 A13,A22,A31

例子:

假设我们有一个 4×4 的矩阵:

A11 A12 A13 A14
A21 A22 A23 A24
A31 A32 A33 A34
A41 A42 A43 A44
  • 主对角线上的元素是:
    A11,A22,A33,A44

  • 斜对角线上的元素是:
    A14,A23,A32,A41

总结:

  • 主对角线是从左上到右下,元素下标相同的元素
  • 斜对角线是从右上到左下,元素下标满足 i+j=n+1 的元素

指针是 C++ 中一种非常重要的概念,它是一个变量,专门用来存储内存地址;简单来说,指针指向某个变量的内存位置,而不是存储实际的值

基本概念

指针的声明和定义: 在 C++ 中,指针的声明格式如下:

类型 *指针名;

例如:

int *ptr;  // ptr 是一个指向 int 类型的指针

指针的初始化

  • 指针可以通过地址符号 & 来获取变量的地址
int x = 10;
int *ptr = &x;  // ptr 存储的是 x 的地址

通过指针访问变量的值(解引用)

  • 解引用操作符 * 用来访问指针指向的地址的值
cout << *ptr;  // 输出 ptr 指向的地址的值,即 10

*ptr 表示“ptr 所指向的内存地址中的内容”,这是指针的解引用

指针的基本操作

获取地址(取地址符 &): 使用 & 可以获取变量的内存地址

int a = 20;
int *p = &a;  // p 存储变量 a 的地址
cout << "a 的地址是: " << &a << endl;
cout << "p 存储的地址是: " << p << endl;

解引用(解引用符 *): 使用 * 操作符可以通过指针访问变量的值

int a = 20;
int *p = &a;  // p 存储 a 的地址
cout << "通过 p 访问 a 的值: " << *p << endl;  // 输出 20

指针与数组

  • 数组名本质上是一个指向数组第一个元素的指针
int arr[] = {1, 2, 3};
int *p = arr;  // p 指向 arr[0]
cout << *p << endl;  // 输出 1,解引用得到数组的第一个元素
p++;  // 指针 p 向后移动,指向 arr[1]
cout << *p << endl;  // 输出 2

动态内存分配

指针在 C++ 中也可以用来动态分配内存,使用 newdelete 操作符

动态内存分配: 使用 new 来动态分配内存

int *ptr = new int;  // 动态分配一个整数的内存
*ptr = 10;           // 给动态分配的内存赋值
cout << *ptr << endl;  // 输出 10
delete ptr;          // 释放动态分配的内存

动态数组: 可以使用 new[] 动态分配数组

int *arr = new int[5];  // 动态分配一个包含 5 个整数的数组
for (int i = 0; i < 5; ++i) {
    arr[i] = i + 1;  // 给数组赋值
}
for (int i = 0; i < 5; ++i) {
    cout << arr[i] << " ";  // 输出数组内容
}
delete[] arr;  // 释放动态分配的数组

常见指针类型

空指针: 空指针 nullptr 用于初始化指针,表示它不指向任何有效的内存地址

int *ptr = nullptr;  // ptr 不指向任何有效内存

指向指针的指针: 一个指针也可以指向另一个指针,这样就形成了指向指针的指针(也叫做二级指针)

int x = 10;
int *p = &x;
int **pp = &p;  // pp 是一个指向指针 p 的指针
cout << **pp << endl;  // 输出 10

常量指针与指针常量

常量指针:指针本身可以修改,但指针指向的值不能修改

int x = 10;
const int *ptr = &x;
// *ptr = 20;  // 错误,不能修改 ptr 指向的值
ptr = nullptr;  // 可以修改 ptr 本身

指针常量:指针本身不能修改,但可以通过指针修改它所指向的值

int x = 10;
int *const ptr = &x;
*ptr = 20;  // 可以修改 ptr 指向的值
// ptr = nullptr;  // 错误,不能修改 ptr 本身

指针的常见应用

函数传参:通过指针传递参数,可以在函数内修改外部变量

void addOne(int *num) {
    (*num)++;  // 通过指针修改 num 指向的值
}

int main() {
    int a = 5;
    addOne(&a);  // 传递 a 的地址
    cout << a << endl;  // 输出 6
    return 0;
}

链表:链表是使用指针实现的一种数据结构,每个节点通过指针链接到下一个节点

struct Node {
    int data;
    Node* next;
};

Node* head = nullptr;

总结

  • 指针 是 C++ 中一种非常有用的工具,能够操作内存地址,访问和修改变量的值
  • 指针的应用包括动态内存管理、函数传参、数据结构(如链表)等
  • 在使用指针时,需要特别注意内存管理,避免内存泄漏和空指针错误

你可能感兴趣的:(华为od,c++,链表)