C#实现数组按素数/非素奇数/偶数分组时索引越界异常排查
问题描述
需编写算法实现如下逻辑:提示用户输入待读取正整数的个数n,读取n个正整数存入数组,要求数组元素排列顺序满足以下规则:
- 素数位于数组最前段
- 既非素数也非偶数的数位于中段
- 偶数位于数组最后段
- 每个分组内的元素需保持输入时的先后顺序
测试样例
若用户输入序列为14, 23, 17, 18, 13, 15, 12, 3, 42, 21,正确排列后的数组应为23 17 13 3 15 21 14 18 12 42。
提交的C#实现代码
using System; using System.Collections.Generic; using System.Linq; using System.Text.RegularExpressions; namespace HelloWorld { public class Program { public static void Main(string[] args) { int n, i, value, f, j, nbeven, nbprime, k,p,q, aux; int[] t; Console.WriteLine("give n:"); n=int.Parse(Console.ReadLine()); t=new int[n]; nbeven=0;nbprime=0; for(i=0;i<n;i++) { Console.WriteLine("give value:"); t[i]=int.Parse(Console.ReadLine()); if(t[i]%2==0) nbeven=nbeven+1; else { f=2; for(j=2;j<=t[i]/2;i++) { if(t[i]%j==0) f=f+1; } if(f==2) nbprime=nbprime+1; } } k=0;p=0;q=0; for(i=0;i<n;i++) { if(t[i]%2==0) { aux=t[i]; t[i]=t[n-nbeven+k]; t[n-nbeven+k]=aux; k=k+1; } else { f=2; for(j=2;j<=t[i]/2;j++) { if(t[i]%j==0) f=f+1; if(f==2) { aux=t[i]; t[i]=t[p]; t[p]=aux; p=p+1; } else { aux=t[i]; t[i]=t[nbprime+q]; t[nbprime+q]=aux; q=q+1; } } } } for(i=0;i<n;i++) Console.Write("{0} ",t[i]); } } }
运行报错
抛出未处理异常:index was outside the bounds of the array(索引超出数组边界)
错误原因分析
代码里有三处直接触发越界的逻辑问题:
- 素数计数阶段内层循环变量写错
第一轮遍历统计偶数、素数数量时,判断素数的内层循环写为for(j=2;j<=t[i]/2;i++),循环自增的变量错用了外层循环的i,而非内层应使用的j。这会导致外层遍历的i被异常累加,大量输入元素还没被读取判断就被跳过,最终统计得到的nbeven(偶数数量)、nbprime(素数数量)数值完全错误,后续计算数组分段位置的基准值从一开始就不对。 - 元素分类交换的触发时机完全错误
第二轮遍历做元素移动时,把分类交换的逻辑写在了素数判断的内层循环内部:判断一个数是不是素数需要让j从2循环到数值的1/2,代码里每执行一次j的循环,不等整个素数判断流程跑完,就会触发一次元素交换,同时给分类计数器p(素数段写入位置)、q(非素非偶段写入位置)累加1。以判断数值15为例,j要从2跑到7,这期间每跑一次j就会做一次交换、q加1,循环跑完q直接加了6次,很快就会超过中段数组的长度上限,访问到数组边界外的索引。 - 原地交换逻辑缺少边界校验,且会跳过未处理元素
代码采用单遍遍历原地交换的思路,但交换时会把还没遍历到的位置的元素换到当前i的位置,这些未分类的元素会直接被后续遍历跳过;同时三个分段的写入计数器k、p、q没有和对应分段的长度做边界校验,一旦计数超出分段范围就会触发越界。
注:实现这个需求最稳妥的方式是不要做复杂的原地交换,直接遍历三次原数组,按顺序分别把素数、非素非偶数、偶数追加到新数组里,既可以保证同组元素的输入顺序,也完全不会出现索引越界问题,逻辑可读性高很多。
内容的提问来源于stack exchange,提问作者Elie Makdissi
相关产品推荐
相关产品推荐

