**(1)基本思想。**把数组分成 A、B 两段:A 是下标 0~p−1 的前 p 项,B 是下标 p~n−1 的后 n−p 项。循环左移的目标正是把 AB 变成 BA,且保持 A、B 各段内部原来的次序。
使用三次原地反转:先反转 A,再反转 B,得到 ARBR;最后反转整个数组,段的先后次序互换、各段内部又反转一次,于是得到 BA。反转时只需用一个临时变量不断交换两端元素。
例如 [1,2,3,4,5,6,7] 左移 p=3:前段反转后为 [3,2,1,4,5,6,7];后段反转后为 [3,2,1,7,6,5,4];整体反转后为 [4,5,6,7,1,2,3],符合题意。
**(2)C 实现。**下标区间均为闭区间,循环中两个指针逐步向中间靠拢。
static void reverse(int r[], int left, int right) {
while (left < right) {
int t = r[left];
r[left++] = r[right];
r[right--] = t;
}
}
void rotate_left(int r[], int n, int p) {
reverse(r, 0, p - 1);
reverse(r, p, n - 1);
reverse(r, 0, n - 1);
}
**(3)正确性与复杂度。**前两次反转不改变两段中的元素集合,整体反转把 B 搬到前面、A 搬到后面,又将各段内部次序恢复,恰好得到 (xp,…,xn−1,x0,…,xp−1)。三次反转处理的区间长度之和为 p+(n−p)+n=2n,时间复杂度为 O(n);只有一个交换临时变量及下标,额外空间为 O(1)。在一般数组上必须移动线性数量的元素,这一复杂度已足够高效。p=1 或 p=n−1 时某一段只有一个元素,反转循环不执行,算法仍正确。