C# 零基础到精通教程 - 第五章:数组——批量管理同一类型的数据
5.1 为什么需要数组5.1.1 没有数组的困境csharp// 如果要存储5个学生的成绩没有数组的话 int score1 85; int score2 92; int score3 78; int score4 90; int score5 88; // 如果要计算平均分 double average (score1 score2 score3 score4 score5) / 5.0; // 如果要找出最高分 int max score1; if (score2 max) max score2; if (score3 max) max score3; if (score4 max) max score4; if (score5 max) max score5; // 问题 // 1. 如果有100个学生代码会非常冗长 // 2. 无法用循环来处理 // 3. 代码难以维护5.1.2 有数组的解决方案csharp// 用数组存储5个学生的成绩 int[] scores { 85, 92, 78, 90, 88 }; // 用循环计算平均分 int sum 0; for (int i 0; i scores.Length; i) { sum scores[i]; } double average (double)sum / scores.Length; // 用循环找出最高分 int max scores[0]; for (int i 1; i scores.Length; i) { if (scores[i] max) max scores[i]; }5.1.3 数组的本质数组 一组相同类型的数据的有序集合text内存示意图 scores 数组 ┌─────┬─────┬─────┬─────┬─────┐ │ 85 │ 92 │ 78 │ 90 │ 88 │ └─────┴─────┴─────┴─────┴─────┘ 索引: 0 1 2 3 4 特点 1. 所有元素类型相同都是 int 2. 在内存中连续存储 3. 通过索引下标访问元素 4. 索引从 0 开始到 Length-1 结束5.2 数组的声明和创建5.2.1 声明数组变量csharp// 语法类型[] 数组名; int[] scores; // 声明一个 int 类型的数组名字叫 scores string[] names; // 声明一个 string 类型的数组 double[] prices; // 声明一个 double 类型的数组 bool[] flags; // 声明一个 bool 类型的数组注意声明数组时[]放在类型后面不是变量名后面。csharpint[] arr1; // ✅ 正确 int arr2[]; // ❌ 错误这是 C/C 的写法5.2.2 创建数组分配内存声明只是告诉编译器我要一个数组但还没有实际分配内存。创建才是真正在内存中开辟空间。csharp// 语法数组名 new 类型[长度]; int[] scores; // 声明 scores new int[5]; // 创建可以存放 5 个 int 的数组 // 声明 创建 合并写法 int[] scores new int[5];5.2.3 创建时的默认值csharpint[] intArray new int[5]; // intArray 中的元素默认值都是 0 string[] stringArray new string[5]; // stringArray 中的元素默认值都是 null bool[] boolArray new bool[5]; // boolArray 中的元素默认值都是 false double[] doubleArray new double[5]; // doubleArray 中的元素默认值都是 0.0 // 输出验证 int[] nums new int[3]; Console.WriteLine(nums[0]); // 输出0 Console.WriteLine(nums[1]); // 输出0 Console.WriteLine(nums[2]); // 输出05.2.4 数组的初始化赋初值csharp// 方式1创建时逐个赋值 int[] scores new int[5]; scores[0] 85; scores[1] 92; scores[2] 78; scores[3] 90; scores[4] 88; // 方式2创建时直接初始化最常用 int[] scores new int[] { 85, 92, 78, 90, 88 }; // 方式3简化写法C# 的语法糖 int[] scores { 85, 92, 78, 90, 88 }; // 方式4指定长度并初始化 int[] scores new int[5] { 85, 92, 78, 90, 88 };5.3 访问数组元素5.3.1 通过索引访问数组元素通过索引下标访问索引从 0 开始csharpint[] scores { 85, 92, 78, 90, 88 }; // 读取元素 int firstScore scores[0]; // 85 int secondScore scores[1]; // 92 int thirdScore scores[2]; // 78 int lastScore scores[4]; // 88 // 修改元素 scores[0] 100; // 把第一个元素改成 100 scores[2] 85; // 把第三个元素改成 85 // 输出所有元素 Console.WriteLine(scores[0]); Console.WriteLine(scores[1]); Console.WriteLine(scores[2]); Console.WriteLine(scores[3]); Console.WriteLine(scores[4]);5.3.2 数组长度——Length 属性csharpint[] scores { 85, 92, 78, 90, 88 }; int length scores.Length; // 5 Console.WriteLine($数组长度{length}); // 最后一个元素的索引是 Length - 1 int lastValue scores[scores.Length - 1]; // 885.3.3 索引越界错误常见错误csharpint[] scores new int[5]; // 有效索引0, 1, 2, 3, 4 scores[5] 100; // ❌ 错误索引 5 超出范围 scores[-1] 100; // ❌ 错误索引不能为负数 // 运行时会抛出 IndexOutOfRangeException如何避免越界csharpint[] scores { 85, 92, 78, 90, 88 }; for (int i 0; i scores.Length; i) // 使用 Length 控制范围 { Console.WriteLine(scores[i]); }5.4 遍历数组5.4.1 使用 for 循环遍历csharpint[] scores { 85, 92, 78, 90, 88 }; // 正向遍历 for (int i 0; i scores.Length; i) { Console.WriteLine($scores[{i}] {scores[i]}); } // 反向遍历 for (int i scores.Length - 1; i 0; i--) { Console.WriteLine($scores[{i}] {scores[i]}); }5.4.2 使用 foreach 遍历更简洁csharpint[] scores { 85, 92, 78, 90, 88 }; // foreach 语法foreach (类型 变量名 in 数组名) foreach (int score in scores) { Console.WriteLine(score); } // 特点 // 1. 不需要索引 // 2. 不能修改数组元素只读 // 3. 代码更简洁for vs foreach 对比特点forforeach需要索引是否可以修改元素是否只能读取可以反向遍历是否可以跳步遍历是i2否代码简洁度较繁琐简洁性能略快略慢可忽略csharp// for 可以修改元素 for (int i 0; i scores.Length; i) { scores[i] 10; // 每个成绩加10分 ✅ } // foreach 不能修改 foreach (int score in scores) { score 10; // ❌ 编译错误不能修改 }5.4.3 数组遍历的常见操作csharpint[] scores { 85, 92, 78, 90, 88 }; // 1. 求和 int sum 0; for (int i 0; i scores.Length; i) { sum scores[i]; } Console.WriteLine($总分{sum}); // 2. 求平均值 double average (double)sum / scores.Length; Console.WriteLine($平均分{average:F2}); // 3. 找最大值 int max scores[0]; for (int i 1; i scores.Length; i) { if (scores[i] max) max scores[i]; } Console.WriteLine($最高分{max}); // 4. 找最小值 int min scores[0]; for (int i 1; i scores.Length; i) { if (scores[i] min) min scores[i]; } Console.WriteLine($最低分{min}); // 5. 统计满足条件的个数及格人数 int passCount 0; foreach (int score in scores) { if (score 60) passCount; } Console.WriteLine($及格人数{passCount});5.5 数组的更多操作5.5.1 数组的复制csharpint[] source { 1, 2, 3, 4, 5 }; // 方法1手动复制 int[] target1 new int[source.Length]; for (int i 0; i source.Length; i) { target1[i] source[i]; } // 方法2CopyTo 方法 int[] target2 new int[source.Length]; source.CopyTo(target2, 0); // 从索引0开始复制 // 方法3Array.Copy 方法 int[] target3 new int[source.Length]; Array.Copy(source, target3, source.Length); // 方法4Clone 方法 int[] target4 (int[])source.Clone(); // 方法5LINQ需要 using System.Linq int[] target5 source.ToArray();5.5.2 数组的排序csharpint[] numbers { 5, 2, 8, 1, 9, 3 }; // 升序排序 Array.Sort(numbers); // 结果{ 1, 2, 3, 5, 8, 9 } // 降序排序先升序再反转 Array.Sort(numbers); Array.Reverse(numbers); // 结果{ 9, 8, 5, 3, 2, 1 } // 字符串排序按字母顺序 string[] names { 张三, 李四, 王五, 赵六 }; Array.Sort(names); // 结果{ 张三, 李四, 王五, 赵六 }按拼音/Unicode顺序5.5.3 数组的查找csharpint[] numbers { 10, 20, 30, 40, 50 }; // IndexOf查找元素第一次出现的位置 int index Array.IndexOf(numbers, 30); // 2 int notFound Array.IndexOf(numbers, 99); // -1未找到 // LastIndexOf查找元素最后一次出现的位置 int[] numbers2 { 10, 20, 30, 20, 40 }; int lastIndex Array.LastIndexOf(numbers2, 20); // 3 // BinarySearch二分查找前提数组已排序 Array.Sort(numbers); int searchIndex Array.BinarySearch(numbers, 30); // 25.5.4 数组的清空和重置csharpint[] numbers { 1, 2, 3, 4, 5 }; // Clear将指定范围的元素设为默认值 Array.Clear(numbers, 0, numbers.Length); // numbers 变成 { 0, 0, 0, 0, 0 } // 只清除部分 int[] numbers2 { 1, 2, 3, 4, 5 }; Array.Clear(numbers2, 1, 3); // 结果{ 1, 0, 0, 0, 5 }5.5.5 数组的扩容数组一旦创建长度不能改变。如果需要更大的数组只能创建新数组并复制。csharpint[] scores { 85, 92, 78 }; Console.WriteLine($原数组长度{scores.Length}); // 3 // 需要添加第4个元素但数组长度固定 // 解决方法创建新数组 int[] newScores new int[scores.Length 1]; // 复制原数组元素 for (int i 0; i scores.Length; i) { newScores[i] scores[i]; } // 添加新元素 newScores[newScores.Length - 1] 90; // 重新指向新数组 scores newScores; Console.WriteLine($新数组长度{scores.Length}); // 4 Console.WriteLine($新数组{string.Join(, , scores)}); // 85, 92, 78, 90实际开发中如果频繁需要扩容建议使用 ListT第九章会讲。5.6 多维数组5.6.1 二维数组表格数据二维数组可以看作是一个表格有行和列csharp// 声明和创建 // 语法类型[,] 数组名 new 类型[行数, 列数]; // 创建一个 3 行 4 列的二维数组 int[,] matrix new int[3, 4]; // 访问元素 matrix[0, 0] 1; // 第1行第1列 matrix[0, 1] 2; // 第1行第2列 matrix[1, 0] 3; // 第2行第1列 matrix[2, 3] 4; // 第3行第4列 // 初始化方式1 int[,] matrix2 new int[,] { { 1, 2, 3, 4 }, { 5, 6, 7, 8 }, { 9, 10, 11, 12 } }; // 初始化方式2简化 int[,] matrix3 { { 1, 2, 3, 4 }, { 5, 6, 7, 8 }, { 9, 10, 11, 12 } };5.6.2 遍历二维数组csharpint[,] scores { { 85, 90, 88 }, // 学生0的成绩语文、数学、英语 { 92, 86, 94 }, // 学生1的成绩 { 78, 88, 80 }, // 学生2的成绩 { 95, 92, 91 } // 学生3的成绩 }; // 获取维度长度 int rows scores.GetLength(0); // 行数4 int cols scores.GetLength(1); // 列数3 // 使用嵌套 for 循环遍历 for (int i 0; i rows; i) { Console.Write($学生{i}的成绩); for (int j 0; j cols; j) { Console.Write(${scores[i, j]} ); } Console.WriteLine(); } // 使用 foreach 遍历会按行优先顺序输出所有元素 foreach (int score in scores) { Console.Write(${score} ); }5.6.3 二维数组的实际应用csharp// 应用1学生成绩统计 int[,] studentScores { { 85, 90, 88 }, // 语文、数学、英语 { 92, 86, 94 }, { 78, 88, 80 }, { 95, 92, 91 } }; string[] subjects { 语文, 数学, 英语 }; // 计算每个学生的总分和平均分 for (int i 0; i studentScores.GetLength(0); i) { int sum 0; for (int j 0; j studentScores.GetLength(1); j) { sum studentScores[i, j]; } double avg (double)sum / studentScores.GetLength(1); Console.WriteLine($学生{i}总分{sum}平均分{avg:F2}); } Console.WriteLine(); // 计算每门课的平均分 for (int j 0; j studentScores.GetLength(1); j) { int sum 0; for (int i 0; i studentScores.GetLength(0); i) { sum studentScores[i, j]; } double avg (double)sum / studentScores.GetLength(0); Console.WriteLine(${subjects[j]}平均分{avg:F2}); }csharp// 应用2矩阵乘法 int[,] A { { 1, 2 }, { 3, 4 } }; int[,] B { { 2, 0 }, { 1, 2 } }; int[,] C new int[2, 2]; // 矩阵乘法C A × B for (int i 0; i 2; i) { for (int j 0; j 2; j) { for (int k 0; k 2; k) { C[i, j] A[i, k] * B[k, j]; } } } // 输出结果 Console.WriteLine(矩阵乘法结果); for (int i 0; i 2; i) { for (int j 0; j 2; j) { Console.Write(${C[i, j]} ); } Console.WriteLine(); }5.6.4 多维数组三维及以上csharp// 三维数组立体数据 int[,,] cube new int[2, 3, 4]; // 2层3行4列 // 初始化 int[,,] cube2 { { // 第0层 { 1, 2, 3, 4 }, { 5, 6, 7, 8 }, { 9, 10, 11, 12 } }, { // 第1层 { 13, 14, 15, 16 }, { 17, 18, 19, 20 }, { 21, 22, 23, 24 } } }; // 访问 int value cube2[1, 2, 3]; // 第2层第3行第4列 245.7 锯齿数组数组的数组锯齿数组是数组的数组每个子数组的长度可以不同csharp// 锯齿数组 vs 二维数组 // 二维数组矩形每一行长度相同 int[,] rectangular new int[3, 4]; // 3行4列每行都是4列 // 锯齿数组每行可以有不同的长度 int[][] jagged new int[3][]; // 声明有3行的锯齿数组 // 为每一行创建不同长度的数组 jagged[0] new int[3]; // 第1行有3个元素 jagged[1] new int[5]; // 第2行有5个元素 jagged[2] new int[2]; // 第3行有2个元素 // 初始化锯齿数组 int[][] jagged2 new int[][] { new int[] { 1, 2, 3 }, new int[] { 4, 5 }, new int[] { 6, 7, 8, 9 } }; // 简化写法 int[][] jagged3 { new int[] { 1, 2, 3 }, new int[] { 4, 5 }, new int[] { 6, 7, 8, 9 } }; // 遍历锯齿数组 for (int i 0; i jagged3.Length; i) { Console.Write($第{i}行长度{jagged3[i].Length}); for (int j 0; j jagged3[i].Length; j) { Console.Write(${jagged3[i][j]} ); } Console.WriteLine(); }5.8 数组与方法的交互5.8.1 数组作为方法参数csharp// 计算数组的平均值 double CalculateAverage(int[] numbers) { if (numbers null || numbers.Length 0) return 0; int sum 0; foreach (int num in numbers) { sum num; } return (double)sum / numbers.Length; } // 使用 int[] scores { 85, 92, 78, 90, 88 }; double avg CalculateAverage(scores); Console.WriteLine($平均分{avg});5.8.2 方法返回数组csharp// 生成一个指定长度的随机数组 int[] GenerateRandomArray(int length, int minValue, int maxValue) { Random random new Random(); int[] array new int[length]; for (int i 0; i length; i) { array[i] random.Next(minValue, maxValue); } return array; } // 使用 int[] randomNumbers GenerateRandomArray(10, 1, 100); Console.WriteLine($随机数组{string.Join(, , randomNumbers)});5.8.3 数组参数传递的特性数组是引用类型传递的是引用地址所以方法内修改会影响原数组csharpvoid AddTenToEach(int[] numbers) { for (int i 0; i numbers.Length; i) { numbers[i] 10; // 修改会影响原数组 } } int[] scores { 85, 92, 78 }; AddTenToEach(scores); Console.WriteLine(string.Join(, , scores)); // 95, 102, 885.9 params 关键字可变参数params 允许方法接受可变数量的参数自动打包成数组csharp// 定义一个可以接受任意数量整数的方法 int Sum(params int[] numbers) { int sum 0; foreach (int num in numbers) { sum num; } return sum; } // 使用方式 int result1 Sum(1, 2, 3); // 6 int result2 Sum(10, 20); // 30 int result3 Sum(1, 2, 3, 4, 5); // 15 int result4 Sum(); // 0可以传0个参数 // 也可以直接传数组 int[] arr { 1, 2, 3 }; int result5 Sum(arr); // 6 Console.WriteLine($Sum(1,2,3) {result1}); Console.WriteLine($Sum(10,20) {result2}); Console.WriteLine($Sum(1,2,3,4,5) {result3});params 的限制一个方法只能有一个params参数params参数必须是方法的最后一个参数csharp// ✅ 正确params 在最后 void Print(string title, params int[] numbers) { Console.Write(title : ); foreach (int n in numbers) Console.Write(n ); Console.WriteLine(); } // 使用 Print(成绩, 85, 92, 78, 90, 88); // 输出成绩: 85 92 78 90 885.10 综合示例示例1学生成绩管理系统csharpusing System; class Program { static void Main() { Console.WriteLine( 学生成绩管理系统 ); Console.Write(请输入学生人数); int studentCount Convert.ToInt32(Console.ReadLine()); string[] names new string[studentCount]; int[] scores new int[studentCount]; // 输入学生信息 for (int i 0; i studentCount; i) { Console.WriteLine($\n第{i 1}个学生); Console.Write( 姓名); names[i] Console.ReadLine(); Console.Write( 成绩); scores[i] Convert.ToInt32(Console.ReadLine()); } // 统计 int sum 0, max scores[0], min scores[0]; int maxIndex 0, minIndex 0; int passCount 0, failCount 0; for (int i 0; i studentCount; i) { sum scores[i]; if (scores[i] max) { max scores[i]; maxIndex i; } if (scores[i] min) { min scores[i]; minIndex i; } if (scores[i] 60) passCount; else failCount; } double average (double)sum / studentCount; // 排序按成绩降序 Array.Sort(scores, names); Array.Reverse(scores); Array.Reverse(names); // 输出结果 Console.WriteLine(\n 统计结果 ); Console.WriteLine($总人数{studentCount}); Console.WriteLine($总分{sum}); Console.WriteLine($平均分{average:F2}); Console.WriteLine($最高分{max}{names[maxIndex]}); Console.WriteLine($最低分{min}{names[minIndex]}); Console.WriteLine($及格人数{passCount}及格率{(double)passCount / studentCount * 100:F1}%); Console.WriteLine($不及格人数{failCount}不及格率{(double)failCount / studentCount * 100:F1}%); // 成绩排名 Console.WriteLine(\n 成绩排名从高到低); for (int i 0; i studentCount; i) { Console.WriteLine(${i 1}. {names[i]}{scores[i]}分); } } }示例2冒泡排序理解排序原理csharpusing System; class Program { static void Main() { int[] numbers { 64, 34, 25, 12, 22, 11, 90 }; Console.WriteLine(原始数组 string.Join(, , numbers)); BubbleSort(numbers); Console.WriteLine(排序后 string.Join(, , numbers)); } static void BubbleSort(int[] arr) { int n arr.Length; for (int i 0; i n - 1; i) { // 每轮把最大的元素冒泡到最后 for (int j 0; j n - i - 1; j) { if (arr[j] arr[j 1]) { // 交换两个元素 int temp arr[j]; arr[j] arr[j 1]; arr[j 1] temp; } } // 打印每一轮的结果 Console.WriteLine($第{i 1}轮{string.Join(, , arr)}); } } } // 输出 // 原始数组64, 34, 25, 12, 22, 11, 90 // 第1轮34, 25, 12, 22, 11, 64, 90 // 第2轮25, 12, 22, 11, 34, 64, 90 // 第3轮12, 22, 11, 25, 34, 64, 90 // 第4轮12, 11, 22, 25, 34, 64, 90 // 第5轮11, 12, 22, 25, 34, 64, 90 // 第6轮11, 12, 22, 25, 34, 64, 90 // 排序后11, 12, 22, 25, 34, 64, 90示例3约瑟夫环问题经典数组应用csharpusing System; class Program { static void Main() { // 约瑟夫环n个人围成一圈每数到m的人出局求最后剩下的人 Console.Write(请输入总人数); int n Convert.ToInt32(Console.ReadLine()); Console.Write(请输入步长每数到几出局); int m Convert.ToInt32(Console.ReadLine()); // 创建数组true表示还在圈中 bool[] people new bool[n]; for (int i 0; i n; i) { people[i] true; } int remainCount n; // 剩余人数 int currentIndex -1; // 当前位置 int count 0; // 计数器 Console.Write(\n出局顺序); while (remainCount 1) { count 0; // 数m个人 while (count m) { currentIndex (currentIndex 1) % n; if (people[currentIndex]) { count; } } // 当前数到的人出局 people[currentIndex] false; Console.Write(${(currentIndex 1)} ); remainCount--; } // 找出最后剩下的人 for (int i 0; i n; i) { if (people[i]) { Console.WriteLine($\n最后剩下的人编号{i 1}); break; } } } }5.11 常见错误与陷阱错误1数组索引越界csharpint[] arr new int[5]; arr[5] 10; // ❌ 索引 5 超出范围有效是 0-4 arr[-1] 10; // ❌ 索引不能为负数错误2数组未初始化就使用csharpint[] arr; Console.WriteLine(arr[0]); // ❌ 编译错误使用了未赋值的局部变量错误3混淆 Length 和 Length()csharpint[] arr { 1, 2, 3 }; int len arr.Length; // ✅ 属性不是方法 int len2 arr.Length(); // ❌ 错误不能当方法用错误4foreach 中修改元素csharpint[] scores { 85, 92, 78 }; foreach (int score in scores) { score 10; // ❌ 编译错误不能修改 foreach 变量 } // ✅ 改用 for for (int i 0; i scores.Length; i) { scores[i] 10; }错误5数组比较的错误方式csharpint[] arr1 { 1, 2, 3 }; int[] arr2 { 1, 2, 3 }; if (arr1 arr2) // 比较的是引用不是内容 { Console.WriteLine(相等); // 不会输出 } // 正确方式使用 SequenceEqual if (arr1.SequenceEqual(arr2)) { Console.WriteLine(相等); // 输出 }错误6多维数组索引语法错误csharpint[,] matrix new int[3, 4]; matrix[1, 2] 10; // ✅ 正确 matrix[1][2] 10; // ❌ 错误这是锯齿数组的语法5.12 本章总结核心知识点导图text数组 ├── 一维数组 │ ├── 声明int[] arr; │ ├── 创建new int[5]; │ ├── 初始化{1,2,3,4,5} │ ├── 访问arr[0] │ ├── 遍历for / foreach │ └── 属性Length │ ├── 二维数组 │ ├── 声明int[,] arr; │ ├── 创建new int[3,4]; │ ├── 访问arr[0,1] │ └── 维度GetLength(0), GetLength(1) │ ├── 锯齿数组 │ ├── 声明int[][] arr; │ ├── 创建new int[3][]; │ └── 特点子数组长度可不同 │ └── 常用操作 ├── Sort排序 ├── IndexOf查找 ├── Copy复制 ├── Clear清空 └── Reverse反转数组 vs 普通变量特点普通变量数组存储数据量1个多个命名方式每个变量独立命名一个名字 索引访问方式变量名变量名[索引]循环处理不能可以内存位置可能不连续连续存储5.13 练习题基础题声明一个包含10个整数的数组用循环给每个元素赋值为索引的平方0,1,4,9...然后输出所有元素。从键盘输入5个整数存入数组输出数组的最大值、最小值、总和、平均值。创建一个包含10个随机数1-100的数组输出所有大于50的数。写一个方法接收一个整数数组返回数组中所有元素的乘积。应用题数组去重给定一个数组{1, 2, 3, 2, 4, 3, 5, 1, 6}输出去
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2629767.html
如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!