我的代码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
3 2
6 5 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上三角形蛇形矩阵的构造方法:
构造矩阵大小:首先确定矩阵的大小为n×n(n是正整数)
填充矩阵:我们按照蛇形排列的规则填充矩阵,元素从1开始到n×n依次填充
上三角部分:由于是上三角矩阵,只保留主对角线及其上方的元素,其他位置填充为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
总结:
指针是 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++ 中也可以用来动态分配内存,使用 new
和 delete
操作符
动态内存分配: 使用 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;
总结