HackerRank数组左旋转问题:自定义实现遇阻求助
嘿,你的思路其实挺棒的——通过判断旋转次数来切换旋转方向,减少操作次数,这个优化点很聪明!咱们一步步来把这个实现补全,同时解决你遇到的几个问题:
一、先修正旋转次数d的计算逻辑
你原来处理d大于数组长度的方式有点小问题,更通用的做法是用取模运算来得到最小的有效旋转次数,因为旋转n次(n等于数组长度)相当于没旋转。比如数组长度为4,旋转5次等价于旋转1次,用d % aLength就能直接得到这个结果。另外还要处理d为0的情况(比如取模后结果为0),直接返回原数组即可。
修正后的这部分逻辑可以改成:
int aLength = a.length; // 先处理特殊情况:数组为空或无需旋转 if (aLength == 0 || d == 0) { return a; } // 计算最小有效旋转次数 d = d % aLength; // 如果旋转次数等于数组长度,直接返回原数组(其实上面的取模已经覆盖了这个情况,不过保留也没问题) if (d == aLength) { return a; }
二、实现左/右旋转的数组赋值逻辑
你卡在了元素赋值环节,核心是要正确计算每个元素在新数组中的位置,避免IndexOutOfBoundsException。咱们分两种情况实现:
情况1:执行左旋转(当d <= aLength/2时)
左旋转d次的本质是:把原数组前d个元素移到数组末尾。比如数组[1,2,3,4]左旋转1次后变成[2,3,4,1]。
赋值逻辑可以这样写:
int[] newArray = new int[aLength]; for (int i = 0; i < aLength; i++) { // 原数组索引i的元素,在新数组中的位置是 (i - d + aLength) % aLength // 加aLength是为了避免i-d出现负数,再取模保证索引合法 newArray[(i - d + aLength) % aLength] = a[i]; }
或者换一种更直观且高效的方式(用原生数组复制方法):
// 复制原数组d到末尾的元素到新数组开头 System.arraycopy(a, d, newArray, 0, aLength - d); // 复制原数组开头到d的元素到新数组末尾 System.arraycopy(a, 0, newArray, aLength - d, d);
情况2:执行右旋转(当d > aLength/2时)
此时我们可以把左旋转d次转换成右旋转k次,其中k = aLength - d(比如数组长度4,左旋转3次=右旋转1次)。右旋转k次的本质是把原数组最后k个元素移到数组开头。
赋值逻辑示例:
int k = aLength - d; int[] newArray = new int[aLength]; // 复制原数组最后k个元素到新数组开头 System.arraycopy(a, aLength - k, newArray, 0, k); // 复制原数组开头到aLength-k的元素到新数组后半部分 System.arraycopy(a, 0, newArray, k, aLength - k);
或者用循环的方式:
int k = aLength - d; int[] newArray = new int[aLength]; for (int i = 0; i < aLength; i++) { // 原数组索引i的元素,在新数组中的位置是 (i + k) % aLength newArray[(i + k) % aLength] = a[i]; }
三、关于IndexOutOfBoundsException和try-catch的疑问
你遇到的越界问题,本质是索引计算错误导致的(比如没有处理负数、没有取模)。通过上面的数学计算或者System.arraycopy的参数控制,完全可以避免这个异常,所以这里使用try-catch属于过度设计。
捕获异常不仅会增加不必要的代码复杂度,还可能掩盖你逻辑上的错误(比如索引计算错误)。更好的做法是通过严谨的逻辑保证索引始终在0到aLength-1的范围内。
完整的实现代码
把上面的逻辑整合起来,完整的方法如下:
static int[] rotLeft(int[] a, int d) { int aLength = a.length; // 处理特殊情况:无需旋转 if (aLength == 0 || d == 0) { return a; } // 计算最小有效旋转次数 d = d % aLength; if (d == 0) { return a; } int[] newArray = new int[aLength]; boolean rotateRight = d > aLength / 2; if (rotateRight) { // 转为右旋转k次 int k = aLength - d; System.arraycopy(a, aLength - k, newArray, 0, k); System.arraycopy(a, 0, newArray, k, aLength - k); } else { // 执行左旋转 System.arraycopy(a, d, newArray, 0, aLength - d); System.arraycopy(a, 0, newArray, aLength - d, d); } return newArray; }
总结
你的思路完全可行,通过切换旋转方向来减少操作次数是个很实用的优化点。只要把索引计算逻辑理清楚,就能顺利解决赋值和越界的问题,不需要依赖try-catch来处理可避免的异常。
内容的提问来源于stack exchange,提问作者Matt

