题目:一个字符数组含有N个字符,要求设计一个算法可以返回字符左移K位后的字符数组,要求时间复杂度为O(N),空间复杂度只允许使用两个附加变量。比如字符串abcdefg左移3位得到defgabc。
解法一:最先想到的肯定是暴力模拟。也就是说每次移动头字符到字符串末尾,移动K次。比如abcdefg -> bcdefga -> cdefgab -> defgabc。
在正式写代码之前,我们需要对K的范围以及正负性进行讨论,比如对于N为7的情况,K可能为-16,也可能为+16,因为字符串左移N位还是原来的字符串。所以我们可以得到如下结论,如果K为负数,则我们只需要左移K%N+N位,当N、K分别为7、-16时,只需左移5位。如果K为非负数,则我们只需要左移K%N位。
解法1的代码如下:
void left_move(char *arr,int K,int N) {if(K<0)
K=K%N+N;
else
K=K%N;
while(K--) {
char head=arr[0];
for(int i=1; i<N; i++) {
arr[i-1]=arr[i];
}
arr[N-1]=head;
}
}
但是解法1的时间复杂度为O(N^2),使用了两个附加变量,分别为i和head。
解法二:这种解法比较巧妙,跟矩阵的转置非常相像。将字符串左移K位,则可以把字符串左侧K位记为X,右侧剩余位记为Y。我们定义一种操作T可以将字符串逆序,也就是说把abcd变为dcba。由定义可知(XT)T=X。我们题目要求将XY转变为YX,而需要注意的一点是,X和Y中的字符顺序并没有发生改变!因此我们仔细思考一下,是否可以利用操作T来达到字符串左移的目的呢?答案是可以的。(XTYT)T=(YT)T(XT)T=YX。因此我们可以首先逆序X,然后逆序Y,最后再将XTYT逆序,即可得到YX。代码如下:
void reverse(char *arr, int b, int e) {
for(; b<e; b++,e--) { //注意这种写法,要将abcde变为edcba,只需要从两头不断逼近就行,逼近之前进行交换。
char t = arr[b];
arr[b] = arr[e];
arr[e] = t;
}
}
void left_move(char *arr, int K, int N) {
if(K<0)
K = K%N+N;
else
K = K%N;
reverse(arr, 0, K-1);
reverse(arr, K, N-1);
reverse(arr, 0, N-1);
}
上面算法的时间复杂度为O(N),计算方式为K/2+N-K/2+N/2=N,其中K/2为逆序X的时间复杂度,N-K/2为逆序Y的时间复杂度。附加变量为b和e。